diff mbox

[i-g-t,1/4,v7] lib/igt_kmod: New library to support driver loading/unloading and additional helpers.

Message ID 20161201122358.1124-2-marius.c.vlad@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Marius Vlad Dec. 1, 2016, 12:23 p.m. UTC
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 <marius.c.vlad@intel.com>
---
 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 mbox

Patch

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 @@ 
     <xi:include href="xml/igt_sysfs.xml"/>
     <xi:include href="xml/igt_draw.xml"/>
     <xi:include href="xml/igt_kms.xml"/>
+    <xi:include href="xml/igt_kmod.xml"/>
     <xi:include href="xml/igt_fb.xml"/>
     <xi:include href="xml/igt_aux.xml"/>
     <xi:include href="xml/igt_gt.xml"/>
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 <termios.h>
 #include <assert.h>
 
+#include <proc/readproc.h>
+
 #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 <libgen.h>   /* 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 <dirent.h>
 #include <unistd.h>
@@ -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 <signal.h>
+
+/**
+ * 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 <libkmod.h>
+
+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 <fcntl.h>
 #include <unistd.h>
 #include <i915_drm.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <fcntl.h>
 
+#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__ */