From: Janusz Krzysztofik <janusz.krzysztofik@xxxxxxxxx> There is a test which verifies unloading of i915 driver module but no test exists that checks how a driver behaves when it gets unbound from a device or when the device gets unplugged. Provide such test using sysfs interface. Two minimalistic subtests - "unbind-rebind" and "unplug-rescan" - perform desired operations on a DRM device which is beleived to be not in use. A subtest named "drm_open-hotunplug" unplugs a DRM device while keeping a file descriptor open. Changelog: v2: - run a subprocess with dummy_load instead of external command (Antonio). v3: - run dummy_load from the test process directly (Antonio). v4: - run dummy_load from inside subtests (Antonio). v5: - try to restore the device to a working state after each subtest (Petri, Daniel). v6: - run workload inside an igt helper subprocess so resources consumed by the workload are cleaned up automatically on workload subprocess crash, without affecting test results, - move the igt helper with workload back from subtests to initial fixture so workload crash also does not affect test results, - other cleanups suggested by Katarzyna and Chris. v7: - no changes. v8: - move workload functions back from fixture to subtests, - register different actions and different workloads in respective tables and iterate over those tables while enumerating subtests, - introduce new subtest flavors by simply omiting module unload step, - instead of simply requesting bus rescan or not, introduce action specific device recovery helpers, required specifically with those new subtests not touching the module, - split workload functions in two parts, one spawning the workload, the other waiting for its completion, - for the new subtests not requiring module unload, run workload functions directly from the test process and use new workload completion wait functions in place of subprocess completion wait, - take more control over logging, longjumps and exit codes in workload subprocesses, - add some debug messages for easy progress watching, - move function API descriptions on top of respective typedefs. v9: All changes after Daniel's comments - thanks! - flatten the code, don't try to create a midlayer (Daniel), - provide mimimal subtests that even don't keep device open (Daniel), - don't use driver unbind in more advanced subtests (Daniel), - provide subtests with different level of resources allocated during device unplug (Daniel), - provide subtests which check driver behavior after device hot unplug (Daniel). v10: - rename variables and function arguments to something that indicates they're file descriptors (Daniel), - introduce a data structure that contains various file descriptors and a helper function to set them all (Daniel), - fix strange indenting (Daniel), - limit scope to first three subtests as the first set of tests to merge (Daniel). v11: - fix typos in some comments, - use SPDX license identifier, - include a per-patch changelog in the commit message (Daniel). Cc: Antonio Argenziano <antonio.argenziano@xxxxxxxxx> Cc: Petri Latvala <petri.latvala@xxxxxxxxx> Cc: Daniel Vetter <daniel@xxxxxxxx> Cc: Katarzyna Dec <katarzyna.dec@xxxxxxxxx> Cc: Chris Wilson <chris@xxxxxxxxxxxxxxxxxx> Cc: Michał Wajdeczko <michal.wajdeczko@xxxxxxxxx> Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@xxxxxxxxx> --- tests/Makefile.sources | 1 + tests/core_hotunplug.c | 222 +++++++++++++++++++++++++++++++++++++++++ tests/meson.build | 1 + 3 files changed, 224 insertions(+) create mode 100644 tests/core_hotunplug.c diff --git a/tests/Makefile.sources b/tests/Makefile.sources index 027ed82f..3f24265f 100644 --- a/tests/Makefile.sources +++ b/tests/Makefile.sources @@ -17,6 +17,7 @@ TESTS_progs = \ core_getclient \ core_getstats \ core_getversion \ + core_hotunplug \ core_setmaster_vs_auth \ debugfs_test \ drm_import_export \ diff --git a/tests/core_hotunplug.c b/tests/core_hotunplug.c new file mode 100644 index 00000000..d36a0572 --- /dev/null +++ b/tests/core_hotunplug.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright © 2019 Intel Corporation + */ + +#include "igt.h" +#include "igt_device.h" +#include "igt_dummyload.h" +#include "igt_kmod.h" +#include "igt_sysfs.h" + +#include <limits.h> +#include <string.h> +#include <unistd.h> + +struct hotunplug { + int chipset; + struct { + int drm; + int sysfs_dev; + int sysfs_bus; + } fd; +}; + +/* Helpers */ + +static void prepare(struct hotunplug *priv) +{ + /* open the driver */ + priv->fd.drm = __drm_open_driver(priv->chipset); + igt_assert(priv->fd.drm >= 0); + + /* prepare for device unplug */ + priv->fd.sysfs_dev = igt_sysfs_open(priv->fd.drm); + igt_assert(priv->fd.sysfs_dev >= 0); + + /* prepare for bus rescan */ + priv->fd.sysfs_bus = openat(priv->fd.sysfs_dev, "device/subsystem", + O_DIRECTORY); + igt_assert(priv->fd.sysfs_bus >= 0); +} + +/* Re-bind the driver to the device */ +static void driver_bind(int fd_sysfs_drv, const char *dev_bus_addr) +{ + igt_set_timeout(60, "Driver re-bind timeout!"); + igt_sysfs_set(fd_sysfs_drv, "bind", dev_bus_addr); + igt_reset_timeout(); + + close(fd_sysfs_drv); +} + +/* Unbind the driver from the device */ +static void driver_unbind(int fd_sysfs_drv, const char *dev_bus_addr) +{ + igt_set_timeout(60, "Driver unbind timeout!"); + igt_sysfs_set(fd_sysfs_drv, "unbind", dev_bus_addr); + igt_reset_timeout(); + + /* don't close fd_sysfs_drv, it will be used for driver rebinding */ +} + +/* Re-discover the device by rescanning its bus */ +static void bus_rescan(int fd_sysfs_bus) +{ + igt_set_timeout(60, "Bus rescan timeout!"); + igt_sysfs_set(fd_sysfs_bus, "rescan", "1"); + igt_reset_timeout(); + + close(fd_sysfs_bus); +} + +/* Remove (virtually unplug) the device from its bus */ +static void device_unplug(int fd_sysfs_dev) +{ + igt_set_timeout(60, "Device unplug timeout!"); + igt_sysfs_set(fd_sysfs_dev, "device/remove", "1"); + igt_reset_timeout(); + + close(fd_sysfs_dev); +} + +/* Subtests */ + +static void unbind_rebind(int chipset) +{ + struct hotunplug priv; + int fd_sysfs_drv, len; + char path[PATH_MAX]; + const char *dev_bus_addr; + + priv.chipset = chipset; + prepare(&priv); + + /* sysfs_bus not needed for unbind/rebind */ + close(priv.fd.sysfs_bus); + + /* prepare for driver bind/unbind */ + fd_sysfs_drv = openat(priv.fd.sysfs_dev, "device/driver", O_DIRECTORY); + igt_assert(fd_sysfs_drv >= 0); + + len = readlinkat(priv.fd.sysfs_dev, "device", path, sizeof(path) - 1); + path[len] = '\0'; + dev_bus_addr = strrchr(path, '/') + 1; + + /* sysfs_dev no longer needed for unbind/rebind */ + close(priv.fd.sysfs_dev); + + igt_debug("closing device\n"); + close(priv.fd.drm); + + igt_debug("unbinding driver\n"); + driver_unbind(fd_sysfs_drv, dev_bus_addr); + + igt_debug("rebinding driver\n"); + driver_bind(fd_sysfs_drv, dev_bus_addr); + + igt_debug("reopening device\n"); + priv.fd.drm = __drm_open_driver(chipset); + igt_assert(priv.fd.drm >= 0); + + close(priv.fd.drm); +} + +static void unplug_rescan(int chipset) +{ + struct hotunplug priv; + + priv.chipset = chipset; + prepare(&priv); + + igt_debug("closing device\n"); + close(priv.fd.drm); + + igt_debug("unplugging device\n"); + device_unplug(priv.fd.sysfs_dev); + + igt_debug("recovering device\n"); + bus_rescan(priv.fd.sysfs_bus); + + igt_debug("reopening driver\n"); + priv.fd.drm = __drm_open_driver(chipset); + igt_assert(priv.fd.drm >= 0); + + close(priv.fd.drm); +} + +static void drm_open_hotunplug(int chipset) +{ + struct hotunplug priv; + + priv.chipset = chipset; + prepare(&priv); + + igt_debug("unplugging device\n"); + device_unplug(priv.fd.sysfs_dev); + + igt_debug("recovering device\n"); + bus_rescan(priv.fd.sysfs_bus); + + igt_debug("closing device\n"); + close(priv.fd.drm); + + igt_debug("reopening driver\n"); + priv.fd.drm = __drm_open_driver(chipset); + igt_assert(priv.fd.drm >= 0); + + close(priv.fd.drm); +} + +/* Main */ + +igt_main { + int chipset; + char *module; + + igt_fixture { + char path[PATH_MAX]; + int fd_drm, fd_sysfs_dev, len; + + /** + * Since some subtests depend on successful unload of a driver + * module, don't use drm_open_driver() as it keeps a device file + * descriptor open for exit handler use and that effectively + * prevents the module from being unloaded. + */ + fd_drm = __drm_open_driver(DRIVER_ANY); + igt_assert(fd_drm >= 0); + + if (is_i915_device(fd_drm)) { + chipset = DRIVER_INTEL; + module = strdup("i915"); + } else { + chipset = DRIVER_ANY; + + /* Capture module name to be unloaded */ + fd_sysfs_dev = igt_sysfs_open(fd_drm); + len = readlinkat(fd_sysfs_dev, "device/driver/module", + path, sizeof(path) - 1); + close(fd_sysfs_dev); + path[len] = '\0'; + module = strdup(strrchr(path, '/') + 1); + } + close(fd_drm); + + igt_info("Running the test on driver \"%s\", chipset mask %#0x\n", + module, chipset); + } + + igt_subtest("unbind-rebind") + unbind_rebind(chipset); + + igt_subtest("unplug-rescan") + unplug_rescan(chipset); + + igt_subtest("drm_open-hotunplug") + drm_open_hotunplug(chipset); + + igt_fixture { + free(module); + } +} diff --git a/tests/meson.build b/tests/meson.build index f168fbba..0c022521 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -3,6 +3,7 @@ test_progs = [ 'core_getclient', 'core_getstats', 'core_getversion', + 'core_hotunplug', 'core_setmaster_vs_auth', 'debugfs_test', 'drm_import_export', -- 2.21.0 _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/intel-gfx