From: Janusz Krzysztofik <janusz.krzyszto...@intel.com>

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.argenzi...@intel.com>
Cc: Petri Latvala <petri.latv...@intel.com>
Cc: Daniel Vetter <dan...@ffwll.ch>
Cc: Katarzyna Dec <katarzyna....@intel.com>
Cc: Chris Wilson <ch...@chris-wilson.co.uk>
Cc: Michał Wajdeczko <michal.wajdec...@intel.com>
Signed-off-by: Janusz Krzysztofik <janusz.krzyszto...@intel.com>
---
 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@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to