[v9,1/1] tests: Add a new test for device hot unplug

Submitted by Janusz Krzysztofik on May 8, 2019, 4:35 p.m.

Details

Message ID 20190508163512.7647-2-janusz.krzysztofik@linux.intel.com
State New
Headers show
Series "tests: Add a new test for device hot unplug" ( rev: 1 ) in IGT - Trybot

Not browsing as part of any series.

Commit Message

Janusz Krzysztofik May 8, 2019, 4:35 p.m.
From: Janusz Krzysztofik <janusz.krzysztofik@intel.com>

Signed-off-by: Janusz Krzysztofik <janusz.krzysztofik@intel.com>
---
 tests/Makefile.sources |   1 +
 tests/core_hotunplug.c | 456 +++++++++++++++++++++++++++++++++++++++++
 tests/meson.build      |   1 +
 3 files changed, 458 insertions(+)
 create mode 100644 tests/core_hotunplug.c

Patch hide | download patch | download mbox

diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 7f921f6c..08942f00 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -16,6 +16,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..6c4600ad
--- /dev/null
+++ b/tests/core_hotunplug.c
@@ -0,0 +1,456 @@ 
+/*
+ * Copyright © 2019 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.
+ */
+
+#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>
+
+/* Re-bind the driver to the device */
+static void driver_bind(int drv, const char *addr)
+{
+	igt_set_timeout(60, "Driver re-bind timeout!");
+	igt_sysfs_set(drv, "bind", addr);
+	igt_reset_timeout();
+
+	close(drv);
+}
+
+/* Unbind the driver from the device */
+static void driver_unbind(int drv, const char *addr)
+{
+	igt_set_timeout(60, "Driver unbind timeout!");
+	igt_sysfs_set(drv, "unbind", addr);
+	igt_reset_timeout();
+
+	/* don't close drv, it will be used for driver rebinding */
+}
+
+/* Re-discover the device by rescanning its bus */
+static void bus_rescan(int bus)
+{
+	igt_set_timeout(60, "Bus rescan timeout!");
+	igt_sysfs_set(bus, "rescan", "1");
+	igt_reset_timeout();
+
+	close(bus);
+}
+
+/* Remove (virtually unplug) the device from its bus */
+static void device_unplug(int dev)
+{
+	igt_set_timeout(60, "Device unplug timeout!");
+	igt_sysfs_set(dev, "device/remove", "1");
+	igt_reset_timeout();
+
+	close(dev);
+}
+
+static bool module_unload(int chipset, const char *module)
+{
+	if (chipset == DRIVER_INTEL)
+		return igt_i915_driver_unload() == IGT_EXIT_SUCCESS;
+	else
+		return igt_kmod_unload(module, 0) == 0;
+}
+
+static void unbind_rebind(int chipset)
+{
+	int fd, dev, drv, len;
+	char path[PATH_MAX];
+	const char *addr;
+
+	fd = __drm_open_driver(chipset);
+	igt_assert(fd >= 0);
+
+	dev = igt_sysfs_open(fd);
+	igt_assert(dev >= 0);
+
+	close(fd);
+
+	/* collect information required for driver bind/unbind */
+	drv = openat(dev, "device/driver", O_DIRECTORY);
+	igt_assert(drv >= 0);
+
+	len = readlinkat(dev, "device", path, sizeof(path) - 1);
+	path[len] = '\0';
+	addr = strrchr(path, '/') + 1;
+
+	close(dev);
+
+	igt_debug("unbinding driver\n");
+        driver_unbind(drv, addr);
+
+	igt_debug("rebinding driver\n");
+	driver_bind(drv, addr);
+
+	igt_debug("reopening device\n");
+	fd = __drm_open_driver(chipset);
+	igt_assert(fd >= 0);
+
+	close(fd);
+}
+
+static void unplug_rescan(int chipset)
+{
+	int fd, dev, bus;
+
+	fd = __drm_open_driver(chipset);
+	igt_assert(fd >= 0);
+
+	dev = igt_sysfs_open(fd);
+	igt_assert(dev >= 0);
+
+	close(fd);
+
+	/* collect information required for bus rescan */
+	bus = openat(dev, "device/subsystem", O_DIRECTORY);
+	igt_assert(bus >= 0);
+
+	igt_debug("unplugging device\n");
+        device_unplug(dev);
+
+	igt_debug("recovering device\n");
+	bus_rescan(bus);
+
+	igt_debug("reopening driver\n");
+	fd = __drm_open_driver(chipset);
+	igt_assert(fd >= 0);
+
+	close(fd);
+}
+
+static void drm_open_hotunplug(int chipset)
+{
+	int fd, dev, bus;
+
+	fd = __drm_open_driver(chipset);
+	igt_assert(fd >= 0);
+
+	dev = igt_sysfs_open(fd);
+	igt_assert(dev >= 0);
+
+	/* collect information required for bus rescan */
+	bus = openat(dev, "device/subsystem", O_DIRECTORY);
+	igt_assert(bus >= 0);
+
+	igt_debug("unplugging device\n");
+        device_unplug(dev);
+
+	igt_debug("recovering device\n");
+	bus_rescan(bus);
+
+	igt_debug("closing device\n");
+	close(fd);
+
+	igt_debug("reopening driver\n");
+	fd = __drm_open_driver(chipset);
+	igt_assert(fd >= 0);
+
+	close(fd);
+}
+
+static void gem_buffer_hotunplug(int chipset)
+{
+	int fd, dev, bus;
+	struct igt_fb fb;
+
+	fd = __drm_open_driver(chipset);
+	igt_assert(fd >= 0);
+
+	igt_debug("creating GEM object\n");
+	igt_create_bo_for_fb(fd, 1, 1, DRM_FORMAT_XRGB8888, 0, &fb);
+
+	dev = igt_sysfs_open(fd);
+	igt_assert(dev >= 0);
+
+	/* collect information required for bus rescan */
+	bus = openat(dev, "device/subsystem", O_DIRECTORY);
+	igt_assert(bus >= 0);
+
+	igt_debug("unplugging device\n");
+        device_unplug(dev);
+
+	igt_debug("recovering device\n");
+	bus_rescan(bus);
+
+	igt_debug("closing device\n");
+	close(fd);
+
+	igt_debug("reopening driver\n");
+	fd = __drm_open_driver(chipset);
+	igt_assert(fd >= 0);
+
+	close(fd);
+}
+
+static void gem_mmap_hotunplug(int chipset)
+{
+	int fd, dev, bus;
+	struct igt_fb fb;
+
+	fd = __drm_open_driver(chipset);
+	igt_assert(fd >= 0);
+
+	igt_debug("mmapping GEM object\n");
+	igt_create_bo_for_fb(fd, 1, 1, DRM_FORMAT_XRGB8888, 0, &fb);
+	igt_ignore_warn(igt_fb_map_buffer(fd, &fb));
+
+	dev = igt_sysfs_open(fd);
+	igt_assert(dev >= 0);
+
+	/* collect information required for bus rescan */
+	bus = openat(dev, "device/subsystem", O_DIRECTORY);
+	igt_assert(bus >= 0);
+
+	igt_debug("unplugging device\n");
+        device_unplug(dev);
+
+	igt_debug("recovering device\n");
+	bus_rescan(bus);
+
+	igt_debug("closing device\n");
+	close(fd);
+
+	igt_debug("reopening driver\n");
+	fd = __drm_open_driver(chipset);
+	igt_assert(fd >= 0);
+
+	close(fd);
+}
+
+static void i915_context_hotunplug(int chipset)
+{
+	int fd, dev, bus;
+
+	fd = __drm_open_driver(chipset);
+	igt_assert(fd >= 0);
+
+	gem_require_contexts(fd);
+
+	igt_debug("creating GEM context\n");
+	igt_ignore_warn(gem_context_create(fd));
+
+	dev = igt_sysfs_open(fd);
+	igt_assert(dev >= 0);
+
+	/* collect information required for bus rescan */
+	bus = openat(dev, "device/subsystem", O_DIRECTORY);
+	igt_assert(bus >= 0);
+
+	igt_debug("unplugging device\n");
+        device_unplug(dev);
+
+	igt_debug("recovering device\n");
+	bus_rescan(bus);
+
+	igt_debug("closing device\n");
+	close(fd);
+
+	igt_debug("reopening driver\n");
+	fd = __drm_open_driver(chipset);
+	igt_assert(fd >= 0);
+
+	close(fd);
+}
+
+static void i915_hotunplug_write(int chipset)
+{
+	int fd, dev, bus, gem;
+	const uint32_t bbe = MI_BATCH_BUFFER_END;
+
+	fd = __drm_open_driver(chipset);
+	igt_assert(fd >= 0);
+
+	igt_require_gem(fd);
+
+	igt_debug("creating GEM buffer object\n");
+	gem = gem_create(fd, 1);
+
+	dev = igt_sysfs_open(fd);
+	igt_assert(dev >= 0);
+
+	/* collect information required for bus rescan */
+	bus = openat(dev, "device/subsystem", O_DIRECTORY);
+	igt_assert(bus >= 0);
+
+	igt_debug("unplugging device\n");
+        device_unplug(dev);
+
+	igt_debug("trying to write to GEM buffer\n");
+	igt_assert_neq(__gem_write(fd, gem, 0, &bbe, sizeof(bbe)), 0);
+
+	igt_debug("recovering device\n");
+	bus_rescan(bus);
+
+	igt_debug("closing device\n");
+	close(fd);
+
+	igt_debug("reopening driver\n");
+	fd = __drm_open_driver(chipset);
+	igt_assert(fd >= 0);
+
+	close(fd);
+}
+
+static void i915_spin_hotunplug(int chipset)
+{
+	int fd, dev, bus;
+
+	fd = __drm_open_driver(chipset);
+	igt_assert(fd >= 0);
+
+	igt_debug("submitting dummy load\n");
+	igt_ignore_warn(igt_spin_new(fd));
+
+	dev = igt_sysfs_open(fd);
+	igt_assert(dev >= 0);
+
+	/* collect information required for bus rescan */
+	bus = openat(dev, "device/subsystem", O_DIRECTORY);
+	igt_assert(bus >= 0);
+
+	igt_debug("unplugging device\n");
+        device_unplug(dev);
+
+	igt_debug("recovering device\n");
+	bus_rescan(bus);
+
+	igt_debug("closing device\n");
+	close(fd);
+
+	igt_debug("reopening driver\n");
+	fd = __drm_open_driver(chipset);
+	igt_assert(fd >= 0);
+
+	igt_debug("performing healthcheck\n");
+	gem_test_engine(fd, ALL_ENGINES);
+
+	close(fd);
+}
+
+static void drm_hotunplug_unload(int chipset, const char *module)
+{
+	int fd, dev, bus;
+
+	fd = __drm_open_driver(chipset);
+	igt_assert(fd >= 0);
+
+	dev = igt_sysfs_open(fd);
+	igt_assert(dev >= 0);
+
+	/* collect information required for bus rescan */
+	bus = openat(dev, "device/subsystem", O_DIRECTORY);
+	igt_assert(bus >= 0);
+
+	igt_debug("unplugging device\n");
+        device_unplug(dev);
+
+	igt_debug("trying to unload module\n");
+	igt_assert(!module_unload(chipset, module));
+
+	igt_debug("closing device\n");
+	close(fd);
+
+	igt_debug("unloading module\n");
+	igt_assert(module_unload(chipset, module));
+
+	igt_debug("recovering device\n");
+	bus_rescan(bus);
+
+	igt_debug("reopening driver\n");
+	fd = __drm_open_driver(chipset);
+	igt_assert(fd >= 0);
+
+	close(fd);
+}
+
+igt_main {
+	int chipset;
+	char *module;
+
+	igt_fixture {
+		char path[PATH_MAX];
+		int fd, 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_open_driver(DRIVER_ANY);
+		igt_assert(fd >= 0);
+
+		if (is_i915_device(fd)) {
+			chipset = DRIVER_INTEL;
+			module = strdup("i915");
+		} else {
+			chipset = DRIVER_ANY;
+
+			/* Capture module name to be unloaded */
+			dev = igt_sysfs_open(fd);
+			len = readlinkat(dev, "device/driver/module", path,
+					 sizeof(path) - 1);
+			close(dev);
+			path[len] = '\0';
+			module = strdup(strrchr(path, '/') + 1);
+		}
+		close(fd);
+
+		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_subtest("gem_buffer-hotunplug")
+		gem_buffer_hotunplug(chipset);
+
+	igt_subtest("gem_mmap-hotunplug")
+		gem_mmap_hotunplug(chipset);
+
+	igt_subtest("i915_context-hotunplug")
+		i915_context_hotunplug(chipset);
+
+	igt_subtest("i915-hotunplug-write")
+		i915_hotunplug_write(chipset);
+
+	igt_subtest("i915_spin-hotunplug")
+		i915_spin_hotunplug(chipset);
+
+	igt_subtest("drm-hotunplug-unload")
+		drm_hotunplug_unload(chipset, module);
+}
diff --git a/tests/meson.build b/tests/meson.build
index 711979b4..ff391c94 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',