From 0268d73b0b267c6f5acdd4bf40ae917b3afff96c Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Thu, 1 Dec 2016 21:45:47 +0000 Subject: lib/igt_kmod: New library to support driver loading/unloading and additional helpers. lib/igt_aux: Added igt_pkill and igt_lsof helper. lib/igt_kmod: Added load/unload kmod helpers. v7: - document the case where leaving stray fd from drm_open_driver() might fail reloading the driver. - list also current opened files from /dev/dri in case we could not unload the driver. - convert igt_info to igt_warn (Chris Wilson) - added KMOD_|PROCPS CFLAGS (Chris Wilson) v6: - include latest modifications from tests/drv_module_reload: display all loaded modules and list information about opened files by processes (Petri Latvala) v5: - added igt_i915_driver_{load/unload}. - added kick_snd_hda_intel() to match current tests/drv_module_reload_basic and integrated into igt_i915_driver_load/unload. - added gtk-doc section for lib/igt_kmod v4: - decided to split libkmod helpers into their own file as there's another user lib/igt_gvt or tests/gvt_basic. - fixed some gtk-doc documentation. v3: - return -errno (igt_pkill()) in case of failure (Cris Wilson) - return bool for igt_kmod_is_loaded(), replaced strncasecmp with strncmp (Chris Wilson) v2: - Renamed libkmod helpers (Chris Wilson) - Removed SIGTERM/SIGKILL case where we repeatedly tried to terminate the process: just call kill(2) once (Chris Wilson) - Removed redundant check in igt_kmod_unload(), igt_module_in_use() (Chris Wilson) - Pass flags to igt_kmod_unload() from the caller (Chris Wilson) - Removed useless function igt_kill() which acts just as kill(2) (Chris Wilson) Signed-off-by: Marius Vlad --- lib/igt_kmod.c | 328 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 328 insertions(+) create mode 100644 lib/igt_kmod.c (limited to 'lib/igt_kmod.c') diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c new file mode 100644 index 00000000..9fb66919 --- /dev/null +++ b/lib/igt_kmod.c @@ -0,0 +1,328 @@ +/* + * Copyright © 2016 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_core.h" +#include "igt_sysfs.h" +#include "igt_kmod.h" + +#include + +/** + * SECTION:igt_kmod + * @short_description: Wrappers around libkmod for module loading/unloading + * @title: kmod + * @include: igt.h + * + * This library provides helpers to load/unload module driver. + * + * Note on loading/reloading: + * + * Loading/unload/reloading the driver requires that resources to /dev/dri to + * be released (closed). A potential mistake would be to submit commands to the + * GPU by having a fd returned by @drm_open_driver, which is closed by atexit + * signal handler so reloading/unloading the driver will fail if performed + * afterwards. One possible solution to this issue is to use + * @__drm_open_driver() or use @igt_set_module_param() to set module parameters + * dynamically. + */ + + +/** + * igt_kmod_is_loaded: + * @mod_name: The name of the module. + * + * Returns: True in case the module has been found or false otherwise. + * + * Function to check the existance of module @mod_name in list of loaded kernel + * modules. + * + */ +bool +igt_kmod_is_loaded(const char *mod_name) +{ + struct kmod_list *mod, *list; + struct kmod_ctx *ctx; + bool ret = false; + + ctx = kmod_new(NULL, NULL); + igt_assert(ctx != NULL); + + if (kmod_module_new_from_loaded(ctx, &list) < 0) { + goto out; + } + + kmod_list_foreach(mod, list) { + struct kmod_module *kmod = kmod_module_get_module(mod); + const char *kmod_name = kmod_module_get_name(kmod); + + if (!strncmp(kmod_name, mod_name, strlen(kmod_name))) { + kmod_module_unref(kmod); + ret = true; + break; + } + kmod_module_unref(kmod); + } + kmod_module_unref_list(list); +out: + kmod_unref(ctx); + + return ret; +} + +/** + * igt_kmod_load: + * @mod_name: The name of the module + * @opts: Parameters for the module. NULL in case no parameters + * are to be passed, or a '\0' terminated string otherwise. + * + * Returns: 0 in case of success or -errno in case the module could not + * be loaded. + * + * This function loads a kernel module using the name specified in @mod_name. + * + * @Note: This functions doesn't automatically resolve other module + * dependencies so make make sure you load the dependencies module(s) before + * this one. + */ +int +igt_kmod_load(const char *mod_name, const char *opts) +{ + struct kmod_ctx *ctx; + struct kmod_module *kmod; + int err = 0; + + ctx = kmod_new(NULL, NULL); + igt_assert(ctx != NULL); + + err = kmod_module_new_from_name(ctx, mod_name, &kmod); + if (err < 0) { + goto out; + } + + err = kmod_module_insert_module(kmod, 0, opts); + if (err < 0) { + switch (err) { + case -EEXIST: + igt_debug("Module %s already inserted\n", + kmod_module_get_name(kmod)); + break; + case -ENOENT: + igt_debug("Unknown symbol in module %s or " + "unknown parameter\n", + kmod_module_get_name(kmod)); + break; + default: + igt_debug("Could not insert %s (%s)\n", + kmod_module_get_name(kmod), strerror(-err)); + break; + } + } +out: + kmod_module_unref(kmod); + kmod_unref(ctx); + + return -err ? err < 0 : err; +} + + +/** + * igt_kmod_unload: + * @mod_name: Module name. + * @flags: flags are passed directly to libkmod and can be: + * KMOD_REMOVE_FORCE or KMOD_REMOVE_NOWAIT. + * + * Returns: 0 in case of success or -errno otherwise. + * + * Removes the module @mod_name. + * + */ +int +igt_kmod_unload(const char *mod_name, unsigned int flags) +{ + struct kmod_ctx *ctx; + struct kmod_module *kmod; + int err = 0; + + ctx = kmod_new(NULL, NULL); + igt_assert(ctx != NULL); + + err = kmod_module_new_from_name(ctx, mod_name, &kmod); + if (err < 0) { + igt_debug("Could not use module %s (%s)\n", mod_name, + strerror(-err)); + goto out; + } + + err = kmod_module_remove_module(kmod, flags); + if (err < 0) { + igt_debug("Could not remove module %s (%s)\n", mod_name, + strerror(-err)); + } + +out: + kmod_module_unref(kmod); + kmod_unref(ctx); + + return -err ? err < 0 : err; +} + +/** + * + * igt_kmod_list_loaded: List all modules currently loaded. + * + */ +void +igt_kmod_list_loaded(void) +{ + struct kmod_list *module, *list; + struct kmod_ctx *ctx; + + ctx = kmod_new(NULL, NULL); + igt_assert(ctx != NULL); + + if (kmod_module_new_from_loaded(ctx, &list) < 0) { + kmod_unref(ctx); + return; + } + + igt_info("Module\t\t Used by\n"); + + kmod_list_foreach(module, list) { + struct kmod_module *kmod = kmod_module_get_module(module); + struct kmod_list *module_deps, *module_deps_list; + + igt_info("%-24s", kmod_module_get_name(kmod)); + module_deps_list = kmod_module_get_holders(kmod); + if (module_deps_list) { + + kmod_list_foreach(module_deps, module_deps_list) { + struct kmod_module *kmod_dep; + + kmod_dep = kmod_module_get_module(module_deps); + igt_info("%s", + kmod_module_get_name(kmod_dep)); + + if (kmod_list_next(module_deps_list, module_deps)) + igt_info(","); + + kmod_module_unref(kmod_dep); + } + } + kmod_module_unref_list(module_deps_list); + + igt_info("\n"); + kmod_module_unref(kmod); + } + + kmod_module_unref_list(list); + kmod_unref(ctx); +} + +/** + * igt_i915_driver_load: + * @opts: options to pass to i915 driver + * + * Loads the i915 driver and its dependencies. + * + */ +int +igt_i915_driver_load(const char *opts) +{ + if (opts) + igt_info("Reloading i915 with %s\n\n", opts); + + /* we do not have automatic loading of dependencies */ + igt_kmod_load("drm", NULL); + igt_kmod_load("drm_kms_helper", NULL); + + if (igt_kmod_load("i915", opts)) { + igt_warn("Could not load i915\n"); + return IGT_EXIT_FAILURE; + } + + kick_fbcon(true); + igt_kmod_load("snd_hda_intel", NULL); + + return IGT_EXIT_SUCCESS; +} + +/** + * igt_i915_driver_unload: + * + * Unloads the i915 driver and its dependencies. + * + */ +int +igt_i915_driver_unload(void) +{ + /* unbind vt */ + kick_fbcon(false); + + if (igt_kmod_is_loaded("snd_hda_intel")) { + igt_terminate_process(SIGTERM, "alsactl"); + + /* unbind snd_hda_intel */ + kick_snd_hda_intel(); + + if (igt_kmod_unload("snd_hda_intel", 0)) { + igt_warn("Could not unload snd_hda_intel\n"); + igt_kmod_list_loaded(); + igt_lsof("/dev/snd"); + return IGT_EXIT_FAILURE; + } + } + + /* gen5 */ + if (igt_kmod_is_loaded("intel_ips")) { + igt_kmod_unload("intel_ips", 0); + } + + if (igt_kmod_is_loaded("i915")) { + if (igt_kmod_unload("i915", 0)) { + igt_warn("Could not unload i915\n"); + igt_kmod_list_loaded(); + igt_lsof("/dev/dri"); + return IGT_EXIT_SKIP; + } else { + igt_info("i915.ko has been unloaded!\n"); + } + } + + if (igt_kmod_is_loaded("intel-gtt")) { + igt_kmod_unload("intel-gtt", 0); + } + + igt_kmod_unload("drm_kms_helper", 0); + igt_kmod_unload("drm", 0); + + if (igt_kmod_is_loaded("i915")) { + igt_warn("i915.ko still loaded!\n"); + return IGT_EXIT_FAILURE; + } else { + igt_info("module successfully unloaded\n"); + } + + + return IGT_EXIT_SUCCESS; +} -- cgit v1.2.3