blob: e1da66c877e9287837a0e755eb81f64fc2078cf0 [file] [log] [blame]
/*
* Copyright © 2007, 2011, 2013 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Authors:
* Eric Anholt <eric@anholt.net>
* Daniel Vetter <daniel.vetter@ffwll.ch>
*
*/
#ifdef HAVE_LIBGEN_H
#include <libgen.h>
#endif
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <string.h>
#include <sys/mman.h>
#include <signal.h>
#include <pciaccess.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <sys/utsname.h>
#include <termios.h>
#include <pthread.h>
#include "drmtest.h"
#include "i915_drm.h"
#include "i915/gem.h"
#include "xe/xe_query.h"
#include "intel_chipset.h"
#include "intel_io.h"
#include "igt_debugfs.h"
#include "igt_device.h"
#include "igt_gt.h"
#include "igt_kmod.h"
#include "igt_params.h"
#include "igt_sysfs.h"
#include "igt_device_scan.h"
#include "version.h"
#include "config.h"
#include "intel_reg.h"
#include "ioctl_wrappers.h"
#include "igt_dummyload.h"
#include "xe/xe_query.h"
/**
* SECTION:drmtest
* @short_description: Base library for drm tests and tools
* @title: drmtest
* @include: igt.h
*
* This library contains the basic support for writing tests, with the most
* important part being the helper function to open drm device nodes.
*
* But there's also a bit of other assorted stuff here.
*
* Note that this library's header pulls in the [i-g-t core](igt-gpu-tools-i-g-t-core.html)
* and [batchbuffer](igt-gpu-tools-intel-batchbuffer.html) libraries as dependencies.
*/
static int __get_drm_device_name(int fd, char *name, int name_size)
{
drm_version_t version;
memset(&version, 0, sizeof(version));
version.name_len = name_size;
version.name = name;
if (!drmIoctl(fd, DRM_IOCTL_VERSION, &version)){
return 0;
}
return -1;
}
static bool __is_device(int fd, const char *expect)
{
char name[12] = "";
if (__get_drm_device_name(fd, name, sizeof(name) - 1))
return false;
return strcmp(expect, name) == 0;
}
bool is_amdgpu_device(int fd)
{
return __is_device(fd, "amdgpu");
}
bool is_i915_device(int fd)
{
return __is_device(fd, "i915");
}
bool is_mtk_device(int fd)
{
return __is_device(fd, "mediatek");
}
bool is_msm_device(int fd)
{
return __is_device(fd, "msm");
}
bool is_nouveau_device(int fd)
{
/* Currently all nouveau-specific codepaths require libdrm */
#ifdef HAVE_LIBDRM_NOUVEAU
return __is_device(fd, "nouveau");
#else
return false;
#endif
}
bool is_vc4_device(int fd)
{
return __is_device(fd, "vc4");
}
bool is_xe_device(int fd)
{
return __is_device(fd, "xe");
}
bool is_intel_device(int fd)
{
return is_i915_device(fd) || is_xe_device(fd);
}
enum intel_driver get_intel_driver(int fd)
{
if (is_xe_device(fd))
return INTEL_DRIVER_XE;
else if (is_i915_device(fd))
return INTEL_DRIVER_I915;
igt_assert_f(0, "Device is not handled by Intel driver\n");
}
static char _forced_driver[16] = "";
/**
* __set_forced_driver:
* @name: name of driver to forcibly use
*
* Set the name of a driver to use when calling #drm_open_driver with
* the #DRIVER_ANY flag.
*/
void __set_forced_driver(const char *name)
{
if (!name) {
igt_warn("No driver specified, keep default behaviour\n");
return;
}
strncpy(_forced_driver, name, sizeof(_forced_driver) - 1);
}
static const char *forced_driver(void)
{
if (_forced_driver[0])
return _forced_driver;
return NULL;
}
static int modprobe(const char *driver)
{
return igt_kmod_load(driver, "");
}
static void modprobe_i915(const char *name)
{
/* When loading i915, we also want to load snd-hda et al */
igt_i915_driver_load(NULL);
}
static const struct module {
unsigned int bit;
const char *module;
void (*modprobe)(const char *name);
} modules[] = {
{ DRIVER_AMDGPU, "amdgpu" },
{ DRIVER_INTEL, "i915", modprobe_i915 },
{ DRIVER_MSM, "msm" },
{ DRIVER_PANFROST, "panfrost" },
{ DRIVER_V3D, "v3d" },
{ DRIVER_VC4, "vc4" },
{ DRIVER_VGEM, "vgem" },
{ DRIVER_VMWGFX, "vmwgfx" },
{ DRIVER_XE, "xe" },
{}
};
struct _opened_device_path {
char *path;
struct igt_list_head link;
};
/*
* Logs path of opened device. Device path opened for the first time is logged at info level,
* subsequent opens (if any) are logged at debug level.
*/
static void log_opened_device_path(const char *device_path)
{
static IGT_LIST_HEAD(opened_paths);
struct _opened_device_path *item;
igt_list_for_each_entry(item, &opened_paths, link) {
if (!strcmp(item->path, device_path)) {
igt_debug("Opened previously opened device: %s\n", device_path);
return;
}
}
item = calloc(1, sizeof(struct _opened_device_path));
igt_assert(item);
item->path = strdup(device_path);
igt_assert(item->path);
igt_list_add(&item->link, &opened_paths);
igt_info("Opened device: %s\n", item->path);
}
static int open_device(const char *name, unsigned int chipset)
{
const char *forced;
char dev_name[16] = "";
int chip = DRIVER_ANY;
int fd;
fd = open(name, O_RDWR);
if (fd == -1)
return -1;
if (__get_drm_device_name(fd, dev_name, sizeof(dev_name) - 1) == -1)
goto err;
forced = forced_driver();
if (forced && chipset == DRIVER_ANY && strcmp(forced, dev_name))
goto err;
for (int start = 0, end = ARRAY_SIZE(modules) - 1; start < end; ){
int mid = start + (end - start) / 2;
int ret = strcmp(modules[mid].module, dev_name);
if (ret < 0) {
start = mid + 1;
} else if (ret > 0) {
end = mid;
} else {
chip = modules[mid].bit;
break;
}
}
if ((chipset & chip) == chip) {
log_opened_device_path(name);
return fd;
}
err:
close(fd);
return -1;
}
static struct {
int fd;
struct stat stat;
}_opened_fds[64];
static int _opened_fds_count;
static void _set_opened_fd(int idx, int fd)
{
assert(idx < ARRAY_SIZE(_opened_fds));
assert(idx <= _opened_fds_count);
_opened_fds[idx].fd = fd;
assert(fstat(fd, &_opened_fds[idx].stat) == 0);
_opened_fds_count = idx+1;
}
static bool _is_already_opened(const char *path, int as_idx)
{
struct stat new;
assert(as_idx < ARRAY_SIZE(_opened_fds));
assert(as_idx <= _opened_fds_count);
/*
* we cannot even stat the device, so it's of no use - let's claim it's
* already opened
*/
if (igt_debug_on(stat(path, &new) != 0))
return true;
for (int i = 0; i < as_idx; ++i) {
/* did we cross filesystem boundary? */
assert(_opened_fds[i].stat.st_dev == new.st_dev);
if (_opened_fds[i].stat.st_ino == new.st_ino)
return true;
}
return false;
}
static int __search_and_open(const char *base, int offset, unsigned int chipset, int as_idx)
{
const char *forced;
forced = forced_driver();
if (forced)
igt_debug("Force option used: Using driver %s\n", forced);
for (int i = 0; i < 16; i++) {
char name[80];
int fd;
sprintf(name, "%s%u", base, i + offset);
if (_is_already_opened(name, as_idx))
continue;
fd = open_device(name, chipset);
if (fd != -1)
return fd;
}
return -1;
}
void drm_load_module(unsigned int chipset)
{
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&mutex);
for (const struct module *m = modules; m->module; m++) {
if (chipset & m->bit) {
if (m->modprobe)
m->modprobe(m->module);
else
modprobe(m->module);
}
}
pthread_mutex_unlock(&mutex);
igt_devices_scan(true);
}
static int __open_driver(const char *base, int offset, unsigned int chipset, int as_idx)
{
int fd;
fd = __search_and_open(base, offset, chipset, as_idx);
if (fd != -1)
return fd;
drm_load_module(chipset);
return __search_and_open(base, offset, chipset, as_idx);
}
static int __open_driver_exact(const char *name, unsigned int chipset)
{
int fd;
fd = open_device(name, chipset);
if (fd != -1)
return fd;
drm_load_module(chipset);
return open_device(name, chipset);
}
/*
* A helper to get the first matching card in case a filter is set.
* It does all the extra logging around the filters for us.
*
* @card: pointer to the igt_device_card structure to be filled
* when a card is found.
*
* Returns:
* True if card according to the added filter was found,
* false othwerwise.
*/
static bool __get_card_for_nth_filter(int idx, struct igt_device_card *card)
{
const char *filter;
if (igt_device_filter_count() > idx) {
filter = igt_device_filter_get(idx);
igt_debug("Looking for devices to open using filter %d: %s\n", idx, filter);
if (igt_device_card_match(filter, card)) {
igt_debug("Filter matched %s | %s\n", card->card, card->render);
return true;
}
}
return false;
}
/**
* __drm_open_driver_another:
* @idx: index of the device you are opening
* @chipset: OR'd flags for each chipset to search, eg. #DRIVER_INTEL
*
* This function is intended to be used instead of drm_open_driver() for tests
* that are opening multiple /dev/dri/card* nodes, usually for the purpose of
* multi-GPU testing.
*
* This function opens device in the following order:
*
* 1. when --device arguments are present:
* * device scanning is executed,
* * idx-th filter (starting with 0, filters are semicolon separated) is used
* * if there is no idx-th filter, goto 2
* * first device maching the filter is selected
* * if it's already opened (for indexes = 0..idx-1) we fail with -1
* * otherwise open the device and return the fd
*
* 2. compatibility mode - open the first DRM device we can find that is not
* already opened for indexes 0..idx-1, searching up to 16 device nodes
*
* The test is reponsible to test the interaction between devices in both
* directions if applicable.
*
* Example:
*
* |[<!-- language="c" -->
* igt_subtest_with_dynamic("basic") {
* int first_fd = -1;
* int second_fd = -1;
*
* first_fd = __drm_open_driver_another(0, DRIVER_ANY);
* igt_require(first_fd >= 0);
*
* second_fd = __drm_open_driver_another(1, DRIVER_ANY);
* igt_require(second_fd >= 0);
*
* if (can_do_foo(first_fd, second_fd))
* igt_dynamic("first-to-second")
* test_basic_from_to(first_fd, second_fd);
*
* if (can_do_foo(second_fd, first_fd))
* igt_dynamic("second-to-first")
* test_basic_from_to(second_fd, first_fd);
*
* close(first_fd);
* close(second_fd);
* }
* ]|
*
* Returns:
* An open DRM fd or -1 on error
*/
int __drm_open_driver_another(int idx, int chipset)
{
int fd = -1;
if (chipset != DRIVER_VGEM && igt_device_filter_count() > idx) {
struct igt_device_card card;
bool found;
found = __get_card_for_nth_filter(idx, &card);
if (!found) {
drm_load_module(chipset);
found = __get_card_for_nth_filter(idx, &card);
}
if (!found || !strlen(card.card))
igt_warn("No card matches the filter! [%s]\n",
igt_device_filter_get(idx));
else if (_is_already_opened(card.card, idx))
igt_warn("card maching filter %d is already opened\n", idx);
else
fd = __open_driver_exact(card.card, chipset);
} else {
/* no filter for device idx, let's open whatever is available */
fd = __open_driver("/dev/dri/card", 0, chipset, idx);
}
if (fd >= 0) {
_set_opened_fd(idx, fd);
/* Cache xe_device struct. */
if (is_xe_device(fd))
xe_device_get(fd);
}
return fd;
}
/**
* __drm_open_driver:
* @chipset: OR'd flags for each chipset to search, eg. #DRIVER_INTEL
*
* Function opens device in the following order:
* 1. when --device arguments are present device scanning will be executed,
* then filter argument is used to find matching one.
* 2. compatibility mode - open the first DRM device we can find,
* searching up to 16 device nodes.
*
* Returns:
* An open DRM fd or -1 on error
*/
int __drm_open_driver(int chipset)
{
return __drm_open_driver_another(0, chipset);
}
int __drm_open_driver_render(int chipset)
{
int fd;
if (chipset != DRIVER_VGEM && igt_device_filter_count() > 0) {
struct igt_device_card card;
bool found;
found = __get_card_for_nth_filter(0, &card);
if (!found || !strlen(card.render))
return -1;
fd = __open_driver_exact(card.render, chipset);
} else {
fd = __open_driver("/dev/dri/renderD", 128, chipset, 0);
}
/* Cache xe_device struct. */
if (fd >= 0 && is_xe_device(fd))
xe_device_get(fd);
return fd;
}
static int at_exit_drm_fd = -1;
static int at_exit_drm_render_fd = -1;
static void __cancel_work_at_exit(int fd)
{
igt_terminate_spins(); /* for older kernels */
igt_params_set(fd, "reset", "%u", -1u /* any method */);
igt_drop_caches_set(fd,
/* cancel everything */
DROP_RESET_ACTIVE | DROP_RESET_SEQNO |
/* cleanup */
DROP_ACTIVE | DROP_RETIRE | DROP_IDLE | DROP_FREED);
}
static void cancel_work_at_exit(int sig)
{
if (at_exit_drm_fd < 0)
return;
__cancel_work_at_exit(at_exit_drm_fd);
close(at_exit_drm_fd);
at_exit_drm_fd = -1;
}
static void cancel_work_at_exit_render(int sig)
{
if (at_exit_drm_render_fd < 0)
return;
__cancel_work_at_exit(at_exit_drm_render_fd);
close(at_exit_drm_render_fd);
at_exit_drm_render_fd = -1;
}
static const char *chipset_to_str(int chipset)
{
switch (chipset) {
case DRIVER_INTEL:
return "intel";
case DRIVER_V3D:
return "v3d";
case DRIVER_VC4:
return "vc4";
case DRIVER_VGEM:
return "vgem";
case DRIVER_AMDGPU:
return "amdgpu";
case DRIVER_PANFROST:
return "panfrost";
case DRIVER_MSM:
return "msm";
case DRIVER_XE:
return "xe";
case DRIVER_VMWGFX:
return "vmwgfx";
case DRIVER_ANY:
return "any";
default:
return "other";
}
}
/**
* drm_open_driver:
* @chipset: OR'd flags for each chipset to search, eg. #DRIVER_INTEL
*
* Open a drm legacy device node. This function always returns a valid
* file descriptor.
*
* Returns: a drm file descriptor
*/
int drm_open_driver(int chipset)
{
static int open_count;
int fd;
fd = __drm_open_driver(chipset);
igt_skip_on_f(fd<0, "No known gpu found for chipset flags 0x%u (%s)\n",
chipset, chipset_to_str(chipset));
/* For i915, at least, we ensure that the driver is idle before
* starting a test and we install an exit handler to wait until
* idle before quitting.
*/
if (is_i915_device(fd)) {
if (__sync_fetch_and_add(&open_count, 1) == 0) {
__cancel_work_at_exit(fd);
at_exit_drm_fd = drm_reopen_driver(fd);
igt_install_exit_handler(cancel_work_at_exit);
}
}
return fd;
}
static bool is_valid_fd(int fd)
{
char path[32];
char buf[PATH_MAX];
int len;
if (fd < 0)
return false;
snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
memset(buf, 0, sizeof(buf));
len = readlink(path, buf, sizeof(buf) - 1);
if (len <= 0)
return false;
buf[len] = '\0';
if (strstr(buf, "/dev/dri/card") == buf ||
strstr(buf, "/dev/dri/renderD") == buf)
return true;
return false;
}
/**
* drm_close_driver:
* @fd: a drm file descriptor
*
* Check the given drm file descriptor @fd is valid & Close if it is
* an valid drm fd.
*
* Returns: 0 on success or -1 on error.
*/
int drm_close_driver(int fd)
{
if (!is_valid_fd(fd)) {
igt_warn("Don't attempt to close standard/invalid file "
"descriptor: %d\n", fd);
return -1;
}
/* Remove xe_device from cache. */
if (is_xe_device(fd))
xe_device_put(fd);
return close(fd);
}
/**
* drm_open_driver_master:
* @chipset: OR'd flags for each chipset to search, eg. #DRIVER_INTEL
*
* Open a drm legacy device node and ensure that it is drm master.
*
* Returns:
* The drm file descriptor or -1 on error
*/
int drm_open_driver_master(int chipset)
{
int fd = drm_open_driver(chipset);
igt_device_set_master(fd);
return fd;
}
/**
* drm_open_driver_render:
* @chipset: OR'd flags for each chipset to search, eg. #DRIVER_INTEL
*
* Open a drm render device node.
*
* Returns:
* The drm file descriptor or -1 on error
*/
int drm_open_driver_render(int chipset)
{
static int open_count;
int fd = __drm_open_driver_render(chipset);
/* no render nodes, fallback to drm_open_driver() */
if (fd == -1)
return drm_open_driver(chipset);
if (__sync_fetch_and_add(&open_count, 1))
return fd;
at_exit_drm_render_fd = drm_reopen_driver(fd);
if (chipset & DRIVER_INTEL) {
__cancel_work_at_exit(fd);
igt_install_exit_handler(cancel_work_at_exit_render);
}
return fd;
}
/**
* drm_reopen_driver:
* @fd: re-open the drm file descriptor
*
* Re-opens the drm fd which is useful in instances where a clean default
* context is needed.
*/
int drm_reopen_driver(int fd)
{
char path[256];
snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
fd = open(path, O_RDWR);
igt_assert_fd(fd);
if (is_xe_device(fd))
xe_device_get(fd);
return fd;
}
void igt_require_amdgpu(int fd)
{
igt_require(is_amdgpu_device(fd));
}
void igt_require_intel(int fd)
{
igt_require(is_intel_device(fd));
}
void igt_require_i915(int fd)
{
igt_require(is_i915_device(fd));
}
void igt_require_nouveau(int fd)
{
igt_require(is_nouveau_device(fd));
}
void igt_require_vc4(int fd)
{
igt_require(is_vc4_device(fd));
}
void igt_require_xe(int fd)
{
igt_require(is_xe_device(fd));
}