summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/igt_aux.c157
-rw-r--r--lib/igt_aux.h1
-rw-r--r--lib/igt_kmod.c53
-rw-r--r--lib/igt_kmod.h2
4 files changed, 193 insertions, 20 deletions
diff --git a/lib/igt_aux.c b/lib/igt_aux.c
index 3945bebe..efe91e6c 100644
--- a/lib/igt_aux.c
+++ b/lib/igt_aux.c
@@ -31,6 +31,7 @@
#endif
#include <stdio.h>
#include <fcntl.h>
+#include <pwd.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <string.h>
@@ -1462,6 +1463,162 @@ igt_lsof(const char *dpath)
free(sanitized);
}
+static void pulseaudio_unload_module(proc_t *proc_info)
+{
+ char xdg_dir[PATH_MAX];
+ const char *homedir;
+ struct passwd *pw;
+
+ igt_fork(child, 1) {
+ pw = getpwuid(proc_info->euid);
+ homedir = pw->pw_dir;
+ snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", proc_info->euid);
+
+ igt_info("Ask pulseaudio to stop using audio device\n");
+
+ setgid(proc_info->egid);
+ setuid(proc_info->euid);
+ clearenv();
+ setenv("HOME", homedir, 1);
+ setenv("XDG_RUNTIME_DIR",xdg_dir, 1);
+
+ system("for i in $(pacmd list-sources|grep module:|cut -d : -f 2); do pactl unload-module $i; done");
+ }
+ igt_waitchildren();
+}
+
+/**
+ * __igt_lsof_audio_and_kill_proc() - check if a given process is using an
+ * audio device. If so, stop or prevent them to use such devices.
+ *
+ * @proc_info: process struct, as returned by readproc()
+ * @proc_path: path of the process under procfs
+ *
+ * No processes can be using an audio device by the time it gets removed.
+ * This function checks if a process is using an audio device from /dev/snd.
+ * If so, it will check:
+ * - if the process is pulseaudio, it can't be killed, as systemd will
+ * respawn it. So, instead, send a request for it to stop bind the
+ * audio devices.
+ * If the check fails, it means that the process can simply be killed.
+ */
+static int
+__igt_lsof_audio_and_kill_proc(proc_t *proc_info, char *proc_path)
+{
+ const char *audio_dev = "/dev/snd/";
+ char path[PATH_MAX * 2];
+ struct dirent *d;
+ struct stat st;
+ char *fd_lnk;
+ int fail = 0;
+ ssize_t read;
+
+ DIR *dp = opendir(proc_path);
+ igt_assert(dp);
+
+ while ((d = readdir(dp))) {
+ 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';
+
+ if (strncmp(audio_dev, fd_lnk, strlen(audio_dev))) {
+ free(fd_lnk);
+ continue;
+ }
+
+ free(fd_lnk);
+
+ /*
+ * In order to avoid racing against pa/systemd, ensure that
+ * pulseaudio will close all audio files. This should be
+ * enough to unbind audio modules and won't cause race issues
+ * with systemd trying to reload it.
+ */
+ if (!strcmp(proc_info->cmd, "pulseaudio")) {
+ pulseaudio_unload_module(proc_info);
+ break;
+ }
+
+ /*
+ * FIXME: terminating pipewire-pulse is not that easy, as
+ * pipewire there's no standard way up to pipewire version
+ * 0.3.49. Just trying to kill pipewire will start a race
+ * between IGT and systemd. If IGT wins, the audio driver
+ * will be unloaded before systemd tries to reload it, but
+ * if systemd wins, the audio device will be re-opened and
+ * used before IGT has a chance to remove the audio driver.
+ * Pipewire version 0.3.50 should bring a standard way:
+ *
+ * 1) start a thread running:
+ * pw-reserve -n Audio0 -r
+ * 2) unload/unbind the the audio driver(s);
+ * 3) stop the pw-reserve thread.
+ *
+ * We should add support for it once distros start shipping it.
+ */
+
+ /* For all other processes, just kill them */
+ igt_info("process %d (%s) is using audio device. Should be terminated.\n",
+ proc_info->tid, proc_info->cmd);
+
+ if (kill(proc_info->tid, SIGTERM) < 0) {
+ igt_info("Fail to terminate %s (pid: %d) with SIGTERM\n",
+ proc_info->cmd, proc_info->tid);
+ if (kill(proc_info->tid, SIGABRT) < 0) {
+ fail++;
+ igt_info("Fail to terminate %s (pid: %d) with SIGABRT\n",
+ proc_info->cmd, proc_info->tid);
+ }
+ }
+
+ break;
+ }
+
+ closedir(dp);
+ return fail;
+}
+
+/*
+ * This function identifies each process running on the machine that is
+ * opening an audio device and tries to stop it.
+ *
+ * Special care should be taken with pipewire and pipewire-pulse, as those
+ * daemons are respanned if they got killed.
+ */
+int
+igt_lsof_kill_audio_processes(void)
+{
+ char path[PATH_MAX];
+ proc_t *proc_info;
+ PROCTAB *proc;
+ int fail = 0;
+
+ proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
+ igt_assert(proc != NULL);
+
+ while ((proc_info = readproc(proc, NULL))) {
+ if (snprintf(path, sizeof(path), "/proc/%d/fd", proc_info->tid) < 1)
+ fail++;
+ else
+ fail += __igt_lsof_audio_and_kill_proc(proc_info, path);
+
+ freeproc(proc_info);
+ }
+ closeproc(proc);
+
+ return fail;
+}
+
static struct igt_siglatency {
timer_t timer;
struct timespec target;
diff --git a/lib/igt_aux.h b/lib/igt_aux.h
index 2f7efd9c..fad1039a 100644
--- a/lib/igt_aux.h
+++ b/lib/igt_aux.h
@@ -325,6 +325,7 @@ bool igt_allow_unlimited_files(void);
int igt_is_process_running(const char *comm);
int igt_terminate_process(int sig, const char *comm);
void igt_lsof(const char *dpath);
+int igt_lsof_kill_audio_processes(void);
#define igt_hweight(x) \
__builtin_choose_expr(sizeof(x) == 8, \
diff --git a/lib/igt_kmod.c b/lib/igt_kmod.c
index f252535d..133d1904 100644
--- a/lib/igt_kmod.c
+++ b/lib/igt_kmod.c
@@ -389,7 +389,7 @@ igt_i915_driver_load(const char *opts)
return 0;
}
-int __igt_i915_driver_unload(const char **who)
+int igt_audio_driver_unload(const char **who)
{
int ret;
const char *sound[] = {
@@ -398,6 +398,27 @@ int __igt_i915_driver_unload(const char **who)
NULL,
};
+ for (const char **m = sound; *m; m++) {
+ if (igt_kmod_is_loaded(*m)) {
+ if (igt_lsof_kill_audio_processes())
+ return EACCES;
+
+ kick_snd_hda_intel();
+ ret = igt_kmod_unload(*m, 0);
+ if (ret) {
+ if (who)
+ *who = *m;
+ return ret;
+ }
+ }
+ }
+ return 0;
+}
+
+int __igt_i915_driver_unload(const char **who)
+{
+ int ret;
+
const char *aux[] = {
/* gen5: ips uses symbol_get() so only a soft module dependency */
"intel_ips",
@@ -411,27 +432,19 @@ int __igt_i915_driver_unload(const char **who)
/* unbind vt */
bind_fbcon(false);
- for (const char **m = sound; *m; m++) {
- if (igt_kmod_is_loaded(*m)) {
- igt_terminate_process(SIGTERM, "alsactl");
- kick_snd_hda_intel();
- ret = igt_kmod_unload(*m, 0);
- if (ret) {
- if (who)
- *who = *m;
- return ret;
- }
- }
- }
+ ret = igt_audio_driver_unload(who);
+ if (ret)
+ return ret;
for (const char **m = aux; *m; m++) {
- if (igt_kmod_is_loaded(*m)) {
- ret = igt_kmod_unload(*m, 0);
- if (ret) {
- if (who)
- *who = *m;
- return ret;
- }
+ if (!igt_kmod_is_loaded(*m))
+ continue;
+
+ ret = igt_kmod_unload(*m, 0);
+ if (ret) {
+ if (who)
+ *who = *m;
+ return ret;
}
}
diff --git a/lib/igt_kmod.h b/lib/igt_kmod.h
index 0898122b..15f0be31 100644
--- a/lib/igt_kmod.h
+++ b/lib/igt_kmod.h
@@ -36,6 +36,8 @@ bool igt_kmod_has_param(const char *mod_name, const char *param);
int igt_kmod_load(const char *mod_name, const char *opts);
int igt_kmod_unload(const char *mod_name, unsigned int flags);
+int igt_audio_driver_unload(const char **whom);
+
int igt_i915_driver_load(const char *opts);
int igt_i915_driver_unload(void);
int __igt_i915_driver_unload(const char **whom);