From patchwork Fri Jun 7 11:51:42 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Janusz Krzysztofik X-Patchwork-Id: 10981555 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id E98611398 for ; Fri, 7 Jun 2019 11:52:35 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D896F28815 for ; Fri, 7 Jun 2019 11:52:35 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id CD25928BB5; Fri, 7 Jun 2019 11:52:35 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 1630528BAB for ; Fri, 7 Jun 2019 11:52:35 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 5CAF389DA2; Fri, 7 Jun 2019 11:52:34 +0000 (UTC) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from mga14.intel.com (mga14.intel.com [192.55.52.115]) by gabe.freedesktop.org (Postfix) with ESMTPS id 12D4689D8E; Fri, 7 Jun 2019 11:52:33 +0000 (UTC) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga103.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 07 Jun 2019 04:52:32 -0700 Received: from jkrzyszt-desk.igk.intel.com ([172.22.244.18]) by orsmga007-auth.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 07 Jun 2019 04:52:28 -0700 From: Janusz Krzysztofik To: igt-dev@lists.freedesktop.org Date: Fri, 7 Jun 2019 13:51:42 +0200 Message-Id: <20190607115142.32668-2-janusz.krzysztofik@linux.intel.com> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190607115142.32668-1-janusz.krzysztofik@linux.intel.com> References: <20190607115142.32668-1-janusz.krzysztofik@linux.intel.com> MIME-Version: 1.0 Subject: [Intel-gfx] [PATCH i-g-t v11 1/1] tests: Add a new test for device hot unplug X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: janusz.krzysztofik@intel.com, intel-gfx@lists.freedesktop.org Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" X-Virus-Scanned: ClamAV using ClamSMTP From: Janusz Krzysztofik 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 Cc: Petri Latvala Cc: Daniel Vetter Cc: Katarzyna Dec Cc: Chris Wilson Cc: Michał Wajdeczko Signed-off-by: Janusz Krzysztofik --- 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 +#include +#include + +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',