/* * 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 * Daniel Vetter * */ #ifdef HAVE_LIBGEN_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "drmtest.h" #include "i915_drm.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_sysfs.h" #include "version.h" #include "config.h" #include "intel_reg.h" #include "ioctl_wrappers.h" #include "igt_dummyload.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_vc4_device(int fd) { return __is_device(fd, "vc4"); } static bool has_known_intel_chipset(int fd) { struct drm_i915_getparam gp; int devid = 0; memset(&gp, 0, sizeof(gp)); gp.param = I915_PARAM_CHIPSET_ID; gp.value = &devid; if (ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp, sizeof(gp))) return false; if (!intel_gen(devid)) return false; return true; } 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; } #define LOCAL_I915_EXEC_VEBOX (4 << 0) /** * gem_quiescent_gpu: * @fd: open i915 drm file descriptor * * Ensure the gpu is idle by launching a nop execbuf and stalling for it. This * is automatically run when opening a drm device node and is also installed as * an exit handler to have the best assurance that the test is run in a pristine * and controlled environment. * * This function simply allows tests to make additional calls in-between, if so * desired. */ void gem_quiescent_gpu(int fd) { igt_terminate_spins(); igt_drop_caches_set(fd, DROP_ACTIVE | DROP_RETIRE | DROP_IDLE | DROP_FREED); } 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_PANFROST, "panfrost" }, { DRIVER_V3D, "v3d" }, { DRIVER_VC4, "vc4" }, { DRIVER_VGEM, "vgem" }, {} }; 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) return fd; err: close(fd); return -1; } static int __search_and_open(const char *base, int offset, unsigned int chipset) { const char *forced; forced = forced_driver(); if (forced) igt_info("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); fd = open_device(name, chipset); if (fd != -1) return fd; } return -1; } static int __open_driver(const char *base, int offset, unsigned int chipset) { static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; int fd; fd = __search_and_open(base, offset, chipset); if (fd != -1) return fd; 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); return __search_and_open(base, offset, chipset); } /** * __drm_open_driver: * @chipset: OR'd flags for each chipset to search, eg. #DRIVER_INTEL * * 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 __open_driver("/dev/dri/card", 0, chipset); } static int __drm_open_driver_render(int chipset) { return __open_driver("/dev/dri/renderD", 128, chipset); } 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_sysfs_set_parameter(fd, "reset", "%x", -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_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) { gem_quiescent_gpu(fd); at_exit_drm_fd = __drm_open_driver(chipset); igt_install_exit_handler(cancel_work_at_exit); } } return 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_open_driver(chipset); if(chipset & DRIVER_INTEL){ gem_quiescent_gpu(fd); igt_install_exit_handler(cancel_work_at_exit_render); } return fd; } void igt_require_amdgpu(int fd) { igt_require(is_amdgpu_device(fd)); } void igt_require_intel(int fd) { igt_require(is_i915_device(fd) && has_known_intel_chipset(fd)); } void igt_require_vc4(int fd) { igt_require(is_vc4_device(fd)); }