From patchwork Thu Dec 1 12:23:55 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Marius Vlad X-Patchwork-Id: 9455929 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 55C806074E for ; Thu, 1 Dec 2016 12:18:36 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4754F28484 for ; Thu, 1 Dec 2016 12:18:36 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 3A979284CF; Thu, 1 Dec 2016 12:18:36 +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=-4.2 required=2.0 tests=BAYES_00, 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 EE87628484 for ; Thu, 1 Dec 2016 12:18:34 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 4690B6E1B5; Thu, 1 Dec 2016 12:18:31 +0000 (UTC) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from mga09.intel.com (mga09.intel.com [134.134.136.24]) by gabe.freedesktop.org (Postfix) with ESMTPS id D9C3D6E1AD for ; Thu, 1 Dec 2016 12:18:28 +0000 (UTC) Received: from fmsmga003.fm.intel.com ([10.253.24.29]) by orsmga102.jf.intel.com with ESMTP; 01 Dec 2016 04:18:05 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.33,282,1477983600"; d="scan'208";a="793024308" Received: from mcvlad-wk.rb.intel.com ([10.237.105.57]) by FMSMGA003.fm.intel.com with ESMTP; 01 Dec 2016 04:18:04 -0800 From: Marius Vlad To: intel-gfx@lists.freedesktop.org Date: Thu, 1 Dec 2016 14:23:55 +0200 Message-Id: <20161201122358.1124-2-marius.c.vlad@intel.com> X-Mailer: git-send-email 2.10.1 In-Reply-To: <20161201122358.1124-1-marius.c.vlad@intel.com> References: <20161201122358.1124-1-marius.c.vlad@intel.com> MIME-Version: 1.0 Subject: [Intel-gfx] [PATCH i-g-t 1/4 v7] lib/igt_kmod: New library to support driver loading/unloading and additional helpers. X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" X-Virus-Scanned: ClamAV using ClamSMTP 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) - use igt_debug() instead of igt_info() for igt_kmod_load()/ igt_kmod_unload() and return -err directly from libkmod (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 --- configure.ac | 2 + .../intel-gpu-tools/intel-gpu-tools-docs.xml | 1 + lib/Makefile.am | 5 +- lib/Makefile.sources | 2 + lib/igt_aux.c | 260 ++++++++++++++++ lib/igt_aux.h | 3 + lib/igt_gvt.c | 43 +-- lib/igt_kmod.c | 328 +++++++++++++++++++++ lib/igt_kmod.h | 39 +++ lib/igt_sysfs.c | 106 +++++++ lib/igt_sysfs.h | 3 + 11 files changed, 750 insertions(+), 42 deletions(-) create mode 100644 lib/igt_kmod.c create mode 100644 lib/igt_kmod.h diff --git a/configure.ac b/configure.ac index e181c83..cba312f 100644 --- a/configure.ac +++ b/configure.ac @@ -121,6 +121,8 @@ AC_SUBST(ASSEMBLER_WARN_CFLAGS) PKG_CHECK_MODULES(DRM, [libdrm]) PKG_CHECK_MODULES(PCIACCESS, [pciaccess >= 0.10]) +PKG_CHECK_MODULES(KMOD, [libkmod]) +PKG_CHECK_MODULES(PROCPS, [libprocps]) case "$target_cpu" in x86*|i?86) diff --git a/docs/reference/intel-gpu-tools/intel-gpu-tools-docs.xml b/docs/reference/intel-gpu-tools/intel-gpu-tools-docs.xml index c862f2a..39061a8 100644 --- a/docs/reference/intel-gpu-tools/intel-gpu-tools-docs.xml +++ b/docs/reference/intel-gpu-tools/intel-gpu-tools-docs.xml @@ -22,6 +22,7 @@ + diff --git a/lib/Makefile.am b/lib/Makefile.am index 4c0893d..5eec207 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -23,7 +23,8 @@ if !HAVE_LIBDRM_INTEL endif AM_CPPFLAGS = -I$(top_srcdir) -AM_CFLAGS = $(CWARNFLAGS) $(DRM_CFLAGS) $(PCIACCESS_CFLAGS) $(LIBUNWIND_CFLAGS) $(DEBUG_CFLAGS) \ +AM_CFLAGS = $(CWARNFLAGS) $(DRM_CFLAGS) $(PCIACCESS_CFLAGS) $(LIBUNWIND_CFLAGS) \ + $(DEBUG_CFLAGS) $(KMOD_CFLAGS) $(PROCPS_CFLAGS) \ -DIGT_SRCDIR=\""$(abs_top_srcdir)/tests"\" \ -DIGT_DATADIR=\""$(pkgdatadir)"\" \ -DIGT_LOG_DOMAIN=\""$(subst _,-,$*)"\" \ @@ -34,6 +35,8 @@ AM_CFLAGS += $(CAIRO_CFLAGS) libintel_tools_la_LIBADD = \ $(DRM_LIBS) \ $(PCIACCESS_LIBS) \ + $(PROCPS_LIBS) \ + $(KMOD_LIBS) \ $(CAIRO_LIBS) \ $(LIBUDEV_LIBS) \ $(LIBUNWIND_LIBS) \ diff --git a/lib/Makefile.sources b/lib/Makefile.sources index e8e277b..2205c86 100644 --- a/lib/Makefile.sources +++ b/lib/Makefile.sources @@ -77,6 +77,8 @@ lib_source_list = \ igt_pm.h \ uwildmat/uwildmat.h \ uwildmat/uwildmat.c \ + igt_kmod.c \ + igt_kmod.h \ $(NULL) .PHONY: version.h.tmp diff --git a/lib/igt_aux.c b/lib/igt_aux.c index 421f6d4..a4ac44b 100644 --- a/lib/igt_aux.c +++ b/lib/igt_aux.c @@ -51,6 +51,8 @@ #include #include +#include + #include "drmtest.h" #include "i915_drm.h" #include "intel_chipset.h" @@ -65,6 +67,10 @@ #include "igt_stats.h" #include "igt_sysfs.h" +#ifdef HAVE_LIBGEN_H +#include /* for dirname() */ +#endif + /** * SECTION:igt_aux * @short_description: Auxiliary libraries and support functions @@ -1193,6 +1199,260 @@ void igt_set_module_param_int(const char *name, int val) igt_set_module_param(name, str); } +/** + * igt_pkill: + * @sig: Signal to send + * @comm: Name of process in the form found in /proc/pid/comm (limited to 15 + * chars) + * + * Returns: 0 in case the process is not found running or the signal has been + * sent successfully or -errno otherwise. + * + * This function sends the signal @sig for a process found in process table + * with name @comm. + */ +int +igt_pkill(int sig, const char *comm) +{ + PROCTAB *proc; + proc_t *proc_info; + int err = 0; + + proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG); + igt_assert(proc != NULL); + + while ((proc_info = readproc(proc, NULL))) { + if (!strncasecmp(proc_info->cmd, comm, sizeof(proc_info->cmd))) { + + if (kill(proc_info->tid, sig) < 0) + err = -errno; + + freeproc(proc_info); + break; + } + freeproc(proc_info); + } + + closeproc(proc); + return err; +} + +struct pinfo { + pid_t pid; + const char *comm; + const char *fn; +}; + +static void +__igt_show_stat(struct pinfo *info) +{ + const char *comm, *fn; + const char *type = ""; + struct stat st; + + pid_t pid = info->pid; + igt_assert((comm = info->comm)); + igt_assert((fn = info->fn)); + + if (lstat(fn, &st) == -1) + return; + + igt_info("%20.20s ", comm); + igt_info("%10d ", pid); + + switch (st.st_mode & S_IFMT) { + case S_IFBLK: + type = "block"; + break; + case S_IFCHR: + type = "character"; + break; + case S_IFDIR: + type = "directory"; + break; + case S_IFIFO: + type = "FIFO/pipe"; + break; + case S_IFLNK: + type = "symlink"; + break; + case S_IFREG: + type = "file"; + break; + case S_IFSOCK: + type = "socket"; + break; + default: + type = "unknown?"; + break; + } + igt_info("%20.20s ", type); + + igt_info("%10ld%10ld ", (long) st.st_uid, (long) st.st_gid); + + igt_info("%15lld bytes ", (long long) st.st_size); + igt_info("%30.30s", fn); + igt_info("\n"); + +} + + +static void +igt_show_stat_header(void) +{ + igt_info("%20.20s%11.11s%21.21s%11.11s%10.10s%22.22s%31.31s\n", + "COMM", "PID", "Type", "UID", "GID", "Size", "Filename"); + +} + +static void +igt_show_stat(proc_t *info, int *state, const char *fn) +{ + if (!*state) + igt_show_stat_header(); + + __igt_show_stat(&(struct pinfo) {.pid = info->tid, + .comm = info->cmd, .fn = fn }); + (*state)++; +} + +static void +__igt_lsof_fds(proc_t *proc_info, int *state, char *proc_path, const char *dir) +{ + struct dirent *d; + struct stat st; + char path[PATH_MAX]; + char *fd_lnk; + + /* default fds or kernel threads */ + const char *default_fds[] = { "/dev/pts", "/dev/null" }; + + DIR *dp = opendir(proc_path); + igt_assert(dp); +again: + while ((d = readdir(dp))) { + char *copy_fd_lnk; + char *dirn; + + unsigned int i; + ssize_t read; + + if (*d->d_name == '.') + continue; + + memset(path, 0, sizeof(path)); + snprintf(path, sizeof(path), "%s/%s", proc_path, d->d_name); + + if (lstat(path, &st) == -1) + continue; + + fd_lnk = malloc(st.st_size + 1); + + igt_assert((read = readlink(path, fd_lnk, st.st_size + 1))); + fd_lnk[read] = '\0'; + + for (i = 0; i < ARRAY_SIZE(default_fds); ++i) { + if (!strncmp(default_fds[i], + fd_lnk, + strlen(default_fds[i]))) { + free(fd_lnk); + goto again; + } + } + + copy_fd_lnk = strdup(fd_lnk); + dirn = dirname(copy_fd_lnk); + + if (!strncmp(dir, dirn, strlen(dir))) + igt_show_stat(proc_info, state, fd_lnk); + + free(copy_fd_lnk); + free(fd_lnk); + } +} + + +/* + * This functions verifies, for each process running on the machine, if the + * current working directory or the fds matches the one supplied in dir. + */ +static void +__igt_lsof(const char *dir) +{ + PROCTAB *proc; + proc_t *proc_info; + + char path[PATH_MAX]; + char *name_lnk; + struct stat st; + int state = 0; + + proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG); + igt_assert(proc != NULL); + + while ((proc_info = readproc(proc, NULL))) { + ssize_t read; + + /* check current working directory */ + memset(path, 0, sizeof(path)); + snprintf(path, sizeof(path), "/proc/%d/cwd", proc_info->tid); + + if (stat(path, &st) == -1) + continue; + + name_lnk = malloc(st.st_size + 1); + + igt_assert((read = readlink(path, name_lnk, st.st_size + 1))); + name_lnk[read] = '\0'; + + if (!strncmp(dir, name_lnk, strlen(dir))) + igt_show_stat(proc_info, &state, name_lnk); + + /* check also fd, seems that lsof(8) doesn't look here */ + memset(path, 0, sizeof(path)); + snprintf(path, sizeof(path), "/proc/%d/fd", proc_info->tid); + + __igt_lsof_fds(proc_info, &state, path, dir); + + free(name_lnk); + freeproc(proc_info); + } + + closeproc(proc); +} + +/** + * igt_lsof: Lists information about files opened by processes. + * @dpath: Path to look under. A valid directory is required. + * + * This function mimics (a restrictive form of) lsof(8), but also shows + * information about opened fds. + */ +void +igt_lsof(const char *dpath) +{ + struct stat st; + size_t len = strlen(dpath); + char *sanitized; + + if (stat(dpath, &st) == -1) + return; + + if (!S_ISDIR(st.st_mode)) { + igt_info("%s not a directory!\n", dpath); + return; + } + + sanitized = strdup(dpath); + /* remove last '/' so matching is easier */ + if (len > 1 && dpath[len - 1] == '/') + sanitized[len - 1] = '\0'; + + __igt_lsof(sanitized); + + free(sanitized); +} + static struct igt_siglatency { timer_t timer; struct timespec target; diff --git a/lib/igt_aux.h b/lib/igt_aux.h index d30196b..4162527 100644 --- a/lib/igt_aux.h +++ b/lib/igt_aux.h @@ -264,4 +264,7 @@ double igt_stop_siglatency(struct igt_mean *result); void igt_set_module_param(const char *name, const char *val); void igt_set_module_param_int(const char *name, int val); +int igt_pkill(int sig, const char *comm); +void igt_lsof(const char *dpath); + #endif /* IGT_AUX_H */ diff --git a/lib/igt_gvt.c b/lib/igt_gvt.c index 0f332d1..8bbf9bd 100644 --- a/lib/igt_gvt.c +++ b/lib/igt_gvt.c @@ -23,6 +23,7 @@ #include "igt.h" #include "igt_gvt.h" +#include "igt_sysfs.h" #include #include @@ -46,49 +47,9 @@ static bool is_gvt_enabled(void) return enabled; } -static void unbind_fbcon(void) -{ - char buf[128]; - const char *path = "/sys/class/vtconsole"; - DIR *dir; - struct dirent *vtcon; - - dir = opendir(path); - if (!dir) - return; - - while ((vtcon = readdir(dir))) { - int fd, len; - - if (strncmp(vtcon->d_name, "vtcon", 5)) - continue; - - sprintf(buf, "%s/%s/name", path, vtcon->d_name); - fd = open(buf, O_RDONLY); - if (fd < 0) - continue; - - len = read(fd, buf, sizeof(buf) - 1); - close(fd); - if (len >= 0) - buf[len] = '\0'; - - if (strstr(buf, "frame buffer device")) { - sprintf(buf, "%s/%s/bind", path, vtcon->d_name); - fd = open(buf, O_WRONLY); - if (fd != -1) { - igt_ignore_warn(write(fd, "1\n", 2)); - close(fd); - } - break; - } - } - closedir(dir); -} - static void unload_i915(void) { - unbind_fbcon(); + kick_fbcon(false); /* pkill alsact */ igt_ignore_warn(system("/sbin/modprobe -s -r i915")); diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c new file mode 100644 index 0000000..afa1a11 --- /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_pkill(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; +} diff --git a/lib/igt_kmod.h b/lib/igt_kmod.h new file mode 100644 index 0000000..dcf8f67 --- /dev/null +++ b/lib/igt_kmod.h @@ -0,0 +1,39 @@ +/* + * 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. + */ + +#ifndef IGT_KMOD_H +#define IGT_KMOD_H + +#include + +bool igt_kmod_is_loaded(const char *mod_name); +void igt_kmod_list_loaded(void); + +int igt_kmod_load(const char *mod_name, const char *opts); +int igt_kmod_unload(const char *mod_name, unsigned int flags); + +int igt_i915_driver_load(const char *opts); +int igt_i915_driver_unload(void); + + +#endif /* IGT_KMOD_H */ diff --git a/lib/igt_sysfs.c b/lib/igt_sysfs.c index c19821d..570329d 100644 --- a/lib/igt_sysfs.c +++ b/lib/igt_sysfs.c @@ -34,7 +34,11 @@ #include #include #include +#include +#include +#include +#include "igt_core.h" #include "igt_sysfs.h" /** @@ -392,3 +396,105 @@ bool igt_sysfs_set_boolean(int dir, const char *attr, bool value) { return igt_sysfs_printf(dir, attr, "%d", value) == 1; } + +/** + * kick_fbcon: + * @enable: boolean value + * + * This functions enables/disables the text console running on top of the + * framebuffer device. + */ +void kick_fbcon(bool enable) +{ + char buf[128]; + const char *path = "/sys/class/vtconsole"; + DIR *dir; + struct dirent *vtcon; + + dir = opendir(path); + if (!dir) + return; + + while ((vtcon = readdir(dir))) { + int fd, len; + + if (strncmp(vtcon->d_name, "vtcon", 5)) + continue; + + sprintf(buf, "%s/%s/name", path, vtcon->d_name); + fd = open(buf, O_RDONLY); + if (fd < 0) + continue; + + len = read(fd, buf, sizeof(buf) - 1); + close(fd); + if (len >= 0) + buf[len] = '\0'; + + if (strstr(buf, "frame buffer device")) { + sprintf(buf, "%s/%s/bind", path, vtcon->d_name); + fd = open(buf, O_WRONLY); + if (fd != -1) { + if (enable) + igt_ignore_warn(write(fd, "1\n", 2)); + else + igt_ignore_warn(write(fd, "0\n", 2)); + close(fd); + } + break; + } + } + closedir(dir); +} + +/** + * kick_snd_hda_intel: + * + * This functions unbinds the snd_hda_intel driver so the module cand be + * unloaded. + * + */ +void kick_snd_hda_intel(void) +{ + DIR *dir; + struct dirent *snd_hda; + int fd; size_t len; + + const char *dpath = "/sys/bus/pci/drivers/snd_hda_intel"; + const char *path = "/sys/bus/pci/drivers/snd_hda_intel/unbind"; + const char *devid = "0000:"; + + fd = open(path, O_WRONLY); + if (fd < 0) { + return; + } + + dir = opendir(dpath); + if (!dir) + goto out; + + len = strlen(devid); + while ((snd_hda = readdir(dir))) { + struct stat st; + char fpath[PATH_MAX]; + + if (*snd_hda->d_name == '.') + continue; + + snprintf(fpath, sizeof(fpath), "%s/%s", dpath, snd_hda->d_name); + if (lstat(fpath, &st)) + continue; + + if (!S_ISLNK(st.st_mode)) + continue; + + if (!strncmp(devid, snd_hda->d_name, len)) { + igt_ignore_warn(write(fd, snd_hda->d_name, + strlen(snd_hda->d_name))); + } + } + + closedir(dir); +out: + close(fd); +} diff --git a/lib/igt_sysfs.h b/lib/igt_sysfs.h index 4820066..69b2e5e 100644 --- a/lib/igt_sysfs.h +++ b/lib/igt_sysfs.h @@ -43,4 +43,7 @@ bool igt_sysfs_set_u32(int dir, const char *attr, uint32_t value); bool igt_sysfs_get_boolean(int dir, const char *attr); bool igt_sysfs_set_boolean(int dir, const char *attr, bool value); +void kick_fbcon(bool enable); +void kick_snd_hda_intel(void); + #endif /* __IGT_SYSFS_H__ */