All of lore.kernel.org
 help / color / mirror / Atom feed
* [igt-dev] [PATCH i-g-t] Use the new procps library libproc2
@ 2022-11-17  8:41 Petri Latvala
  2022-11-17  8:46 ` Petri Latvala
                   ` (3 more replies)
  0 siblings, 4 replies; 11+ messages in thread
From: Petri Latvala @ 2022-11-17  8:41 UTC (permalink / raw)
  To: igt-dev; +Cc: Craig Small

From: Craig Small <csmall@dropbear.xyz>

---
 lib/igt_aux.c        |  246 +++++-
 lib/igt_aux.c.orig   | 1921 ++++++++++++++++++++++++++++++++++++++++++
 lib/meson.build      |    7 +-
 lib/meson.build.orig |  358 ++++++++
 meson.build          |   10 +-
 5 files changed, 2500 insertions(+), 42 deletions(-)
 create mode 100644 lib/igt_aux.c.orig
 create mode 100644 lib/meson.build.orig

diff --git a/lib/igt_aux.c b/lib/igt_aux.c
index 15e30440..2aee1275 100644
--- a/lib/igt_aux.c
+++ b/lib/igt_aux.c
@@ -52,8 +52,16 @@
 #include <assert.h>
 #include <grp.h>
 
+#ifdef HAVE_LIBPROCPS
 #include <proc/readproc.h>
+#endif
+#ifdef HAVE_LIBPROC2
+#include <libproc2/pids.h>
+#endif
+
 #include <libudev.h>
+#include <linux/limits.h>
+#include <dirent.h>
 
 #include "drmtest.h"
 #include "i915_drm.h"
@@ -1217,6 +1225,7 @@ void igt_unlock_mem(void)
  */
 int igt_is_process_running(const char *comm)
 {
+#if HAVE_LIBPROCPS
 	PROCTAB *proc;
 	proc_t *proc_info;
 	bool found = false;
@@ -1235,6 +1244,26 @@ int igt_is_process_running(const char *comm)
 
 	closeproc(proc);
 	return found;
+#endif
+#if HAVE_LIBPROC2
+	enum pids_item Item[] = { PIDS_CMD };
+	struct pids_info *info = NULL;
+	struct pids_stack *stack;
+	char *pid_comm;
+	bool found = false;
+
+	if (procps_pids_new(&info, Item, 1) < 0)
+	    return false;
+	while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
+	    pid_comm = PIDS_VAL(0, str, stack, info);
+	    if (!strncasecmp(pid_comm, comm, strlen(pid_comm))) {
+		found = true;
+		break;
+	    }
+	}
+	procps_pids_unref(&info);
+	return found;
+#endif
 }
 
 /**
@@ -1251,6 +1280,7 @@ int igt_is_process_running(const char *comm)
  */
 int igt_terminate_process(int sig, const char *comm)
 {
+#if HAVE_LIBPROCPS
 	PROCTAB *proc;
 	proc_t *proc_info;
 	int err = 0;
@@ -1272,6 +1302,29 @@ int igt_terminate_process(int sig, const char *comm)
 
 	closeproc(proc);
 	return err;
+#endif
+#if HAVE_LIBPROC2
+	enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD };
+	struct pids_info *info = NULL;
+	struct pids_stack *stack;
+	char *pid_comm;
+	int pid;
+	int err = 0;
+
+	if (procps_pids_new(&info, Items, 2) < 0)
+		return -errno;
+	while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
+		pid = PIDS_VAL(0, s_int, stack, info);
+		pid_comm = PIDS_VAL(1, str, stack, info);
+		if (!strncasecmp(pid_comm, comm, strlen(pid_comm))) {
+			if (kill(pid, sig) < 0)
+				err = -errno;
+			break;
+		}
+	}
+	procps_pids_unref(&info);
+	return err;
+#endif
 }
 
 struct pinfo {
@@ -1341,9 +1394,9 @@ igt_show_stat_header(void)
 }
 
 static void
-igt_show_stat(proc_t *info, int *state, const char *fn)
+igt_show_stat(const pid_t tid, const char *cmd, int *state, const char *fn)
 {
-	struct pinfo p = { .pid = info->tid, .comm = info->cmd, .fn = fn };
+	struct pinfo p = { .pid = tid, .comm = cmd, .fn = fn };
 
 	if (!*state)
 		igt_show_stat_header();
@@ -1353,7 +1406,7 @@ igt_show_stat(proc_t *info, int *state, const char *fn)
 }
 
 static void
-__igt_lsof_fds(proc_t *proc_info, int *state, char *proc_path, const char *dir)
+__igt_lsof_fds(const pid_t tid, const char *cmd, int *state, char *proc_path, const char *dir)
 {
 	struct dirent *d;
 	struct stat st;
@@ -1400,7 +1453,7 @@ again:
 		dirn = dirname(copy_fd_lnk);
 
 		if (!strncmp(dir, dirn, strlen(dir)))
-			igt_show_stat(proc_info, state, fd_lnk);
+			igt_show_stat(tid, cmd, state, fd_lnk);
 
 		free(copy_fd_lnk);
 		free(fd_lnk);
@@ -1416,13 +1469,14 @@ again:
 static void
 __igt_lsof(const char *dir)
 {
-	PROCTAB *proc;
-	proc_t *proc_info;
-
 	char path[30];
 	char *name_lnk;
 	struct stat st;
 	int state = 0;
+#ifdef HAVE_LIBPROCPS
+	PROCTAB *proc;
+	proc_t *proc_info;
+
 
 	proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
 	igt_assert(proc != NULL);
@@ -1443,19 +1497,57 @@ __igt_lsof(const char *dir)
 		name_lnk[read] = '\0';
 
 		if (!strncmp(dir, name_lnk, strlen(dir)))
-			igt_show_stat(proc_info, &state, name_lnk);
+			igt_show_stat(proc_info->tid, proc_info->cmd, &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);
+		__igt_lsof_fds(proc_info->tid, proc_info->cmd, &state, path, dir);
 
 		free(name_lnk);
 		freeproc(proc_info);
 	}
 
 	closeproc(proc);
+#endif
+#ifdef HAVE_LIBPROC2
+	enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD };
+	struct pids_info *info = NULL;
+	struct pids_stack *stack;
+
+	if (procps_pids_new(&info, Items, 2) < 0)
+		return;
+	while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
+		ssize_t read;
+		int tid = PIDS_VAL(0, s_int, stack, info);
+		char *pid_comm = PIDS_VAL(1, str, stack, info);
+
+		/* check current working directory */
+		memset(path, 0, sizeof(path));
+		snprintf(path, sizeof(path), "/proc/%d/cwd", 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(tid, pid_comm, &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", tid);
+
+		__igt_lsof_fds(tid, pid_comm, &state, path, dir);
+
+		free(name_lnk);
+	}
+	procps_pids_unref(&info);
+#endif
 }
 
 /**
@@ -1490,7 +1582,7 @@ igt_lsof(const char *dpath)
 	free(sanitized);
 }
 
-static void pulseaudio_unload_module(proc_t *proc_info)
+static void pulseaudio_unload_module(const uid_t euid, const gid_t egid)
 {
 	struct igt_helper_process pa_proc = {};
 	char xdg_dir[PATH_MAX];
@@ -1498,14 +1590,14 @@ static void pulseaudio_unload_module(proc_t *proc_info)
 	struct passwd *pw;
 
 	igt_fork_helper(&pa_proc) {
-		pw = getpwuid(proc_info->euid);
+		pw = getpwuid(euid);
 		homedir = pw->pw_dir;
-		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", proc_info->euid);
+		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", euid);
 
 		igt_info("Request pulseaudio to stop using audio device\n");
 
-		setgid(proc_info->egid);
-		setuid(proc_info->euid);
+		setgid(egid);
+		setuid(euid);
 		clearenv();
 		setenv("HOME", homedir, 1);
 		setenv("XDG_RUNTIME_DIR",xdg_dir, 1);
@@ -1524,10 +1616,13 @@ static void pipewire_reserve_wait(void)
 	char xdg_dir[PATH_MAX];
 	const char *homedir;
 	struct passwd *pw;
-	proc_t *proc_info;
-	PROCTAB *proc;
+	int tid=0, euid, egid;
 
+#ifdef HAVE_LIBPROCPS
 	igt_fork_helper(&pw_reserve_proc) {
+		proc_t *proc_info;
+		PROCTAB *proc;
+
 		igt_info("Preventing pipewire-pulse to use the audio drivers\n");
 
 		proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
@@ -1540,19 +1635,44 @@ static void pipewire_reserve_wait(void)
 		}
 		closeproc(proc);
 
+		tid = proc_info->tid;
+		euid = proc_info->euid;
+		egid = proc_info->egid;
+		freeproc(proc_info);
+#endif
+#ifdef HAVE_LIBPROC2
+	igt_fork(child, 1) {
+		enum pids_item Items[] = { PIDS_ID_PID, PIDS_ID_EUID, PIDS_ID_EGID };
+		enum rel_items { EU_PID, EU_EUID, EU_EGID };
+		struct pids_info *info = NULL;
+		struct pids_stack *stack;
+
+		igt_info("Preventing pipewire-pulse to use the audio drivers\n");
+
+		if (procps_pids_new(&info, Items, 3) < 0)
+		    return;
+		while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
+			tid = PIDS_VAL(EU_PID, s_int, stack, info);
+			if (pipewire_pulse_pid == tid)
+				break;
+		}
+		euid = PIDS_VAL(EU_EUID, s_int, stack, info);
+		egid = PIDS_VAL(EU_EGID, s_int, stack, info);
+		procps_pids_unref(&info);
+#endif
+
 		/* Sanity check: if it can't find the process, it means it has gone */
-		if (pipewire_pulse_pid != proc_info->tid)
+		if (pipewire_pulse_pid != tid)
 			exit(0);
 
-		pw = getpwuid(proc_info->euid);
+		pw = getpwuid(euid);
 		homedir = pw->pw_dir;
-		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", proc_info->euid);
-		setgid(proc_info->egid);
-		setuid(proc_info->euid);
+		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", euid);
+		setgid(egid);
+		setuid(euid);
 		clearenv();
 		setenv("HOME", homedir, 1);
 		setenv("XDG_RUNTIME_DIR",xdg_dir, 1);
-		freeproc(proc_info);
 
 		/*
 		 * pw-reserve will run in background. It will only exit when
@@ -1570,9 +1690,7 @@ static void pipewire_reserve_wait(void)
 int pipewire_pulse_start_reserve(void)
 {
 	bool is_pw_reserve_running = false;
-	proc_t *proc_info;
 	int attempts = 0;
-	PROCTAB *proc;
 
 	if (!pipewire_pulse_pid)
 		return 0;
@@ -1584,6 +1702,10 @@ int pipewire_pulse_start_reserve(void)
 	 * pipewire version 0.3.50 or upper.
 	 */
 	for (attempts = 0; attempts < PIPEWIRE_RESERVE_MAX_TIME; attempts++) {
+#ifdef HAVE_LIBPROCPS
+		PROCTAB *proc;
+		proc_t *proc_info;
+
 		usleep(1000);
 		proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
 		igt_assert(proc != NULL);
@@ -1598,6 +1720,25 @@ int pipewire_pulse_start_reserve(void)
 			freeproc(proc_info);
 		}
 		closeproc(proc);
+#endif
+#ifdef HAVE_LIBPROC2
+		enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD };
+		struct pids_info *info = NULL;
+		struct pids_stack *stack;
+
+		usleep(1000);
+
+		if (procps_pids_new(&info, Items, 2) < 0)
+			return 1;
+		while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
+			if (!strcmp(PIDS_VAL(1, str, stack, info), "pw-reserve")) {
+				is_pw_reserve_running = true;
+				pipewire_pw_reserve_pid = PIDS_VAL(0, s_int, stack, info);
+				break;
+			}
+		}
+		procps_pids_unref(&info);
+#endif
 		if (is_pw_reserve_running)
 			break;
 	}
@@ -1645,7 +1786,7 @@ void pipewire_pulse_stop_reserve(void)
  * 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)
+__igt_lsof_audio_and_kill_proc(const pid_t tid, const char *cmd, const uid_t euid, const gid_t egid, char *proc_path)
 {
 	const char *audio_dev = "/dev/snd/";
 	char path[PATH_MAX * 2];
@@ -1670,10 +1811,10 @@ __igt_lsof_audio_and_kill_proc(proc_t *proc_info, char *proc_path)
 	 * 2) unload/unbind the the audio driver(s);
 	 * 3) stop the pw-reserve thread.
 	 */
-	if (!strcmp(proc_info->cmd, "pipewire-pulse")) {
+	if (!strcmp(cmd, "pipewire-pulse")) {
 		igt_info("process %d (%s) is using audio device. Should be requested to stop using them.\n",
-			 proc_info->tid, proc_info->cmd);
-		pipewire_pulse_pid = proc_info->tid;
+			 tid, cmd);
+		pipewire_pulse_pid = tid;
 		return 0;
 	}
 	/*
@@ -1685,9 +1826,9 @@ __igt_lsof_audio_and_kill_proc(proc_t *proc_info, char *proc_path)
 	 * will respawn them. So, just ignore here, they'll honor pw-reserve,
 	 * when the time comes.
 	 */
-	if (!strcmp(proc_info->cmd, "pipewire-media-session"))
+	if (!strcmp(cmd, "pipewire-media-session"))
 		return 0;
-	if (!strcmp(proc_info->cmd, "wireplumber"))
+	if (!strcmp(cmd, "wireplumber"))
 		return 0;
 
 	dp = opendir(proc_path);
@@ -1723,22 +1864,22 @@ __igt_lsof_audio_and_kill_proc(proc_t *proc_info, char *proc_path)
 		 * 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);
+		if (!strcmp(cmd, "pulseaudio")) {
+			pulseaudio_unload_module(euid, egid);
 			break;
 		}
 
 		/* 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);
+				tid, cmd);
 
-		if (kill(proc_info->tid, SIGTERM) < 0) {
+		if (kill(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) {
+				cmd, tid);
+			if (kill(tid, SIGABRT) < 0) {
 				fail++;
 				igt_info("Fail to terminate %s (pid: %d) with SIGABRT\n",
-					proc_info->cmd, proc_info->tid);
+					cmd, tid);
 			}
 		}
 
@@ -1760,23 +1901,48 @@ int
 igt_lsof_kill_audio_processes(void)
 {
 	char path[PATH_MAX];
+	int fail = 0;
+
+#ifdef HAVE_LIBPROCPS
 	proc_t *proc_info;
 	PROCTAB *proc;
-	int fail = 0;
 
 	proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
 	igt_assert(proc != NULL);
 	pipewire_pulse_pid = 0;
-
 	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);
+			fail += __igt_lsof_audio_and_kill_proc(proc_info->tid, proc_info->cmd, proc_info->euid, proc_info->egid, path);
 
 		freeproc(proc_info);
 	}
 	closeproc(proc);
+#endif
+#ifdef HAVE_LIBPROC2
+	enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD, PIDS_ID_EUID, PIDS_ID_EGID };
+	enum rel_items { EU_PID, EU_CMD, EU_EUID, EU_EGID };
+	struct pids_info *info = NULL;
+	struct pids_stack *stack;
+	pid_t tid;
+
+	if (procps_pids_new(&info, Items, 4) < 0)
+		return 1;
+	while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
+		tid = PIDS_VAL(EU_PID, s_int, stack, info);
+
+		if (snprintf(path, sizeof(path), "/proc/%d/fd", tid) < 1)
+			fail++;
+		else
+			fail += __igt_lsof_audio_and_kill_proc(tid,
+				PIDS_VAL(EU_CMD, str, stack, info),
+				PIDS_VAL(EU_EUID, s_int, stack, info),
+				PIDS_VAL(EU_EGID, s_int, stack, info),
+				path);
+	}
+	procps_pids_unref(&info);
+#endif
 
 	return fail;
 }
diff --git a/lib/igt_aux.c.orig b/lib/igt_aux.c.orig
new file mode 100644
index 00000000..15e30440
--- /dev/null
+++ b/lib/igt_aux.c.orig
@@ -0,0 +1,1921 @@
+/*
+ * Copyright © 2007, 2011, 2013, 2014, 2015 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.
+ *
+ * Authors:
+ *    Eric Anholt <eric@anholt.net>
+ *    Daniel Vetter <daniel.vetter@ffwll.ch>
+ *
+ */
+
+#ifdef HAVE_LIBGEN_H
+#include <libgen.h>
+#endif
+#include <stdio.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <signal.h>
+#include <pciaccess.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/poll.h>
+#include <sys/wait.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <sys/utsname.h>
+#include <termios.h>
+#include <assert.h>
+#include <grp.h>
+
+#include <proc/readproc.h>
+#include <libudev.h>
+
+#include "drmtest.h"
+#include "i915_drm.h"
+#include "intel_chipset.h"
+#include "igt_aux.h"
+#include "igt_debugfs.h"
+#include "igt_gt.h"
+#include "igt_params.h"
+#include "igt_rand.h"
+#include "igt_sysfs.h"
+#include "config.h"
+#include "intel_reg.h"
+#include "ioctl_wrappers.h"
+#include "igt_kms.h"
+#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
+ * @title: aux
+ * @include: igt.h
+ *
+ * This library provides various auxiliary helper functions that don't really
+ * fit into any other topic.
+ */
+
+static struct __igt_sigiter_global {
+	pid_t tid;
+	timer_t timer;
+	struct timespec offset;
+	struct {
+		long hit, miss;
+		long ioctls, signals;
+	} stat;
+} __igt_sigiter;
+
+static void sigiter(int sig, siginfo_t *info, void *arg)
+{
+	__igt_sigiter.stat.signals++;
+}
+
+#if 0
+#define SIG_ASSERT(expr) igt_assert(expr)
+#else
+#define SIG_ASSERT(expr)
+#endif
+
+static int
+sig_ioctl(int fd, unsigned long request, void *arg)
+{
+	struct itimerspec its;
+	int ret;
+
+	SIG_ASSERT(__igt_sigiter.timer);
+	SIG_ASSERT(__igt_sigiter.tid == gettid());
+
+	memset(&its, 0, sizeof(its));
+	if (timer_settime(__igt_sigiter.timer, 0, &its, NULL)) {
+		/* oops, we didn't undo the interrupter (i.e. !unwound abort) */
+		igt_ioctl = drmIoctl;
+		return drmIoctl(fd, request, arg);
+	}
+
+	its.it_value = __igt_sigiter.offset;
+	do {
+		long serial;
+
+		__igt_sigiter.stat.ioctls++;
+
+		ret = 0;
+		serial = __igt_sigiter.stat.signals;
+		igt_assert(timer_settime(__igt_sigiter.timer, 0, &its, NULL) == 0);
+		if (ioctl(fd, request, arg))
+			ret = errno;
+		if (__igt_sigiter.stat.signals == serial)
+			__igt_sigiter.stat.miss++;
+		if (ret == 0)
+			break;
+
+		if (ret == EINTR) {
+			__igt_sigiter.stat.hit++;
+
+			its.it_value.tv_sec *= 2;
+			its.it_value.tv_nsec *= 2;
+			while (its.it_value.tv_nsec >= NSEC_PER_SEC) {
+				its.it_value.tv_nsec -= NSEC_PER_SEC;
+				its.it_value.tv_sec += 1;
+			}
+
+			SIG_ASSERT(its.it_value.tv_nsec >= 0);
+			SIG_ASSERT(its.it_value.tv_sec >= 0);
+		}
+	} while (ret == EAGAIN || ret == EINTR);
+
+	memset(&its, 0, sizeof(its));
+	timer_settime(__igt_sigiter.timer, 0, &its, NULL);
+
+	errno = ret;
+	return ret ? -1 : 0;
+}
+
+static bool igt_sigiter_start(struct __igt_sigiter *iter, bool enable)
+{
+	/* Note that until we can automatically clean up on failed/skipped
+	 * tests, we cannot assume the state of the igt_ioctl indirection.
+	 */
+	SIG_ASSERT(igt_ioctl == drmIoctl);
+	igt_ioctl = drmIoctl;
+
+	if (enable) {
+		struct timespec start, end;
+		struct sigevent sev;
+		struct sigaction act;
+		struct itimerspec its;
+
+		igt_ioctl = sig_ioctl;
+		__igt_sigiter.tid = gettid();
+
+		memset(&sev, 0, sizeof(sev));
+		sev.sigev_notify = SIGEV_SIGNAL | SIGEV_THREAD_ID;
+		sev.sigev_notify_thread_id = __igt_sigiter.tid;
+		sev.sigev_signo = SIGRTMIN;
+		igt_assert(timer_create(CLOCK_MONOTONIC, &sev, &__igt_sigiter.timer) == 0);
+
+		memset(&its, 0, sizeof(its));
+		igt_assert(timer_settime(__igt_sigiter.timer, 0, &its, NULL) == 0);
+
+		memset(&act, 0, sizeof(act));
+		act.sa_sigaction = sigiter;
+		act.sa_flags = SA_SIGINFO;
+		igt_assert(sigaction(SIGRTMIN, &act, NULL) == 0);
+
+		/* Try to find the approximate delay required to skip over
+		 * the timer_setttime and into the following ioctl() to try
+		 * and avoid the timer firing before we enter the drmIoctl.
+		 */
+		igt_assert(clock_gettime(CLOCK_MONOTONIC, &start) == 0);
+		igt_assert(timer_settime(__igt_sigiter.timer, 0, &its, NULL) == 0);
+		igt_assert(clock_gettime(CLOCK_MONOTONIC, &end) == 0);
+
+		__igt_sigiter.offset.tv_sec = end.tv_sec - start.tv_sec;
+		__igt_sigiter.offset.tv_nsec = end.tv_nsec - start.tv_nsec;
+		if (__igt_sigiter.offset.tv_nsec < 0) {
+			__igt_sigiter.offset.tv_nsec += NSEC_PER_SEC;
+			__igt_sigiter.offset.tv_sec -= 1;
+		}
+		if (__igt_sigiter.offset.tv_sec < 0) {
+			__igt_sigiter.offset.tv_nsec = 0;
+			__igt_sigiter.offset.tv_sec = 0;
+		}
+		igt_assert(__igt_sigiter.offset.tv_sec == 0);
+
+		igt_debug("Initial delay for interruption: %ld.%09lds\n",
+			  __igt_sigiter.offset.tv_sec,
+			  __igt_sigiter.offset.tv_nsec);
+	}
+
+	return true;
+}
+
+static bool igt_sigiter_stop(struct __igt_sigiter *iter, bool enable)
+{
+	if (enable) {
+		struct sigaction act;
+
+		SIG_ASSERT(igt_ioctl == sig_ioctl);
+		SIG_ASSERT(__igt_sigiter.tid == gettid());
+		igt_ioctl = drmIoctl;
+
+		timer_delete(__igt_sigiter.timer);
+
+		memset(&act, 0, sizeof(act));
+		act.sa_handler = SIG_IGN;
+		sigaction(SIGRTMIN, &act, NULL);
+
+		memset(&__igt_sigiter, 0, sizeof(__igt_sigiter));
+	}
+
+	memset(iter, 0, sizeof(*iter));
+	return false;
+}
+
+bool __igt_sigiter_continue(struct __igt_sigiter *iter, bool enable)
+{
+	if (iter->pass++ == 0)
+		return igt_sigiter_start(iter, enable);
+
+	/* If nothing reported SIGINT, nothing will on the next pass, so
+	 * give up! Also give up if everything is now executing faster
+	 * than current sigtimer.
+	 */
+	if (__igt_sigiter.stat.hit == 0 ||
+	    __igt_sigiter.stat.miss == __igt_sigiter.stat.ioctls)
+		return igt_sigiter_stop(iter, enable);
+
+	igt_debug("%s: pass %d, missed %ld/%ld\n",
+		  __func__, iter->pass - 1,
+		  __igt_sigiter.stat.miss,
+		  __igt_sigiter.stat.ioctls);
+
+	SIG_ASSERT(igt_ioctl == sig_ioctl);
+	SIG_ASSERT(__igt_sigiter.timer);
+
+	__igt_sigiter.offset.tv_sec *= 2;
+	__igt_sigiter.offset.tv_nsec *= 2;
+	while (__igt_sigiter.offset.tv_nsec >= NSEC_PER_SEC) {
+		__igt_sigiter.offset.tv_nsec -= NSEC_PER_SEC;
+		__igt_sigiter.offset.tv_sec += 1;
+	}
+	SIG_ASSERT(__igt_sigiter.offset.tv_nsec >= 0);
+	SIG_ASSERT(__igt_sigiter.offset.tv_sec >= 0);
+
+	memset(&__igt_sigiter.stat, 0, sizeof(__igt_sigiter.stat));
+	return true;
+}
+
+static struct igt_helper_process signal_helper;
+long long int sig_stat;
+__noreturn static void signal_helper_process(pid_t pid)
+{
+	/* Interrupt the parent process at 500Hz, just to be annoying */
+	while (1) {
+		usleep(1000 * 1000 / 500);
+		if (kill(pid, SIGCONT)) /* Parent has died, so must we. */
+			exit(0);
+	}
+}
+
+static void sig_handler(int i)
+{
+	sig_stat++;
+}
+
+/**
+ * igt_fork_signal_helper:
+ *
+ * Fork a child process using #igt_fork_helper to interrupt the parent process
+ * with a SIGCONT signal at regular quick intervals. The corresponding dummy
+ * signal handler is installed in the parent process.
+ *
+ * This is useful to exercise ioctl error paths, at least where those can be
+ * exercises by interrupting blocking waits, like stalling for the gpu. This
+ * helper can also be used from children spawned with #igt_fork.
+ *
+ * In tests with subtests this function can be called outside of failure
+ * catching code blocks like #igt_fixture or #igt_subtest.
+ *
+ * Note that this just spews signals at the current process unconditionally and
+ * hence incurs quite a bit of overhead. For a more focused approach, with less
+ * overhead, look at the #igt_while_interruptible code block macro.
+ */
+void igt_fork_signal_helper(void)
+{
+	if (igt_only_list_subtests())
+		return;
+
+	/* We pick SIGCONT as it is a "safe" signal - if we send SIGCONT to
+	 * an unexpecting process it spuriously wakes up and does nothing.
+	 * Most other signals (e.g. SIGUSR1) cause the process to die if they
+	 * are not handled. This is an issue in case the sighandler is not
+	 * inherited correctly (or if there is a race in the inheritance
+	 * and we send the signal at exactly the wrong time).
+	 */
+	signal(SIGCONT, sig_handler);
+	setpgid(0, 0); /* define a new process group for the tests */
+
+	igt_fork_helper(&signal_helper) {
+		setpgid(0, 0); /* Escape from the test process group */
+
+		/* Pass along the test process group identifier,
+		 * negative pid => send signal to everyone in the group.
+		 */
+		signal_helper_process(-getppid());
+	}
+}
+
+/**
+ * igt_stop_signal_helper:
+ *
+ * Stops the child process spawned with igt_fork_signal_helper() again.
+ *
+ * In tests with subtests this function can be called outside of failure
+ * catching code blocks like #igt_fixture or #igt_subtest.
+ */
+void igt_stop_signal_helper(void)
+{
+	if (igt_only_list_subtests())
+		return;
+
+	igt_stop_helper(&signal_helper);
+
+	sig_stat = 0;
+}
+
+/**
+ * igt_suspend_signal_helper:
+ *
+ * Suspends the child process spawned with igt_fork_signal_helper(). This
+ * should be called before a critical section of code that has difficulty to
+ * make progress if interrupted frequently, like the clone() syscall called
+ * from a largish executable. igt_resume_signal_helper() must be called after
+ * the critical section to restart interruptions for the test.
+ */
+void igt_suspend_signal_helper(void)
+{
+	int status;
+
+	if (!signal_helper.running)
+		return;
+
+	kill(signal_helper.pid, SIGSTOP);
+	while (waitpid(signal_helper.pid, &status, WUNTRACED) == -1 &&
+	       errno == EINTR)
+		;
+}
+
+/**
+ * igt_resume_signal_helper:
+ *
+ * Resumes the child process spawned with igt_fork_signal_helper().
+ *
+ * This should be paired with igt_suspend_signal_helper() and called after the
+ * problematic code sensitive to signals.
+ */
+void igt_resume_signal_helper(void)
+{
+	if (!signal_helper.running)
+		return;
+
+	kill(signal_helper.pid, SIGCONT);
+}
+
+static struct igt_helper_process shrink_helper;
+__noreturn static void shrink_helper_process(int fd, pid_t pid)
+{
+	while (1) {
+		igt_drop_caches_set(fd, DROP_SHRINK_ALL);
+		usleep(1000 * 1000 / 50);
+		if (kill(pid, 0)) /* Parent has died, so must we. */
+			exit(0);
+	}
+}
+
+/**
+ * igt_fork_shrink_helper:
+ *
+ * Fork a child process using #igt_fork_helper to force all available objects
+ * to be paged out (via i915_gem_shrink()).
+ *
+ * This is useful to exercise swapping paths, without requiring us to hit swap.
+ *
+ * This should only be used from an igt_fixture.
+ */
+void igt_fork_shrink_helper(int drm_fd)
+{
+	assert(!igt_only_list_subtests());
+	igt_require(igt_drop_caches_has(drm_fd, DROP_SHRINK_ALL));
+	igt_fork_helper(&shrink_helper)
+		shrink_helper_process(drm_fd, getppid());
+}
+
+/**
+ * igt_stop_shrink_helper:
+ *
+ * Stops the child process spawned with igt_fork_shrink_helper().
+ */
+void igt_stop_shrink_helper(void)
+{
+	igt_stop_helper(&shrink_helper);
+}
+
+static void show_kernel_stack(pid_t pid)
+{
+	char buf[80], *str;
+	int dir;
+
+	snprintf(buf, sizeof(buf), "/proc/%d", pid);
+	dir = open(buf, O_RDONLY);
+	if (dir < 0)
+		return;
+
+	str = igt_sysfs_get(dir, "stack");
+	if (str) {
+		igt_debug("Kernel stack for pid %d:\n%s\n", pid, str);
+		free(str);
+	}
+
+	close(dir);
+}
+
+static struct igt_helper_process hang_detector;
+__noreturn static void
+hang_detector_process(int fd, pid_t pid, dev_t rdev)
+{
+	struct udev_monitor *mon =
+		udev_monitor_new_from_netlink(udev_new(), "kernel");
+	struct pollfd pfd;
+	int ret;
+
+	udev_monitor_filter_add_match_subsystem_devtype(mon, "drm", NULL);
+	udev_monitor_enable_receiving(mon);
+
+	pfd.fd = udev_monitor_get_fd(mon);
+	pfd.events = POLLIN;
+
+	while ((ret = poll(&pfd, 1, 2000)) >= 0) {
+		struct udev_device *dev;
+		dev_t devnum;
+
+		if (kill(pid, 0)) { /* Parent has died, so must we. */
+			igt_warn("Parent died without killing its children (%s)\n",
+				 __func__);
+			break;
+		}
+
+		dev = NULL;
+		if (ret > 0)
+			dev = udev_monitor_receive_device(mon);
+		if (dev == NULL)
+			continue;
+
+		devnum = udev_device_get_devnum(dev);
+		if (memcmp(&rdev, &devnum, sizeof(dev_t)) == 0) {
+			const char *str;
+
+			str = udev_device_get_property_value(dev, "ERROR");
+			if (str && atoi(str) == 1) {
+				show_kernel_stack(pid);
+				kill(pid, SIGIO);
+			}
+		}
+
+		udev_device_unref(dev);
+	}
+
+	exit(0);
+}
+
+__noreturn static void sig_abort(int sig)
+{
+	errno = 0; /* inside a signal, last errno reporting is confusing */
+	igt_assert(!"GPU hung");
+}
+
+void igt_fork_hang_detector(int fd)
+{
+	struct stat st;
+
+	igt_assert(fstat(fd, &st) == 0);
+
+	/*
+	 * Disable per-engine reset to force an error uevent. We don't
+	 * expect to get any hangs whilst the detector is enabled (if we do
+	 * they are a test failure!) and so the loss of per-engine reset
+	 * functionality is not an issue.
+	 */
+	igt_assert(igt_params_set(fd, "reset", "%d", 1 /* only global reset */));
+
+	signal(SIGIO, sig_abort);
+	igt_fork_helper(&hang_detector)
+		hang_detector_process(fd, getppid(), st.st_rdev);
+}
+
+void igt_stop_hang_detector(void)
+{
+	/*
+	 * Give the uevent time to arrive. No sleep at all misses about 20% of
+	 * hangs (at least, in the i915_hangman/detector test). A sleep of 1ms
+	 * seems to miss about 2%, 10ms loses <1%, so 100ms should be safe.
+	 */
+	usleep(100 * 1000);
+
+	igt_stop_helper(&hang_detector);
+}
+
+/**
+ * igt_check_boolean_env_var:
+ * @env_var: environment variable name
+ * @default_value: default value for the environment variable
+ *
+ * This function should be used to parse boolean environment variable options.
+ *
+ * Returns:
+ * The boolean value of the environment variable @env_var as decoded by atoi()
+ * if it is set and @default_value if the variable is not set.
+ */
+bool igt_check_boolean_env_var(const char *env_var, bool default_value)
+{
+	char *val;
+
+	val = getenv(env_var);
+	if (!val)
+		return default_value;
+
+	return atoi(val) != 0;
+}
+
+/**
+ * igt_aub_dump_enabled:
+ *
+ * Returns:
+ * True if AUB dumping is enabled with IGT_DUMP_AUB=1 in the environment, false
+ * otherwise.
+ */
+bool igt_aub_dump_enabled(void)
+{
+	static int dump_aub = -1;
+
+	if (dump_aub == -1)
+		dump_aub = igt_check_boolean_env_var("IGT_DUMP_AUB", false);
+
+	return dump_aub;
+}
+
+/* other helpers */
+/**
+ * igt_exchange_int:
+ * @array: pointer to the array of integers
+ * @i: first position
+ * @j: second position
+ *
+ * Exchanges the two values at array indices @i and @j. Useful as an exchange
+ * function for igt_permute_array().
+ */
+void igt_exchange_int(void *array, unsigned i, unsigned j)
+{
+	int *int_arr, tmp;
+	int_arr = array;
+
+	tmp = int_arr[i];
+	int_arr[i] = int_arr[j];
+	int_arr[j] = tmp;
+}
+
+/**
+ * igt_exchange_int64:
+ * @array: pointer to the array of int64_t
+ * @i: first position
+ * @j: second position
+ *
+ * Exchanges the two values at array indices @i and @j. Useful as an exchange
+ * function for igt_permute_array().
+ */
+void igt_exchange_int64(void *array, unsigned i, unsigned j)
+{
+	int64_t *a = array;
+
+	igt_swap(a[i], a[j]);
+}
+
+/**
+ * igt_permute_array:
+ * @array: pointer to array
+ * @size: size of the array
+ * @exchange_func: function to exchange array elements
+ *
+ * This function randomly permutes the array using random() as the PRNG source.
+ * The @exchange_func function is called to exchange two elements in the array
+ * when needed.
+ */
+void igt_permute_array(void *array, unsigned size,
+                       void (*exchange_func)(void *array,
+                                             unsigned i,
+                                             unsigned j))
+{
+	int i;
+
+	for (i = size - 1; i > 0; i--) {
+		/* yes, not perfectly uniform, who cares */
+		long l = hars_petruska_f54_1_random_unsafe() % (i +1);
+		if (i != l)
+			exchange_func(array, i, l);
+	}
+}
+
+__attribute__((format(printf, 1, 2)))
+static void igt_interactive_info(const char *format, ...)
+{
+	va_list args;
+
+	if (!isatty(STDERR_FILENO) || __igt_plain_output) {
+		errno = 0; /* otherwise would be either ENOTTY or EBADF */
+		return;
+	}
+
+	if (igt_log_level > IGT_LOG_INFO)
+		return;
+
+	va_start(args, format);
+	vfprintf(stderr, format, args);
+	va_end(args);
+}
+
+
+/**
+ * igt_progress:
+ * @header: header string to prepend to the progress indicator
+ * @i: work processed thus far
+ * @total: total amount of work
+ *
+ * This function draws a progress indicator, which is useful for running
+ * long-winded tests manually on the console. To avoid spamming log files in
+ * automated runs the progress indicator is suppressed when not running on a
+ * terminal.
+ */
+void igt_progress(const char *header, uint64_t i, uint64_t total)
+{
+	int divider = 200;
+
+	if (i+1 >= total) {
+		igt_interactive_info("\r%s100%%\n", header);
+		return;
+	}
+
+	if (total / 200 == 0)
+		divider = 1;
+
+	/* only bother updating about every 0.5% */
+	if (i % (total / divider) == 0)
+		igt_interactive_info("\r%s%3llu%%", header,
+				     (long long unsigned)i * 100 / total);
+}
+
+/**
+ * igt_print_activity:
+ *
+ * Print a '.' to indicate activity. This is printed without a newline and
+ * only if output is to a terminal.
+ */
+void igt_print_activity(void)
+{
+	igt_interactive_info(".");
+}
+
+static int autoresume_delay;
+
+static const char *suspend_state_name[] = {
+	[SUSPEND_STATE_FREEZE] = "freeze",
+	[SUSPEND_STATE_STANDBY] = "standby",
+	[SUSPEND_STATE_S3] = "mem", /* Forces Suspend-to-Ram (S3) */
+	[SUSPEND_STATE_MEM] = "mem", /* Respects system default */
+	[SUSPEND_STATE_DISK] = "disk",
+};
+
+static const char *suspend_test_name[] = {
+	[SUSPEND_TEST_NONE] = "none",
+	[SUSPEND_TEST_FREEZER] = "freezer",
+	[SUSPEND_TEST_DEVICES] = "devices",
+	[SUSPEND_TEST_PLATFORM] = "platform",
+	[SUSPEND_TEST_PROCESSORS] = "processors",
+	[SUSPEND_TEST_CORE] = "core",
+};
+
+static const char *mem_sleep_name[] = {
+	[MEM_SLEEP_S2IDLE] = "s2idle",
+	[MEM_SLEEP_SHALLOW] = "shallow",
+	[MEM_SLEEP_DEEP] = "deep"
+};
+
+static enum igt_suspend_test get_suspend_test(int power_dir)
+{
+	char *test_line;
+	char *test_name;
+	enum igt_suspend_test test;
+
+	if (faccessat(power_dir, "pm_test", R_OK, 0))
+		return SUSPEND_TEST_NONE;
+
+	igt_assert((test_line = igt_sysfs_get(power_dir, "pm_test")));
+	for (test_name = strtok(test_line, " "); test_name;
+	     test_name = strtok(NULL, " "))
+		if (test_name[0] == '[') {
+			test_name[strlen(test_name) - 1] = '\0';
+			test_name++;
+			break;
+		}
+
+	if (!test_name) {
+	  	free(test_line);
+		return SUSPEND_TEST_NONE;
+	}
+
+	for (test = SUSPEND_TEST_NONE; test < SUSPEND_TEST_NUM; test++)
+		if (strcmp(suspend_test_name[test], test_name) == 0)
+			break;
+
+	igt_assert(test < SUSPEND_TEST_NUM);
+
+	free(test_line);
+	return test;
+}
+
+static void set_suspend_test(int power_dir, enum igt_suspend_test test)
+{
+	igt_assert(test < SUSPEND_TEST_NUM);
+
+	if (faccessat(power_dir, "pm_test", W_OK, 0)) {
+		igt_require(test == SUSPEND_TEST_NONE);
+		return;
+	}
+
+	igt_assert(igt_sysfs_set(power_dir, "pm_test", suspend_test_name[test]));
+}
+
+#define SQUELCH ">/dev/null 2>&1"
+
+static void suspend_via_rtcwake(enum igt_suspend_state state)
+{
+	char cmd[128];
+	int delay, ret;
+
+	igt_assert(state < SUSPEND_STATE_NUM);
+
+	delay = igt_get_autoresume_delay(state);
+
+	/*
+	 * Skip if rtcwake would fail for a reason not related to the kernel's
+	 * suspend functionality.
+	 */
+	snprintf(cmd, sizeof(cmd), "rtcwake -n -s %d -m %s " SQUELCH,
+		 delay, suspend_state_name[state]);
+	ret = igt_system(cmd);
+	igt_require_f(ret == 0, "rtcwake test failed with %i\n"
+		     "This failure could mean that something is wrong with "
+		     "the rtcwake tool or how your distro is set up.\n",
+		      ret);
+
+	snprintf(cmd, sizeof(cmd), "rtcwake -s %d -m %s ",
+		 delay, suspend_state_name[state]);
+	ret = igt_system(cmd);
+	if (ret) {
+		const char *path = "suspend_stats";
+		char *info;
+		int dir;
+
+		igt_warn("rtcwake failed with %i\n"
+			 "Check dmesg for further details.\n",
+			 ret);
+
+		dir = open(igt_debugfs_mount(), O_RDONLY);
+		info = igt_sysfs_get(dir, path);
+		close(dir);
+		if (info) {
+			igt_debug("%s:\n%s\n", path, info);
+			free(info);
+		}
+	}
+	igt_assert_eq(ret, 0);
+}
+
+static void suspend_via_sysfs(int power_dir, enum igt_suspend_state state)
+{
+	igt_assert(state < SUSPEND_STATE_NUM);
+	igt_assert(igt_sysfs_set(power_dir, "state",
+				 suspend_state_name[state]));
+}
+
+static bool is_state_supported(int power_dir, enum igt_suspend_state state)
+{
+	const char *str;
+	char *states;
+
+	igt_assert((states = igt_sysfs_get(power_dir, "state")));
+
+	str = strstr(states, suspend_state_name[state]);
+
+	if (!str)
+		igt_info("State %s not supported.\nSupported States: %s\n",
+			 suspend_state_name[state], states);
+
+	free(states);
+	return str;
+}
+
+static int get_mem_sleep(void)
+{
+	char *mem_sleep_states;
+	char *mem_sleep_state;
+	enum igt_mem_sleep mem_sleep;
+	int power_dir;
+
+	igt_require((power_dir = open("/sys/power", O_RDONLY)) >= 0);
+
+	if (faccessat(power_dir, "mem_sleep", R_OK, 0))
+		return MEM_SLEEP_NONE;
+
+	igt_assert((mem_sleep_states = igt_sysfs_get(power_dir, "mem_sleep")));
+	for (mem_sleep_state = strtok(mem_sleep_states, " "); mem_sleep_state;
+	     mem_sleep_state = strtok(NULL, " ")) {
+		if (mem_sleep_state[0] == '[') {
+			mem_sleep_state[strlen(mem_sleep_state) - 1] = '\0';
+			mem_sleep_state++;
+			break;
+		}
+	}
+
+	if (!mem_sleep_state) {
+		free(mem_sleep_states);
+		return MEM_SLEEP_NONE;
+	}
+
+	for (mem_sleep = MEM_SLEEP_S2IDLE; mem_sleep < MEM_SLEEP_NUM; mem_sleep++) {
+		if (strcmp(mem_sleep_name[mem_sleep], mem_sleep_state) == 0)
+			break;
+	}
+
+	igt_assert_f(mem_sleep < MEM_SLEEP_NUM, "Invalid mem_sleep state\n");
+
+	free(mem_sleep_states);
+	close(power_dir);
+	return mem_sleep;
+}
+
+static void set_mem_sleep(int power_dir, enum igt_mem_sleep sleep)
+{
+	igt_assert(sleep < MEM_SLEEP_NUM);
+
+	igt_assert_eq(faccessat(power_dir, "mem_sleep", W_OK, 0), 0);
+
+	igt_assert(igt_sysfs_set(power_dir, "mem_sleep",
+				 mem_sleep_name[sleep]));
+}
+
+static bool is_mem_sleep_state_supported(int power_dir, enum igt_mem_sleep state)
+{
+	const char *str;
+	char *mem_sleep_states;
+
+	igt_assert((mem_sleep_states = igt_sysfs_get(power_dir, "mem_sleep")));
+
+	str = strstr(mem_sleep_states, mem_sleep_name[state]);
+
+	if (!str)
+		igt_info("mem_sleep state %s not supported.\nSupported mem_sleep states: %s\n",
+			 mem_sleep_name[state], mem_sleep_states);
+
+	free(mem_sleep_states);
+	return str;
+}
+
+/**
+ * igt_system_suspend_autoresume:
+ * @state: an #igt_suspend_state, the target suspend state
+ * @test: an #igt_suspend_test, test point at which to complete the suspend
+ *	  cycle
+ *
+ * Execute a system suspend cycle targeting the given @state optionally
+ * completing the cycle at the given @test point and automaically wake up
+ * again. Waking up is either achieved using the RTC wake-up alarm for a full
+ * suspend cycle or a kernel timer for a suspend test cycle. The kernel timer
+ * delay for a test cycle can be configured by the suspend.pm_test_delay
+ * kernel parameter (5 sec by default).
+ *
+ * #SUSPEND_TEST_NONE specifies a full suspend cycle.
+ * The #SUSPEND_TEST_FREEZER..#SUSPEND_TEST_CORE test points can make it
+ * possible to collect error logs in case a full suspend cycle would prevent
+ * this by hanging the machine, or they can provide an idea of the faulty
+ * component by comparing fail/no-fail results at different test points.
+ *
+ * This is very handy for implementing any kind of suspend/resume test.
+ */
+void igt_system_suspend_autoresume(enum igt_suspend_state state,
+				   enum igt_suspend_test test)
+{
+	int power_dir;
+	enum igt_suspend_test orig_test;
+	enum igt_mem_sleep orig_mem_sleep = MEM_SLEEP_NONE;
+
+	igt_require((power_dir = open("/sys/power", O_RDONLY)) >= 0);
+	igt_require(is_state_supported(power_dir, state));
+	igt_require(test == SUSPEND_TEST_NONE ||
+		    faccessat(power_dir, "pm_test", R_OK | W_OK, 0) == 0);
+
+	igt_skip_on_f(state == SUSPEND_STATE_DISK &&
+		      !igt_get_total_swap_mb(),
+		      "Suspend to disk requires swap space.\n");
+
+	orig_test = get_suspend_test(power_dir);
+
+	if (state == SUSPEND_STATE_S3) {
+		orig_mem_sleep = get_mem_sleep();
+		igt_skip_on_f(!is_mem_sleep_state_supported(power_dir, MEM_SLEEP_DEEP),
+			      "S3 not supported in this system.\n");
+		set_mem_sleep(power_dir, MEM_SLEEP_DEEP);
+		igt_skip_on_f(get_mem_sleep() != MEM_SLEEP_DEEP,
+			      "S3 not possible in this system.\n");
+	}
+
+	set_suspend_test(power_dir, test);
+
+	if (test == SUSPEND_TEST_NONE)
+		suspend_via_rtcwake(state);
+	else
+		suspend_via_sysfs(power_dir, state);
+
+	if (orig_mem_sleep)
+		set_mem_sleep(power_dir, orig_mem_sleep);
+
+	set_suspend_test(power_dir, orig_test);
+	close(power_dir);
+}
+
+static int original_autoresume_delay;
+
+static void igt_restore_autoresume_delay(int sig)
+{
+	int delay_fd;
+	char delay_str[10];
+
+	igt_require((delay_fd = open("/sys/module/suspend/parameters/pm_test_delay",
+				    O_WRONLY)) >= 0);
+
+	snprintf(delay_str, sizeof(delay_str), "%d", original_autoresume_delay);
+	igt_require(write(delay_fd, delay_str, strlen(delay_str)));
+
+	close(delay_fd);
+}
+
+/**
+ * igt_set_autoresume_delay:
+ * @delay_secs: The delay in seconds before resuming the system
+ *
+ * Sets how long we wait to resume the system after suspending it, using the
+ * suspend.pm_test_delay variable. On exit, the original delay value is
+ * restored.
+ */
+void igt_set_autoresume_delay(int delay_secs)
+{
+	int delay_fd;
+	char delay_str[10];
+
+	delay_fd = open("/sys/module/suspend/parameters/pm_test_delay", O_RDWR);
+
+	if (delay_fd >= 0) {
+		if (!original_autoresume_delay) {
+			igt_require(read(delay_fd, delay_str,
+					 sizeof(delay_str)));
+			original_autoresume_delay = atoi(delay_str);
+			igt_install_exit_handler(igt_restore_autoresume_delay);
+		}
+
+		snprintf(delay_str, sizeof(delay_str), "%d", delay_secs);
+		igt_require(write(delay_fd, delay_str, strlen(delay_str)));
+
+		close(delay_fd);
+	}
+
+	autoresume_delay = delay_secs;
+}
+
+/**
+ * igt_get_autoresume_delay:
+ * @state: an #igt_suspend_state, the target suspend state
+ *
+ * Retrieves how long we wait to resume the system after suspending it.
+ * This can either be set through igt_set_autoresume_delay or be a default
+ * value that depends on the suspend state.
+ *
+ * Returns: The autoresume delay, in seconds.
+ */
+int igt_get_autoresume_delay(enum igt_suspend_state state)
+{
+	int delay;
+
+	if (autoresume_delay)
+		delay = autoresume_delay;
+	else
+		delay = state == SUSPEND_STATE_DISK ? 30 : 15;
+
+	return delay;
+}
+
+/**
+ * igt_drop_root:
+ *
+ * Drop root privileges and make sure it actually worked. Useful for tests
+ * which need to check security constraints. Note that this should only be
+ * called from manually forked processes, since the lack of root privileges
+ * will wreak havoc with the automatic cleanup handlers.
+ */
+void igt_drop_root(void)
+{
+	igt_assert_eq(getuid(), 0);
+
+	igt_assert_eq(setgroups(0, NULL), 0);
+	igt_assert_eq(setgid(2), 0);
+	igt_assert_eq(setuid(2), 0);
+
+	igt_assert_eq(getgroups(0, NULL), 0);
+	igt_assert_eq(getgid(), 2);
+	igt_assert_eq(getuid(), 2);
+}
+
+/**
+ * igt_debug_wait_for_keypress:
+ * @var: var lookup to to enable this wait
+ *
+ * Waits for a key press when run interactively and when the corresponding debug
+ * var is set in the --interactive-debug=$var variable. Multiple keys
+ * can be specified as a comma-separated list or alternatively "all" if a wait
+ * should happen for all cases. Calling this function with "all" will assert.
+ *
+ * When not connected to a terminal interactive_debug is ignored
+ * and execution immediately continues.
+ *
+ * This is useful for display tests where under certain situation manual
+ * inspection of the display is useful. Or when running a testcase in the
+ * background.
+ */
+void igt_debug_wait_for_keypress(const char *var)
+{
+	struct termios oldt, newt;
+
+	if (!isatty(STDIN_FILENO)) {
+		errno = 0; /* otherwise would be either ENOTTY or EBADF */
+		return;
+	}
+
+	if (!igt_interactive_debug)
+		return;
+
+	if (strstr(var, "all"))
+		igt_assert_f(false, "Bug in test: Do not call igt_debug_wait_for_keypress with \"all\"\n");
+
+	if (!strstr(igt_interactive_debug, var) &&
+	    !strstr(igt_interactive_debug, "all"))
+		return;
+
+	igt_info("Press any key to continue ...\n");
+
+	tcgetattr ( STDIN_FILENO, &oldt );
+	newt = oldt;
+	newt.c_lflag &= ~( ICANON | ECHO );
+	tcsetattr ( STDIN_FILENO, TCSANOW, &newt );
+	getchar();
+	tcsetattr ( STDIN_FILENO, TCSANOW, &oldt );
+}
+
+/**
+ * igt_debug_interactive_mode_check:
+ * @var: var lookup to to enable this wait
+ * @expected: message to be printed as expected behaviour before wait for keys Y/n
+ *
+ * Waits for a key press when run interactively and when the corresponding debug
+ * var is set in the --interactive-debug=$var variable. Multiple vars
+ * can be specified as a comma-separated list or alternatively "all" if a wait
+ * should happen for all cases.
+ *
+ * This is useful for display tests where under certain situation manual
+ * inspection of the display is useful. Or when running a testcase in the
+ * background.
+ *
+ * When not connected to a terminal interactive_debug is ignored
+ * and execution immediately continues. For this reason by default this function
+ * returns true. It returns false only when N/n is pressed indicating the
+ * user isn't seeing what was expected.
+ *
+ * Force test fail when N/n is pressed.
+ */
+void igt_debug_interactive_mode_check(const char *var, const char *expected)
+{
+	struct termios oldt, newt;
+	char key;
+
+	if (!isatty(STDIN_FILENO)) {
+		errno = 0; /* otherwise would be either ENOTTY or EBADF */
+		return;
+	}
+
+	if (!igt_interactive_debug)
+		return;
+
+	if (!strstr(igt_interactive_debug, var) &&
+	    !strstr(igt_interactive_debug, "all"))
+		return;
+
+	igt_info("Is %s [Y/n]", expected);
+
+	tcgetattr ( STDIN_FILENO, &oldt );
+	newt = oldt;
+	newt.c_lflag &= ~ICANON;
+	tcsetattr ( STDIN_FILENO, TCSANOW, &newt );
+	key = getchar();
+	tcsetattr ( STDIN_FILENO, TCSANOW, &oldt );
+
+	igt_info("\n");
+
+	igt_assert(key != 'n' && key != 'N');
+}
+
+/**
+ * igt_lock_mem:
+ * @size: the amount of memory to lock into RAM, in MB
+ *
+ * Allocate @size MB of memory and lock it into RAM. This releases any
+ * previously locked memory.
+ *
+ * Use #igt_unlock_mem to release the currently locked memory.
+ */
+static char *locked_mem;
+static size_t locked_size;
+
+void igt_lock_mem(size_t size)
+{
+	long pagesize = sysconf(_SC_PAGESIZE);
+	size_t i;
+	int ret;
+
+	if (size == 0) {
+		return;
+	}
+
+	if (locked_mem) {
+		igt_unlock_mem();
+		igt_warn("Unlocking previously locked memory.\n");
+	}
+
+	locked_size = size * 1024 * 1024;
+
+	locked_mem = malloc(locked_size);
+	igt_require_f(locked_mem,
+		      "Could not malloc %zdMiB for locking.\n", size);
+
+	/* write into each page to ensure it is allocated */
+	for (i = 0; i < locked_size; i += pagesize)
+		locked_mem[i] = i;
+
+	ret = mlock(locked_mem, locked_size);
+	igt_assert_f(ret == 0, "Could not mlock %zdMiB.\n", size);
+}
+
+/**
+ * igt_unlock_mem:
+ *
+ * Release and free the RAM used by #igt_lock_mem.
+ */
+void igt_unlock_mem(void)
+{
+	if (!locked_mem)
+		return;
+
+	munlock(locked_mem, locked_size);
+
+	free(locked_mem);
+	locked_mem = NULL;
+}
+
+/**
+ * igt_is_process_running:
+ * @comm: Name of process in the form found in /proc/pid/comm (limited to 15
+ * chars)
+ *
+ * Returns: true in case the process has been found, false otherwise.
+ *
+ * This function checks in the process table for an entry with the name @comm.
+ */
+int igt_is_process_running(const char *comm)
+{
+	PROCTAB *proc;
+	proc_t *proc_info;
+	bool found = false;
+
+	proc = openproc(PROC_FILLCOM | PROC_FILLSTAT);
+	igt_assert(proc != NULL);
+
+	while ((proc_info = readproc(proc, NULL))) {
+		if (!strncasecmp(proc_info->cmd, comm, sizeof(proc_info->cmd))) {
+			freeproc(proc_info);
+			found = true;
+			break;
+		}
+		freeproc(proc_info);
+	}
+
+	closeproc(proc);
+	return found;
+}
+
+/**
+ * igt_terminate_process:
+ * @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_terminate_process(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)
+{
+	struct pinfo p = { .pid = info->tid, .comm = info->cmd, .fn = fn };
+
+	if (!*state)
+		igt_show_stat_header();
+
+	__igt_show_stat(&p);
+	++*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);
+	}
+
+	closedir(dp);
+}
+
+/*
+ * 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[30];
+	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_warn("%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 void pulseaudio_unload_module(proc_t *proc_info)
+{
+	struct igt_helper_process pa_proc = {};
+	char xdg_dir[PATH_MAX];
+	const char *homedir;
+	struct passwd *pw;
+
+	igt_fork_helper(&pa_proc) {
+		pw = getpwuid(proc_info->euid);
+		homedir = pw->pw_dir;
+		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", proc_info->euid);
+
+		igt_info("Request 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_wait_helper(&pa_proc);
+}
+
+static int pipewire_pulse_pid = 0;
+static int pipewire_pw_reserve_pid = 0;
+static struct igt_helper_process pw_reserve_proc = {};
+
+static void pipewire_reserve_wait(void)
+{
+	char xdg_dir[PATH_MAX];
+	const char *homedir;
+	struct passwd *pw;
+	proc_t *proc_info;
+	PROCTAB *proc;
+
+	igt_fork_helper(&pw_reserve_proc) {
+		igt_info("Preventing pipewire-pulse to use the audio drivers\n");
+
+		proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
+		igt_assert(proc != NULL);
+
+		while ((proc_info = readproc(proc, NULL))) {
+			if (pipewire_pulse_pid == proc_info->tid)
+				break;
+			freeproc(proc_info);
+		}
+		closeproc(proc);
+
+		/* Sanity check: if it can't find the process, it means it has gone */
+		if (pipewire_pulse_pid != proc_info->tid)
+			exit(0);
+
+		pw = getpwuid(proc_info->euid);
+		homedir = pw->pw_dir;
+		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", proc_info->euid);
+		setgid(proc_info->egid);
+		setuid(proc_info->euid);
+		clearenv();
+		setenv("HOME", homedir, 1);
+		setenv("XDG_RUNTIME_DIR",xdg_dir, 1);
+		freeproc(proc_info);
+
+		/*
+		 * pw-reserve will run in background. It will only exit when
+		 * igt_kill_children() is called later on. So, it shouldn't
+		 * call igt_waitchildren(). Instead, just exit with the return
+		 * code from pw-reserve.
+		 */
+		exit(system("pw-reserve -n Audio0 -r"));
+	}
+}
+
+/* Maximum time waiting for pw-reserve to start running */
+#define PIPEWIRE_RESERVE_MAX_TIME 1000 /* milisseconds */
+
+int pipewire_pulse_start_reserve(void)
+{
+	bool is_pw_reserve_running = false;
+	proc_t *proc_info;
+	int attempts = 0;
+	PROCTAB *proc;
+
+	if (!pipewire_pulse_pid)
+		return 0;
+
+	pipewire_reserve_wait();
+
+	/*
+	 * Note: using pw-reserve to stop using audio only works with
+	 * pipewire version 0.3.50 or upper.
+	 */
+	for (attempts = 0; attempts < PIPEWIRE_RESERVE_MAX_TIME; attempts++) {
+		usleep(1000);
+		proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
+		igt_assert(proc != NULL);
+
+		while ((proc_info = readproc(proc, NULL))) {
+			if (!strcmp(proc_info->cmd, "pw-reserve")) {
+				is_pw_reserve_running = true;
+				pipewire_pw_reserve_pid = proc_info->tid;
+				freeproc(proc_info);
+				break;
+			}
+			freeproc(proc_info);
+		}
+		closeproc(proc);
+		if (is_pw_reserve_running)
+			break;
+	}
+	if (!is_pw_reserve_running) {
+		igt_warn("Failed to remove audio drivers from pipewire\n");
+		return 1;
+	}
+	/* Let's grant some time for pw_reserve to notify pipewire via D-BUS */
+	usleep(50000);
+
+	/*
+	 * pw-reserve is running, and should have stopped using the audio
+	 * drivers. We can now remove the driver.
+	 */
+
+	return 0;
+}
+
+void pipewire_pulse_stop_reserve(void)
+{
+	if (!pipewire_pulse_pid)
+		return;
+
+	igt_stop_helper(&pw_reserve_proc);
+}
+
+/**
+ * __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
+ * @pipewire_pulse_pid: PID of pipewire-pulse process
+ *
+ * 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 process is pipewire-pulse, it can't be killed, as systemd will
+ *	  respawn it. So, instead, the caller should call pw-reserve, remove
+ *	  the kernel driver and then stop pw-reserve. On such case, this
+ *	  function returns the PID of pipewire-pulse, but won't touch it.
+ * 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;
+
+	/*
+	 * Terminating pipewire-pulse require an special procedure, which
+	 * is only available at version 0.3.50 and upper. 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.
+	 */
+	if (!strcmp(proc_info->cmd, "pipewire-pulse")) {
+		igt_info("process %d (%s) is using audio device. Should be requested to stop using them.\n",
+			 proc_info->tid, proc_info->cmd);
+		pipewire_pulse_pid = proc_info->tid;
+		return 0;
+	}
+	/*
+	 * pipewire-pulse itself doesn't hook into a /dev/snd device. Instead,
+	 * the actual binding happens at the Pipewire Session Manager, e.g.
+	 * either wireplumber or pipewire media-session.
+	 *
+	 * Just killing such processes won't produce any effect, as systemd
+	 * will respawn them. So, just ignore here, they'll honor pw-reserve,
+	 * when the time comes.
+	 */
+	if (!strcmp(proc_info->cmd, "pipewire-media-session"))
+		return 0;
+	if (!strcmp(proc_info->cmd, "wireplumber"))
+		return 0;
+
+	dp = opendir(proc_path);
+	if (!dp && errno == ENOENT)
+		return 0;
+	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;
+		}
+
+		/* 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);
+	pipewire_pulse_pid = 0;
+
+	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;
+	struct sigaction oldact;
+	struct igt_mean mean;
+
+	int sig;
+} igt_siglatency;
+
+static long delay(void)
+{
+	return hars_petruska_f54_1_random_unsafe() % (NSEC_PER_SEC / 1000);
+}
+
+static double elapsed(const struct timespec *now, const struct timespec *last)
+{
+	double nsecs;
+
+	nsecs = now->tv_nsec - last ->tv_nsec;
+	nsecs += 1e9*(now->tv_sec - last->tv_sec);
+
+	return nsecs;
+}
+
+static void siglatency(int sig, siginfo_t *info, void *arg)
+{
+	struct itimerspec its;
+
+	clock_gettime(CLOCK_MONOTONIC, &its.it_value);
+	if (info)
+		igt_mean_add(&igt_siglatency.mean,
+			     elapsed(&its.it_value, &igt_siglatency.target));
+	igt_siglatency.target = its.it_value;
+
+	its.it_value.tv_nsec += 100 * 1000;
+	its.it_value.tv_nsec += delay();
+	if (its.it_value.tv_nsec >= NSEC_PER_SEC) {
+		its.it_value.tv_nsec -= NSEC_PER_SEC;
+		its.it_value.tv_sec += 1;
+	}
+	its.it_interval.tv_sec = its.it_interval.tv_nsec = 0;
+	timer_settime(igt_siglatency.timer, TIMER_ABSTIME, &its, NULL);
+}
+
+void igt_start_siglatency(int sig)
+{
+	struct sigevent sev;
+	struct sigaction act;
+
+	if (sig <= 0)
+		sig = SIGRTMIN;
+
+	if (igt_siglatency.sig)
+		(void)igt_stop_siglatency(NULL);
+	igt_assert(igt_siglatency.sig == 0);
+	igt_siglatency.sig = sig;
+
+	memset(&sev, 0, sizeof(sev));
+	sev.sigev_notify = SIGEV_SIGNAL | SIGEV_THREAD_ID;
+	sev.sigev_notify_thread_id = gettid();
+	sev.sigev_signo = sig;
+	timer_create(CLOCK_MONOTONIC, &sev, &igt_siglatency.timer);
+
+	memset(&act, 0, sizeof(act));
+	act.sa_sigaction = siglatency;
+	sigaction(sig, &act, &igt_siglatency.oldact);
+
+	siglatency(sig, NULL, NULL);
+}
+
+double igt_stop_siglatency(struct igt_mean *result)
+{
+	double mean = igt_mean_get(&igt_siglatency.mean);
+
+	if (result)
+		*result = igt_siglatency.mean;
+
+	sigaction(igt_siglatency.sig, &igt_siglatency.oldact, NULL);
+	timer_delete(igt_siglatency.timer);
+	memset(&igt_siglatency, 0, sizeof(igt_siglatency));
+
+	return mean;
+}
+
+bool igt_allow_unlimited_files(void)
+{
+	struct rlimit rlim;
+	unsigned nofile_rlim = 1024*1024;
+
+	FILE *file = fopen("/proc/sys/fs/nr_open", "r");
+	if (file) {
+		igt_assert(fscanf(file, "%u", &nofile_rlim) == 1);
+		igt_info("System limit for open files is %u\n", nofile_rlim);
+		fclose(file);
+	}
+
+	if (getrlimit(RLIMIT_NOFILE, &rlim))
+		return false;
+
+	rlim.rlim_cur = nofile_rlim;
+	rlim.rlim_max = nofile_rlim;
+	return setrlimit(RLIMIT_NOFILE, &rlim) == 0;
+}
+
+/**
+ * vfs_file_max: report maximum number of files
+ *
+ * Get the global system-wide maximum of open files the kernel allows,
+ * by reading /proc/sys/fs/file-max. Fails the current subtest if
+ * reading the file fails, and returns a suitable best guess if it
+ * cannot be opened.
+ *
+ * Returns: System-wide maximum of open files, or a best effort guess.
+ */
+uint64_t vfs_file_max(void)
+{
+	static long long unsigned max;
+	if (max == 0) {
+		FILE *file = fopen("/proc/sys/fs/file-max", "r");
+		max = 80000;
+		if (file) {
+			igt_assert(fscanf(file, "%llu", &max) == 1);
+			fclose(file);
+		}
+	}
+	return max;
+}
+
+void *igt_memdup(const void *ptr, size_t len)
+{
+	void *dup;
+
+	dup = malloc(len);
+	if (dup)
+		memcpy(dup, ptr, len);
+
+	return dup;
+}
diff --git a/lib/meson.build b/lib/meson.build
index cef2d0ff..cb83ee99 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -103,7 +103,6 @@ lib_deps = [
 	libdrm,
 	libdw,
 	libkmod,
-	libprocps,
 	libudev,
 	math,
 	pciaccess,
@@ -173,6 +172,12 @@ if chamelium.found()
 	lib_sources += 'monitor_edids/monitor_edids_helper.c'
 endif
 
+if libprocps.found()
+	lib_deps += libprocps
+else
+	lib_deps += libproc2
+endif
+
 if get_option('srcdir') != ''
     srcdir = join_paths(get_option('srcdir'), 'tests')
 else
diff --git a/lib/meson.build.orig b/lib/meson.build.orig
new file mode 100644
index 00000000..cef2d0ff
--- /dev/null
+++ b/lib/meson.build.orig
@@ -0,0 +1,358 @@
+lib_sources = [
+	'drmtest.c',
+	'huc_copy.c',
+	'i915/gem.c',
+	'i915/gem_context.c',
+	'i915/gem_create.c',
+	'i915/gem_engine_topology.c',
+	'i915/gem_scheduler.c',
+	'i915/gem_submission.c',
+	'i915/gem_ring.c',
+	'i915/gem_mman.c',
+	'i915/gem_vm.c',
+	'i915/intel_memory_region.c',
+	'i915/intel_mocs.c',
+	'i915/i915_blt.c',
+	'i915/i915_crc.c',
+	'igt_collection.c',
+	'igt_color_encoding.c',
+	'igt_crc.c',
+	'igt_debugfs.c',
+	'igt_device.c',
+	'igt_device_scan.c',
+	'igt_drm_fdinfo.c',
+	'igt_aux.c',
+	'igt_gt.c',
+	'igt_halffloat.c',
+	'igt_hwmon.c',
+	'igt_io.c',
+	'igt_matrix.c',
+	'igt_os.c',
+	'igt_params.c',
+	'igt_perf.c',
+	'igt_pipe_crc.c',
+	'igt_power.c',
+	'igt_primes.c',
+	'igt_rand.c',
+	'igt_stats.c',
+	'igt_syncobj.c',
+	'igt_sysfs.c',
+	'igt_sysrq.c',
+	'igt_taints.c',
+	'igt_thread.c',
+	'igt_types.c',
+	'igt_vec.c',
+	'igt_vgem.c',
+	'igt_x86.c',
+	'instdone.c',
+	'intel_allocator.c',
+	'intel_allocator_msgchannel.c',
+	'intel_allocator_random.c',
+	'intel_allocator_reloc.c',
+	'intel_allocator_simple.c',
+	'intel_batchbuffer.c',
+	'intel_bufops.c',
+	'intel_chipset.c',
+	'intel_ctx.c',
+	'intel_device_info.c',
+	'intel_mmio.c',
+	'ioctl_wrappers.c',
+	'media_spin.c',
+	'media_fill.c',
+	'gpgpu_fill.c',
+	'gpu_cmds.c',
+	'rendercopy_i915.c',
+	'rendercopy_i830.c',
+	'rendercopy_gen4.c',
+	'rendercopy_gen6.c',
+	'rendercopy_gen7.c',
+	'rendercopy_gen8.c',
+	'rendercopy_gen9.c',
+	'runnercomms.c',
+	'sw_sync.c',
+	'intel_aux_pgtable.c',
+	'intel_reg_map.c',
+	'intel_iosf.c',
+	'igt_kms.c',
+	'igt_fb.c',
+	'igt_core.c',
+	'igt_draw.c',
+	'igt_list.c',
+	'igt_map.c',
+	'igt_pm.c',
+	'igt_dummyload.c',
+	'igt_store.c',
+	'uwildmat/uwildmat.c',
+	'igt_kmod.c',
+	'igt_panfrost.c',
+	'igt_v3d.c',
+	'igt_vc4.c',
+	'igt_psr.c',
+	'igt_amd.c',
+	'igt_edid.c',
+	'igt_eld.c',
+	'igt_infoframe.c',
+	'veboxcopy_gen12.c',
+	'igt_msm.c',
+]
+
+lib_deps = [
+	cairo,
+	glib,
+	libatomic,
+	libdrm,
+	libdw,
+	libkmod,
+	libprocps,
+	libudev,
+	math,
+	pciaccess,
+	pixman,
+	pthreads,
+	realtime,
+	zlib
+]
+
+if libdrm_intel.found()
+	lib_deps += libdrm_intel
+else
+	lib_sources += 'stubs/drm/intel_bufmgr.c'
+	inc = [ inc, include_directories('stubs/drm') ]
+endif
+
+if libdrm_nouveau.found()
+	lib_deps += libdrm_nouveau
+	lib_sources += [
+		'igt_nouveau.c',
+		'nouveau/cea0b5.c'
+	]
+endif
+
+if libdrm_amdgpu.found()
+	lib_deps += libdrm_amdgpu
+	lib_sources += [
+		'amdgpu/amd_memory.c',
+		'amdgpu/amd_command_submission.c',
+		'amdgpu/amd_compute.c',
+		'amdgpu/amd_gfx.c',
+		'amdgpu/amd_ip_blocks.c',
+		'amdgpu/amd_shaders.c',
+		'amdgpu/amd_gfx_v8_0.c',
+		'amdgpu/amd_gfx_v9_0.c',
+		'amdgpu/amd_dispatch_helpers.c',
+		'amdgpu/amd_dispatch.c',
+		'amdgpu/amd_deadlock_helpers.c',
+		'amdgpu/amd_pci_unplug.c',
+		'amdgpu/xalloc.h'
+	]
+endif
+
+if libunwind.found()
+	lib_deps += libunwind
+else
+	inc = [ inc, include_directories('stubs/libunwind') ]
+endif
+
+if valgrind.found()
+	lib_deps += valgrind
+endif
+
+if gsl.found()
+	lib_deps += gsl
+	lib_sources += [ 'igt_frame.c', 'igt_audio.c' ]
+endif
+
+if alsa.found()
+	lib_deps += alsa
+	lib_sources += 'igt_alsa.c'
+endif
+
+if chamelium.found()
+	lib_deps += chamelium
+	lib_sources += [ 'igt_chamelium.c', 'igt_chamelium_stream.c' ]
+	lib_sources += 'monitor_edids/monitor_edids_helper.c'
+endif
+
+if get_option('srcdir') != ''
+    srcdir = join_paths(get_option('srcdir'), 'tests')
+else
+    srcdir = join_paths(meson.source_root(), 'tests')
+endif
+
+if get_option('version_hash') != ''
+    vcs_command = ['echo', get_option('version_hash')]
+else
+    vcs_command = [ 'git', 'log', '-n1', '--pretty=format:g%h' ]
+endif
+
+lib_version = vcs_tag(input : 'version.h.in', output : 'version.h',
+		      fallback : 'NO-GIT',
+		      command : vcs_command )
+
+lib_intermediates = []
+foreach f: lib_sources
+    name = f.underscorify()
+    lib = static_library('igt-' + name,
+	[ f, lib_version ],
+	include_directories: inc,
+	dependencies : lib_deps,
+	c_args : [
+	    '-DIGT_DATADIR="@0@"'.format(join_paths(prefix, datadir)),
+	    '-DIGT_SRCDIR="@0@"'.format(srcdir),
+	    '-DIGT_LOG_DOMAIN="@0@"'.format(f.split('.')[0]),
+	])
+
+    lib_intermediates += lib
+endforeach
+
+lib_igt_build = shared_library('igt',
+    ['dummy.c'],
+    link_whole: lib_intermediates,
+    dependencies: lib_deps,
+    install : true,
+    soversion : '0',
+)
+
+lib_igt = declare_dependency(link_with : lib_igt_build,
+			    include_directories : inc)
+
+igt_deps = [ lib_igt ] + lib_deps
+
+lin_igt_chipset_build = static_library('igt_chipset',
+                                       ['intel_chipset.c',
+                                        'intel_device_info.c'],
+                                       include_directories : inc)
+
+lib_igt_chipset = declare_dependency(link_with : lin_igt_chipset_build,
+                                     include_directories : inc)
+
+lib_igt_perf_build = static_library('igt_perf',
+	['igt_perf.c'],
+	include_directories : inc)
+
+lib_igt_perf = declare_dependency(link_with : lib_igt_perf_build,
+				  include_directories : inc)
+
+scan_dep = [
+	glib,
+	libudev,
+]
+
+lib_igt_device_scan_build = static_library('igt_device_scan',
+	['igt_device_scan.c',
+	'igt_list.c',
+	'igt_tools_stub.c',
+	'intel_device_info.c',
+	],
+	dependencies : scan_dep,
+	include_directories : inc)
+
+lib_igt_device_scan = declare_dependency(link_with : lib_igt_device_scan_build,
+				  include_directories : inc)
+
+lib_igt_drm_fdinfo_build = static_library('igt_drm_fdinfo',
+	['igt_drm_fdinfo.c'],
+	include_directories : inc)
+
+lib_igt_drm_fdinfo = declare_dependency(link_with : lib_igt_drm_fdinfo_build,
+				  include_directories : inc)
+i915_perf_files = [
+  'igt_list.c',
+  'i915/perf.c',
+  'i915/perf_data_reader.c',
+]
+
+i915_perf_hardware = [
+  'hsw',
+  'bdw', 'chv',
+  'sklgt2', 'sklgt3', 'sklgt4',
+  'kblgt2', 'kblgt3',
+  'cflgt2', 'cflgt3',
+  'bxt', 'glk',
+  'cnl',
+  'icl', 'ehl',
+  'tglgt1', 'tglgt2', 'rkl', 'dg1', 'adl',
+  'acmgt1', 'acmgt2', 'acmgt3',
+]
+
+i915_xml_files = []
+foreach hw : i915_perf_hardware
+  i915_xml_files += files('i915/perf-configs/oa-@0@.xml'.format(hw))
+endforeach
+
+i915_perf_files += custom_target(
+  'i915-perf-equations',
+  input : [ 'i915/perf-configs/perf-equations-codegen.py' ] + i915_xml_files,
+  output : [ 'i915_perf_equations.c', 'i915_perf_equations.h' ],
+  command : [
+    python3, '@INPUT0@',
+    '--code', '@OUTPUT0@',
+    '--header', '@OUTPUT1@',
+    i915_xml_files,
+  ])
+
+foreach hw : i915_perf_hardware
+  i915_perf_files += custom_target(
+    'i915-perf-registers-@0@'.format(hw),
+    input : [ 'i915/perf-configs/perf-registers-codegen.py',
+              'i915/perf-configs/oa-@0@.xml'.format(hw) ],
+    output : [ 'i915_perf_registers_@0@.c'.format(hw),
+               'i915_perf_registers_@0@.h'.format(hw), ],
+    command : [
+      python3, '@INPUT0@',
+      '--code', '@OUTPUT0@',
+      '--header', '@OUTPUT1@',
+      '--xml-file', '@INPUT1@'
+    ])
+  i915_perf_files += custom_target(
+    'i915-perf-metrics-@0@'.format(hw),
+    input : [ 'i915/perf-configs/perf-metricset-codegen.py',
+              'i915/perf-configs/oa-@0@.xml'.format(hw) ],
+    output : [ 'i915_perf_metrics_@0@.c'.format(hw),
+               'i915_perf_metrics_@0@.h'.format(hw), ],
+    command : [
+      python3, '@INPUT0@',
+      '--code', '@OUTPUT0@',
+      '--header', '@OUTPUT1@',
+      '--equations-include', 'i915_perf_equations.h',
+      '--registers-include', 'i915_perf_registers_@0@.h'.format(hw),
+      '--xml-file', '@INPUT1@',
+    ])
+endforeach
+
+lib_igt_i915_perf_build = shared_library(
+  'i915_perf',
+  i915_perf_files,
+  dependencies: lib_igt_chipset,
+  include_directories : inc,
+  install: true,
+  soversion: '1.5')
+
+lib_igt_i915_perf = declare_dependency(
+  link_with : lib_igt_i915_perf_build,
+  include_directories : inc)
+
+install_headers(
+  'igt_list.h',
+  'intel_chipset.h',
+  'i915/perf.h',
+  'i915/perf_data.h',
+  'i915/perf_data_reader.h',
+  subdir : 'i915-perf'
+)
+
+pkgconf = configuration_data()
+
+pkgconf.set('prefix', get_option('prefix'))
+pkgconf.set('exec_prefix', '${prefix}')
+pkgconf.set('libdir', '${prefix}/@0@'.format(get_option('libdir')))
+pkgconf.set('includedir', '${prefix}/@0@'.format(get_option('includedir')))
+pkgconf.set('i915_perf_version', '1.5.1')
+
+configure_file(
+  input : 'i915-perf.pc.in',
+  output : 'i915-perf.pc',
+  configuration : pkgconf,
+  install_dir : pkgconfigdir)
+
+subdir('tests')
diff --git a/meson.build b/meson.build
index 3e937f5a..475680ac 100644
--- a/meson.build
+++ b/meson.build
@@ -121,7 +121,15 @@ build_info += 'With libdrm: ' + ','.join(libdrm_info)
 
 pciaccess = dependency('pciaccess', version : '>=0.10')
 libkmod = dependency('libkmod')
-libprocps = dependency('libprocps', required : true)
+libprocps = dependency('libprocps', required : false)
+libproc2 = dependency('libproc2', required : false)
+if libprocps.found()
+  config.set('HAVE_LIBPROCPS', 1)
+elif libproc2.found()
+  config.set('HAVE_LIBPROC2', 1)
+else
+  error('Either libprocps or libproc2 is required')
+endif
 
 libunwind = dependency('libunwind', required : get_option('libunwind'))
 build_info += 'With libunwind: @0@'.format(libunwind.found())
-- 
2.30.2

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [igt-dev] [PATCH i-g-t] Use the new procps library libproc2
  2022-11-17  8:41 [igt-dev] [PATCH i-g-t] Use the new procps library libproc2 Petri Latvala
@ 2022-11-17  8:46 ` Petri Latvala
  2022-11-17  9:05   ` Craig Small
  2022-11-17 10:06 ` [igt-dev] ✓ Fi.CI.BAT: success for " Patchwork
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 11+ messages in thread
From: Petri Latvala @ 2022-11-17  8:46 UTC (permalink / raw)
  To: igt-dev; +Cc: Craig Small

On Thu, Nov 17, 2022 at 10:41:29AM +0200, Petri Latvala wrote:
> From: Craig Small <csmall@dropbear.xyz>

Craig, I assumed you are the original author of this patch.

Need a commit message fleshed out, and if you are the author, this
needs your Signed-off-by line.


-- 
Petri Latvala



> 
> ---
>  lib/igt_aux.c        |  246 +++++-
>  lib/igt_aux.c.orig   | 1921 ++++++++++++++++++++++++++++++++++++++++++
>  lib/meson.build      |    7 +-
>  lib/meson.build.orig |  358 ++++++++
>  meson.build          |   10 +-
>  5 files changed, 2500 insertions(+), 42 deletions(-)
>  create mode 100644 lib/igt_aux.c.orig
>  create mode 100644 lib/meson.build.orig
> 
> diff --git a/lib/igt_aux.c b/lib/igt_aux.c
> index 15e30440..2aee1275 100644
> --- a/lib/igt_aux.c
> +++ b/lib/igt_aux.c
> @@ -52,8 +52,16 @@
>  #include <assert.h>
>  #include <grp.h>
>  
> +#ifdef HAVE_LIBPROCPS
>  #include <proc/readproc.h>
> +#endif
> +#ifdef HAVE_LIBPROC2
> +#include <libproc2/pids.h>
> +#endif
> +
>  #include <libudev.h>
> +#include <linux/limits.h>
> +#include <dirent.h>
>  
>  #include "drmtest.h"
>  #include "i915_drm.h"
> @@ -1217,6 +1225,7 @@ void igt_unlock_mem(void)
>   */
>  int igt_is_process_running(const char *comm)
>  {
> +#if HAVE_LIBPROCPS
>  	PROCTAB *proc;
>  	proc_t *proc_info;
>  	bool found = false;
> @@ -1235,6 +1244,26 @@ int igt_is_process_running(const char *comm)
>  
>  	closeproc(proc);
>  	return found;
> +#endif
> +#if HAVE_LIBPROC2
> +	enum pids_item Item[] = { PIDS_CMD };
> +	struct pids_info *info = NULL;
> +	struct pids_stack *stack;
> +	char *pid_comm;
> +	bool found = false;
> +
> +	if (procps_pids_new(&info, Item, 1) < 0)
> +	    return false;
> +	while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
> +	    pid_comm = PIDS_VAL(0, str, stack, info);
> +	    if (!strncasecmp(pid_comm, comm, strlen(pid_comm))) {
> +		found = true;
> +		break;
> +	    }
> +	}
> +	procps_pids_unref(&info);
> +	return found;
> +#endif
>  }
>  
>  /**
> @@ -1251,6 +1280,7 @@ int igt_is_process_running(const char *comm)
>   */
>  int igt_terminate_process(int sig, const char *comm)
>  {
> +#if HAVE_LIBPROCPS
>  	PROCTAB *proc;
>  	proc_t *proc_info;
>  	int err = 0;
> @@ -1272,6 +1302,29 @@ int igt_terminate_process(int sig, const char *comm)
>  
>  	closeproc(proc);
>  	return err;
> +#endif
> +#if HAVE_LIBPROC2
> +	enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD };
> +	struct pids_info *info = NULL;
> +	struct pids_stack *stack;
> +	char *pid_comm;
> +	int pid;
> +	int err = 0;
> +
> +	if (procps_pids_new(&info, Items, 2) < 0)
> +		return -errno;
> +	while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
> +		pid = PIDS_VAL(0, s_int, stack, info);
> +		pid_comm = PIDS_VAL(1, str, stack, info);
> +		if (!strncasecmp(pid_comm, comm, strlen(pid_comm))) {
> +			if (kill(pid, sig) < 0)
> +				err = -errno;
> +			break;
> +		}
> +	}
> +	procps_pids_unref(&info);
> +	return err;
> +#endif
>  }
>  
>  struct pinfo {
> @@ -1341,9 +1394,9 @@ igt_show_stat_header(void)
>  }
>  
>  static void
> -igt_show_stat(proc_t *info, int *state, const char *fn)
> +igt_show_stat(const pid_t tid, const char *cmd, int *state, const char *fn)
>  {
> -	struct pinfo p = { .pid = info->tid, .comm = info->cmd, .fn = fn };
> +	struct pinfo p = { .pid = tid, .comm = cmd, .fn = fn };
>  
>  	if (!*state)
>  		igt_show_stat_header();
> @@ -1353,7 +1406,7 @@ igt_show_stat(proc_t *info, int *state, const char *fn)
>  }
>  
>  static void
> -__igt_lsof_fds(proc_t *proc_info, int *state, char *proc_path, const char *dir)
> +__igt_lsof_fds(const pid_t tid, const char *cmd, int *state, char *proc_path, const char *dir)
>  {
>  	struct dirent *d;
>  	struct stat st;
> @@ -1400,7 +1453,7 @@ again:
>  		dirn = dirname(copy_fd_lnk);
>  
>  		if (!strncmp(dir, dirn, strlen(dir)))
> -			igt_show_stat(proc_info, state, fd_lnk);
> +			igt_show_stat(tid, cmd, state, fd_lnk);
>  
>  		free(copy_fd_lnk);
>  		free(fd_lnk);
> @@ -1416,13 +1469,14 @@ again:
>  static void
>  __igt_lsof(const char *dir)
>  {
> -	PROCTAB *proc;
> -	proc_t *proc_info;
> -
>  	char path[30];
>  	char *name_lnk;
>  	struct stat st;
>  	int state = 0;
> +#ifdef HAVE_LIBPROCPS
> +	PROCTAB *proc;
> +	proc_t *proc_info;
> +
>  
>  	proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
>  	igt_assert(proc != NULL);
> @@ -1443,19 +1497,57 @@ __igt_lsof(const char *dir)
>  		name_lnk[read] = '\0';
>  
>  		if (!strncmp(dir, name_lnk, strlen(dir)))
> -			igt_show_stat(proc_info, &state, name_lnk);
> +			igt_show_stat(proc_info->tid, proc_info->cmd, &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);
> +		__igt_lsof_fds(proc_info->tid, proc_info->cmd, &state, path, dir);
>  
>  		free(name_lnk);
>  		freeproc(proc_info);
>  	}
>  
>  	closeproc(proc);
> +#endif
> +#ifdef HAVE_LIBPROC2
> +	enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD };
> +	struct pids_info *info = NULL;
> +	struct pids_stack *stack;
> +
> +	if (procps_pids_new(&info, Items, 2) < 0)
> +		return;
> +	while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
> +		ssize_t read;
> +		int tid = PIDS_VAL(0, s_int, stack, info);
> +		char *pid_comm = PIDS_VAL(1, str, stack, info);
> +
> +		/* check current working directory */
> +		memset(path, 0, sizeof(path));
> +		snprintf(path, sizeof(path), "/proc/%d/cwd", 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(tid, pid_comm, &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", tid);
> +
> +		__igt_lsof_fds(tid, pid_comm, &state, path, dir);
> +
> +		free(name_lnk);
> +	}
> +	procps_pids_unref(&info);
> +#endif
>  }
>  
>  /**
> @@ -1490,7 +1582,7 @@ igt_lsof(const char *dpath)
>  	free(sanitized);
>  }
>  
> -static void pulseaudio_unload_module(proc_t *proc_info)
> +static void pulseaudio_unload_module(const uid_t euid, const gid_t egid)
>  {
>  	struct igt_helper_process pa_proc = {};
>  	char xdg_dir[PATH_MAX];
> @@ -1498,14 +1590,14 @@ static void pulseaudio_unload_module(proc_t *proc_info)
>  	struct passwd *pw;
>  
>  	igt_fork_helper(&pa_proc) {
> -		pw = getpwuid(proc_info->euid);
> +		pw = getpwuid(euid);
>  		homedir = pw->pw_dir;
> -		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", proc_info->euid);
> +		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", euid);
>  
>  		igt_info("Request pulseaudio to stop using audio device\n");
>  
> -		setgid(proc_info->egid);
> -		setuid(proc_info->euid);
> +		setgid(egid);
> +		setuid(euid);
>  		clearenv();
>  		setenv("HOME", homedir, 1);
>  		setenv("XDG_RUNTIME_DIR",xdg_dir, 1);
> @@ -1524,10 +1616,13 @@ static void pipewire_reserve_wait(void)
>  	char xdg_dir[PATH_MAX];
>  	const char *homedir;
>  	struct passwd *pw;
> -	proc_t *proc_info;
> -	PROCTAB *proc;
> +	int tid=0, euid, egid;
>  
> +#ifdef HAVE_LIBPROCPS
>  	igt_fork_helper(&pw_reserve_proc) {
> +		proc_t *proc_info;
> +		PROCTAB *proc;
> +
>  		igt_info("Preventing pipewire-pulse to use the audio drivers\n");
>  
>  		proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
> @@ -1540,19 +1635,44 @@ static void pipewire_reserve_wait(void)
>  		}
>  		closeproc(proc);
>  
> +		tid = proc_info->tid;
> +		euid = proc_info->euid;
> +		egid = proc_info->egid;
> +		freeproc(proc_info);
> +#endif
> +#ifdef HAVE_LIBPROC2
> +	igt_fork(child, 1) {
> +		enum pids_item Items[] = { PIDS_ID_PID, PIDS_ID_EUID, PIDS_ID_EGID };
> +		enum rel_items { EU_PID, EU_EUID, EU_EGID };
> +		struct pids_info *info = NULL;
> +		struct pids_stack *stack;
> +
> +		igt_info("Preventing pipewire-pulse to use the audio drivers\n");
> +
> +		if (procps_pids_new(&info, Items, 3) < 0)
> +		    return;
> +		while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
> +			tid = PIDS_VAL(EU_PID, s_int, stack, info);
> +			if (pipewire_pulse_pid == tid)
> +				break;
> +		}
> +		euid = PIDS_VAL(EU_EUID, s_int, stack, info);
> +		egid = PIDS_VAL(EU_EGID, s_int, stack, info);
> +		procps_pids_unref(&info);
> +#endif
> +
>  		/* Sanity check: if it can't find the process, it means it has gone */
> -		if (pipewire_pulse_pid != proc_info->tid)
> +		if (pipewire_pulse_pid != tid)
>  			exit(0);
>  
> -		pw = getpwuid(proc_info->euid);
> +		pw = getpwuid(euid);
>  		homedir = pw->pw_dir;
> -		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", proc_info->euid);
> -		setgid(proc_info->egid);
> -		setuid(proc_info->euid);
> +		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", euid);
> +		setgid(egid);
> +		setuid(euid);
>  		clearenv();
>  		setenv("HOME", homedir, 1);
>  		setenv("XDG_RUNTIME_DIR",xdg_dir, 1);
> -		freeproc(proc_info);
>  
>  		/*
>  		 * pw-reserve will run in background. It will only exit when
> @@ -1570,9 +1690,7 @@ static void pipewire_reserve_wait(void)
>  int pipewire_pulse_start_reserve(void)
>  {
>  	bool is_pw_reserve_running = false;
> -	proc_t *proc_info;
>  	int attempts = 0;
> -	PROCTAB *proc;
>  
>  	if (!pipewire_pulse_pid)
>  		return 0;
> @@ -1584,6 +1702,10 @@ int pipewire_pulse_start_reserve(void)
>  	 * pipewire version 0.3.50 or upper.
>  	 */
>  	for (attempts = 0; attempts < PIPEWIRE_RESERVE_MAX_TIME; attempts++) {
> +#ifdef HAVE_LIBPROCPS
> +		PROCTAB *proc;
> +		proc_t *proc_info;
> +
>  		usleep(1000);
>  		proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
>  		igt_assert(proc != NULL);
> @@ -1598,6 +1720,25 @@ int pipewire_pulse_start_reserve(void)
>  			freeproc(proc_info);
>  		}
>  		closeproc(proc);
> +#endif
> +#ifdef HAVE_LIBPROC2
> +		enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD };
> +		struct pids_info *info = NULL;
> +		struct pids_stack *stack;
> +
> +		usleep(1000);
> +
> +		if (procps_pids_new(&info, Items, 2) < 0)
> +			return 1;
> +		while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
> +			if (!strcmp(PIDS_VAL(1, str, stack, info), "pw-reserve")) {
> +				is_pw_reserve_running = true;
> +				pipewire_pw_reserve_pid = PIDS_VAL(0, s_int, stack, info);
> +				break;
> +			}
> +		}
> +		procps_pids_unref(&info);
> +#endif
>  		if (is_pw_reserve_running)
>  			break;
>  	}
> @@ -1645,7 +1786,7 @@ void pipewire_pulse_stop_reserve(void)
>   * 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)
> +__igt_lsof_audio_and_kill_proc(const pid_t tid, const char *cmd, const uid_t euid, const gid_t egid, char *proc_path)
>  {
>  	const char *audio_dev = "/dev/snd/";
>  	char path[PATH_MAX * 2];
> @@ -1670,10 +1811,10 @@ __igt_lsof_audio_and_kill_proc(proc_t *proc_info, char *proc_path)
>  	 * 2) unload/unbind the the audio driver(s);
>  	 * 3) stop the pw-reserve thread.
>  	 */
> -	if (!strcmp(proc_info->cmd, "pipewire-pulse")) {
> +	if (!strcmp(cmd, "pipewire-pulse")) {
>  		igt_info("process %d (%s) is using audio device. Should be requested to stop using them.\n",
> -			 proc_info->tid, proc_info->cmd);
> -		pipewire_pulse_pid = proc_info->tid;
> +			 tid, cmd);
> +		pipewire_pulse_pid = tid;
>  		return 0;
>  	}
>  	/*
> @@ -1685,9 +1826,9 @@ __igt_lsof_audio_and_kill_proc(proc_t *proc_info, char *proc_path)
>  	 * will respawn them. So, just ignore here, they'll honor pw-reserve,
>  	 * when the time comes.
>  	 */
> -	if (!strcmp(proc_info->cmd, "pipewire-media-session"))
> +	if (!strcmp(cmd, "pipewire-media-session"))
>  		return 0;
> -	if (!strcmp(proc_info->cmd, "wireplumber"))
> +	if (!strcmp(cmd, "wireplumber"))
>  		return 0;
>  
>  	dp = opendir(proc_path);
> @@ -1723,22 +1864,22 @@ __igt_lsof_audio_and_kill_proc(proc_t *proc_info, char *proc_path)
>  		 * 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);
> +		if (!strcmp(cmd, "pulseaudio")) {
> +			pulseaudio_unload_module(euid, egid);
>  			break;
>  		}
>  
>  		/* 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);
> +				tid, cmd);
>  
> -		if (kill(proc_info->tid, SIGTERM) < 0) {
> +		if (kill(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) {
> +				cmd, tid);
> +			if (kill(tid, SIGABRT) < 0) {
>  				fail++;
>  				igt_info("Fail to terminate %s (pid: %d) with SIGABRT\n",
> -					proc_info->cmd, proc_info->tid);
> +					cmd, tid);
>  			}
>  		}
>  
> @@ -1760,23 +1901,48 @@ int
>  igt_lsof_kill_audio_processes(void)
>  {
>  	char path[PATH_MAX];
> +	int fail = 0;
> +
> +#ifdef HAVE_LIBPROCPS
>  	proc_t *proc_info;
>  	PROCTAB *proc;
> -	int fail = 0;
>  
>  	proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
>  	igt_assert(proc != NULL);
>  	pipewire_pulse_pid = 0;
> -
>  	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);
> +			fail += __igt_lsof_audio_and_kill_proc(proc_info->tid, proc_info->cmd, proc_info->euid, proc_info->egid, path);
>  
>  		freeproc(proc_info);
>  	}
>  	closeproc(proc);
> +#endif
> +#ifdef HAVE_LIBPROC2
> +	enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD, PIDS_ID_EUID, PIDS_ID_EGID };
> +	enum rel_items { EU_PID, EU_CMD, EU_EUID, EU_EGID };
> +	struct pids_info *info = NULL;
> +	struct pids_stack *stack;
> +	pid_t tid;
> +
> +	if (procps_pids_new(&info, Items, 4) < 0)
> +		return 1;
> +	while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
> +		tid = PIDS_VAL(EU_PID, s_int, stack, info);
> +
> +		if (snprintf(path, sizeof(path), "/proc/%d/fd", tid) < 1)
> +			fail++;
> +		else
> +			fail += __igt_lsof_audio_and_kill_proc(tid,
> +				PIDS_VAL(EU_CMD, str, stack, info),
> +				PIDS_VAL(EU_EUID, s_int, stack, info),
> +				PIDS_VAL(EU_EGID, s_int, stack, info),
> +				path);
> +	}
> +	procps_pids_unref(&info);
> +#endif
>  
>  	return fail;
>  }
> diff --git a/lib/igt_aux.c.orig b/lib/igt_aux.c.orig
> new file mode 100644
> index 00000000..15e30440
> --- /dev/null
> +++ b/lib/igt_aux.c.orig
> @@ -0,0 +1,1921 @@
> +/*
> + * Copyright © 2007, 2011, 2013, 2014, 2015 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.
> + *
> + * Authors:
> + *    Eric Anholt <eric@anholt.net>
> + *    Daniel Vetter <daniel.vetter@ffwll.ch>
> + *
> + */
> +
> +#ifdef HAVE_LIBGEN_H
> +#include <libgen.h>
> +#endif
> +#include <stdio.h>
> +#include <fcntl.h>
> +#include <pwd.h>
> +#include <sys/stat.h>
> +#include <sys/ioctl.h>
> +#include <string.h>
> +#include <sys/mman.h>
> +#include <signal.h>
> +#include <pciaccess.h>
> +#include <stdlib.h>
> +#include <time.h>
> +#include <unistd.h>
> +#include <sys/poll.h>
> +#include <sys/wait.h>
> +#include <sys/resource.h>
> +#include <sys/time.h>
> +#include <sys/types.h>
> +#include <sys/syscall.h>
> +#include <sys/utsname.h>
> +#include <termios.h>
> +#include <assert.h>
> +#include <grp.h>
> +
> +#include <proc/readproc.h>
> +#include <libudev.h>
> +
> +#include "drmtest.h"
> +#include "i915_drm.h"
> +#include "intel_chipset.h"
> +#include "igt_aux.h"
> +#include "igt_debugfs.h"
> +#include "igt_gt.h"
> +#include "igt_params.h"
> +#include "igt_rand.h"
> +#include "igt_sysfs.h"
> +#include "config.h"
> +#include "intel_reg.h"
> +#include "ioctl_wrappers.h"
> +#include "igt_kms.h"
> +#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
> + * @title: aux
> + * @include: igt.h
> + *
> + * This library provides various auxiliary helper functions that don't really
> + * fit into any other topic.
> + */
> +
> +static struct __igt_sigiter_global {
> +	pid_t tid;
> +	timer_t timer;
> +	struct timespec offset;
> +	struct {
> +		long hit, miss;
> +		long ioctls, signals;
> +	} stat;
> +} __igt_sigiter;
> +
> +static void sigiter(int sig, siginfo_t *info, void *arg)
> +{
> +	__igt_sigiter.stat.signals++;
> +}
> +
> +#if 0
> +#define SIG_ASSERT(expr) igt_assert(expr)
> +#else
> +#define SIG_ASSERT(expr)
> +#endif
> +
> +static int
> +sig_ioctl(int fd, unsigned long request, void *arg)
> +{
> +	struct itimerspec its;
> +	int ret;
> +
> +	SIG_ASSERT(__igt_sigiter.timer);
> +	SIG_ASSERT(__igt_sigiter.tid == gettid());
> +
> +	memset(&its, 0, sizeof(its));
> +	if (timer_settime(__igt_sigiter.timer, 0, &its, NULL)) {
> +		/* oops, we didn't undo the interrupter (i.e. !unwound abort) */
> +		igt_ioctl = drmIoctl;
> +		return drmIoctl(fd, request, arg);
> +	}
> +
> +	its.it_value = __igt_sigiter.offset;
> +	do {
> +		long serial;
> +
> +		__igt_sigiter.stat.ioctls++;
> +
> +		ret = 0;
> +		serial = __igt_sigiter.stat.signals;
> +		igt_assert(timer_settime(__igt_sigiter.timer, 0, &its, NULL) == 0);
> +		if (ioctl(fd, request, arg))
> +			ret = errno;
> +		if (__igt_sigiter.stat.signals == serial)
> +			__igt_sigiter.stat.miss++;
> +		if (ret == 0)
> +			break;
> +
> +		if (ret == EINTR) {
> +			__igt_sigiter.stat.hit++;
> +
> +			its.it_value.tv_sec *= 2;
> +			its.it_value.tv_nsec *= 2;
> +			while (its.it_value.tv_nsec >= NSEC_PER_SEC) {
> +				its.it_value.tv_nsec -= NSEC_PER_SEC;
> +				its.it_value.tv_sec += 1;
> +			}
> +
> +			SIG_ASSERT(its.it_value.tv_nsec >= 0);
> +			SIG_ASSERT(its.it_value.tv_sec >= 0);
> +		}
> +	} while (ret == EAGAIN || ret == EINTR);
> +
> +	memset(&its, 0, sizeof(its));
> +	timer_settime(__igt_sigiter.timer, 0, &its, NULL);
> +
> +	errno = ret;
> +	return ret ? -1 : 0;
> +}
> +
> +static bool igt_sigiter_start(struct __igt_sigiter *iter, bool enable)
> +{
> +	/* Note that until we can automatically clean up on failed/skipped
> +	 * tests, we cannot assume the state of the igt_ioctl indirection.
> +	 */
> +	SIG_ASSERT(igt_ioctl == drmIoctl);
> +	igt_ioctl = drmIoctl;
> +
> +	if (enable) {
> +		struct timespec start, end;
> +		struct sigevent sev;
> +		struct sigaction act;
> +		struct itimerspec its;
> +
> +		igt_ioctl = sig_ioctl;
> +		__igt_sigiter.tid = gettid();
> +
> +		memset(&sev, 0, sizeof(sev));
> +		sev.sigev_notify = SIGEV_SIGNAL | SIGEV_THREAD_ID;
> +		sev.sigev_notify_thread_id = __igt_sigiter.tid;
> +		sev.sigev_signo = SIGRTMIN;
> +		igt_assert(timer_create(CLOCK_MONOTONIC, &sev, &__igt_sigiter.timer) == 0);
> +
> +		memset(&its, 0, sizeof(its));
> +		igt_assert(timer_settime(__igt_sigiter.timer, 0, &its, NULL) == 0);
> +
> +		memset(&act, 0, sizeof(act));
> +		act.sa_sigaction = sigiter;
> +		act.sa_flags = SA_SIGINFO;
> +		igt_assert(sigaction(SIGRTMIN, &act, NULL) == 0);
> +
> +		/* Try to find the approximate delay required to skip over
> +		 * the timer_setttime and into the following ioctl() to try
> +		 * and avoid the timer firing before we enter the drmIoctl.
> +		 */
> +		igt_assert(clock_gettime(CLOCK_MONOTONIC, &start) == 0);
> +		igt_assert(timer_settime(__igt_sigiter.timer, 0, &its, NULL) == 0);
> +		igt_assert(clock_gettime(CLOCK_MONOTONIC, &end) == 0);
> +
> +		__igt_sigiter.offset.tv_sec = end.tv_sec - start.tv_sec;
> +		__igt_sigiter.offset.tv_nsec = end.tv_nsec - start.tv_nsec;
> +		if (__igt_sigiter.offset.tv_nsec < 0) {
> +			__igt_sigiter.offset.tv_nsec += NSEC_PER_SEC;
> +			__igt_sigiter.offset.tv_sec -= 1;
> +		}
> +		if (__igt_sigiter.offset.tv_sec < 0) {
> +			__igt_sigiter.offset.tv_nsec = 0;
> +			__igt_sigiter.offset.tv_sec = 0;
> +		}
> +		igt_assert(__igt_sigiter.offset.tv_sec == 0);
> +
> +		igt_debug("Initial delay for interruption: %ld.%09lds\n",
> +			  __igt_sigiter.offset.tv_sec,
> +			  __igt_sigiter.offset.tv_nsec);
> +	}
> +
> +	return true;
> +}
> +
> +static bool igt_sigiter_stop(struct __igt_sigiter *iter, bool enable)
> +{
> +	if (enable) {
> +		struct sigaction act;
> +
> +		SIG_ASSERT(igt_ioctl == sig_ioctl);
> +		SIG_ASSERT(__igt_sigiter.tid == gettid());
> +		igt_ioctl = drmIoctl;
> +
> +		timer_delete(__igt_sigiter.timer);
> +
> +		memset(&act, 0, sizeof(act));
> +		act.sa_handler = SIG_IGN;
> +		sigaction(SIGRTMIN, &act, NULL);
> +
> +		memset(&__igt_sigiter, 0, sizeof(__igt_sigiter));
> +	}
> +
> +	memset(iter, 0, sizeof(*iter));
> +	return false;
> +}
> +
> +bool __igt_sigiter_continue(struct __igt_sigiter *iter, bool enable)
> +{
> +	if (iter->pass++ == 0)
> +		return igt_sigiter_start(iter, enable);
> +
> +	/* If nothing reported SIGINT, nothing will on the next pass, so
> +	 * give up! Also give up if everything is now executing faster
> +	 * than current sigtimer.
> +	 */
> +	if (__igt_sigiter.stat.hit == 0 ||
> +	    __igt_sigiter.stat.miss == __igt_sigiter.stat.ioctls)
> +		return igt_sigiter_stop(iter, enable);
> +
> +	igt_debug("%s: pass %d, missed %ld/%ld\n",
> +		  __func__, iter->pass - 1,
> +		  __igt_sigiter.stat.miss,
> +		  __igt_sigiter.stat.ioctls);
> +
> +	SIG_ASSERT(igt_ioctl == sig_ioctl);
> +	SIG_ASSERT(__igt_sigiter.timer);
> +
> +	__igt_sigiter.offset.tv_sec *= 2;
> +	__igt_sigiter.offset.tv_nsec *= 2;
> +	while (__igt_sigiter.offset.tv_nsec >= NSEC_PER_SEC) {
> +		__igt_sigiter.offset.tv_nsec -= NSEC_PER_SEC;
> +		__igt_sigiter.offset.tv_sec += 1;
> +	}
> +	SIG_ASSERT(__igt_sigiter.offset.tv_nsec >= 0);
> +	SIG_ASSERT(__igt_sigiter.offset.tv_sec >= 0);
> +
> +	memset(&__igt_sigiter.stat, 0, sizeof(__igt_sigiter.stat));
> +	return true;
> +}
> +
> +static struct igt_helper_process signal_helper;
> +long long int sig_stat;
> +__noreturn static void signal_helper_process(pid_t pid)
> +{
> +	/* Interrupt the parent process at 500Hz, just to be annoying */
> +	while (1) {
> +		usleep(1000 * 1000 / 500);
> +		if (kill(pid, SIGCONT)) /* Parent has died, so must we. */
> +			exit(0);
> +	}
> +}
> +
> +static void sig_handler(int i)
> +{
> +	sig_stat++;
> +}
> +
> +/**
> + * igt_fork_signal_helper:
> + *
> + * Fork a child process using #igt_fork_helper to interrupt the parent process
> + * with a SIGCONT signal at regular quick intervals. The corresponding dummy
> + * signal handler is installed in the parent process.
> + *
> + * This is useful to exercise ioctl error paths, at least where those can be
> + * exercises by interrupting blocking waits, like stalling for the gpu. This
> + * helper can also be used from children spawned with #igt_fork.
> + *
> + * In tests with subtests this function can be called outside of failure
> + * catching code blocks like #igt_fixture or #igt_subtest.
> + *
> + * Note that this just spews signals at the current process unconditionally and
> + * hence incurs quite a bit of overhead. For a more focused approach, with less
> + * overhead, look at the #igt_while_interruptible code block macro.
> + */
> +void igt_fork_signal_helper(void)
> +{
> +	if (igt_only_list_subtests())
> +		return;
> +
> +	/* We pick SIGCONT as it is a "safe" signal - if we send SIGCONT to
> +	 * an unexpecting process it spuriously wakes up and does nothing.
> +	 * Most other signals (e.g. SIGUSR1) cause the process to die if they
> +	 * are not handled. This is an issue in case the sighandler is not
> +	 * inherited correctly (or if there is a race in the inheritance
> +	 * and we send the signal at exactly the wrong time).
> +	 */
> +	signal(SIGCONT, sig_handler);
> +	setpgid(0, 0); /* define a new process group for the tests */
> +
> +	igt_fork_helper(&signal_helper) {
> +		setpgid(0, 0); /* Escape from the test process group */
> +
> +		/* Pass along the test process group identifier,
> +		 * negative pid => send signal to everyone in the group.
> +		 */
> +		signal_helper_process(-getppid());
> +	}
> +}
> +
> +/**
> + * igt_stop_signal_helper:
> + *
> + * Stops the child process spawned with igt_fork_signal_helper() again.
> + *
> + * In tests with subtests this function can be called outside of failure
> + * catching code blocks like #igt_fixture or #igt_subtest.
> + */
> +void igt_stop_signal_helper(void)
> +{
> +	if (igt_only_list_subtests())
> +		return;
> +
> +	igt_stop_helper(&signal_helper);
> +
> +	sig_stat = 0;
> +}
> +
> +/**
> + * igt_suspend_signal_helper:
> + *
> + * Suspends the child process spawned with igt_fork_signal_helper(). This
> + * should be called before a critical section of code that has difficulty to
> + * make progress if interrupted frequently, like the clone() syscall called
> + * from a largish executable. igt_resume_signal_helper() must be called after
> + * the critical section to restart interruptions for the test.
> + */
> +void igt_suspend_signal_helper(void)
> +{
> +	int status;
> +
> +	if (!signal_helper.running)
> +		return;
> +
> +	kill(signal_helper.pid, SIGSTOP);
> +	while (waitpid(signal_helper.pid, &status, WUNTRACED) == -1 &&
> +	       errno == EINTR)
> +		;
> +}
> +
> +/**
> + * igt_resume_signal_helper:
> + *
> + * Resumes the child process spawned with igt_fork_signal_helper().
> + *
> + * This should be paired with igt_suspend_signal_helper() and called after the
> + * problematic code sensitive to signals.
> + */
> +void igt_resume_signal_helper(void)
> +{
> +	if (!signal_helper.running)
> +		return;
> +
> +	kill(signal_helper.pid, SIGCONT);
> +}
> +
> +static struct igt_helper_process shrink_helper;
> +__noreturn static void shrink_helper_process(int fd, pid_t pid)
> +{
> +	while (1) {
> +		igt_drop_caches_set(fd, DROP_SHRINK_ALL);
> +		usleep(1000 * 1000 / 50);
> +		if (kill(pid, 0)) /* Parent has died, so must we. */
> +			exit(0);
> +	}
> +}
> +
> +/**
> + * igt_fork_shrink_helper:
> + *
> + * Fork a child process using #igt_fork_helper to force all available objects
> + * to be paged out (via i915_gem_shrink()).
> + *
> + * This is useful to exercise swapping paths, without requiring us to hit swap.
> + *
> + * This should only be used from an igt_fixture.
> + */
> +void igt_fork_shrink_helper(int drm_fd)
> +{
> +	assert(!igt_only_list_subtests());
> +	igt_require(igt_drop_caches_has(drm_fd, DROP_SHRINK_ALL));
> +	igt_fork_helper(&shrink_helper)
> +		shrink_helper_process(drm_fd, getppid());
> +}
> +
> +/**
> + * igt_stop_shrink_helper:
> + *
> + * Stops the child process spawned with igt_fork_shrink_helper().
> + */
> +void igt_stop_shrink_helper(void)
> +{
> +	igt_stop_helper(&shrink_helper);
> +}
> +
> +static void show_kernel_stack(pid_t pid)
> +{
> +	char buf[80], *str;
> +	int dir;
> +
> +	snprintf(buf, sizeof(buf), "/proc/%d", pid);
> +	dir = open(buf, O_RDONLY);
> +	if (dir < 0)
> +		return;
> +
> +	str = igt_sysfs_get(dir, "stack");
> +	if (str) {
> +		igt_debug("Kernel stack for pid %d:\n%s\n", pid, str);
> +		free(str);
> +	}
> +
> +	close(dir);
> +}
> +
> +static struct igt_helper_process hang_detector;
> +__noreturn static void
> +hang_detector_process(int fd, pid_t pid, dev_t rdev)
> +{
> +	struct udev_monitor *mon =
> +		udev_monitor_new_from_netlink(udev_new(), "kernel");
> +	struct pollfd pfd;
> +	int ret;
> +
> +	udev_monitor_filter_add_match_subsystem_devtype(mon, "drm", NULL);
> +	udev_monitor_enable_receiving(mon);
> +
> +	pfd.fd = udev_monitor_get_fd(mon);
> +	pfd.events = POLLIN;
> +
> +	while ((ret = poll(&pfd, 1, 2000)) >= 0) {
> +		struct udev_device *dev;
> +		dev_t devnum;
> +
> +		if (kill(pid, 0)) { /* Parent has died, so must we. */
> +			igt_warn("Parent died without killing its children (%s)\n",
> +				 __func__);
> +			break;
> +		}
> +
> +		dev = NULL;
> +		if (ret > 0)
> +			dev = udev_monitor_receive_device(mon);
> +		if (dev == NULL)
> +			continue;
> +
> +		devnum = udev_device_get_devnum(dev);
> +		if (memcmp(&rdev, &devnum, sizeof(dev_t)) == 0) {
> +			const char *str;
> +
> +			str = udev_device_get_property_value(dev, "ERROR");
> +			if (str && atoi(str) == 1) {
> +				show_kernel_stack(pid);
> +				kill(pid, SIGIO);
> +			}
> +		}
> +
> +		udev_device_unref(dev);
> +	}
> +
> +	exit(0);
> +}
> +
> +__noreturn static void sig_abort(int sig)
> +{
> +	errno = 0; /* inside a signal, last errno reporting is confusing */
> +	igt_assert(!"GPU hung");
> +}
> +
> +void igt_fork_hang_detector(int fd)
> +{
> +	struct stat st;
> +
> +	igt_assert(fstat(fd, &st) == 0);
> +
> +	/*
> +	 * Disable per-engine reset to force an error uevent. We don't
> +	 * expect to get any hangs whilst the detector is enabled (if we do
> +	 * they are a test failure!) and so the loss of per-engine reset
> +	 * functionality is not an issue.
> +	 */
> +	igt_assert(igt_params_set(fd, "reset", "%d", 1 /* only global reset */));
> +
> +	signal(SIGIO, sig_abort);
> +	igt_fork_helper(&hang_detector)
> +		hang_detector_process(fd, getppid(), st.st_rdev);
> +}
> +
> +void igt_stop_hang_detector(void)
> +{
> +	/*
> +	 * Give the uevent time to arrive. No sleep at all misses about 20% of
> +	 * hangs (at least, in the i915_hangman/detector test). A sleep of 1ms
> +	 * seems to miss about 2%, 10ms loses <1%, so 100ms should be safe.
> +	 */
> +	usleep(100 * 1000);
> +
> +	igt_stop_helper(&hang_detector);
> +}
> +
> +/**
> + * igt_check_boolean_env_var:
> + * @env_var: environment variable name
> + * @default_value: default value for the environment variable
> + *
> + * This function should be used to parse boolean environment variable options.
> + *
> + * Returns:
> + * The boolean value of the environment variable @env_var as decoded by atoi()
> + * if it is set and @default_value if the variable is not set.
> + */
> +bool igt_check_boolean_env_var(const char *env_var, bool default_value)
> +{
> +	char *val;
> +
> +	val = getenv(env_var);
> +	if (!val)
> +		return default_value;
> +
> +	return atoi(val) != 0;
> +}
> +
> +/**
> + * igt_aub_dump_enabled:
> + *
> + * Returns:
> + * True if AUB dumping is enabled with IGT_DUMP_AUB=1 in the environment, false
> + * otherwise.
> + */
> +bool igt_aub_dump_enabled(void)
> +{
> +	static int dump_aub = -1;
> +
> +	if (dump_aub == -1)
> +		dump_aub = igt_check_boolean_env_var("IGT_DUMP_AUB", false);
> +
> +	return dump_aub;
> +}
> +
> +/* other helpers */
> +/**
> + * igt_exchange_int:
> + * @array: pointer to the array of integers
> + * @i: first position
> + * @j: second position
> + *
> + * Exchanges the two values at array indices @i and @j. Useful as an exchange
> + * function for igt_permute_array().
> + */
> +void igt_exchange_int(void *array, unsigned i, unsigned j)
> +{
> +	int *int_arr, tmp;
> +	int_arr = array;
> +
> +	tmp = int_arr[i];
> +	int_arr[i] = int_arr[j];
> +	int_arr[j] = tmp;
> +}
> +
> +/**
> + * igt_exchange_int64:
> + * @array: pointer to the array of int64_t
> + * @i: first position
> + * @j: second position
> + *
> + * Exchanges the two values at array indices @i and @j. Useful as an exchange
> + * function for igt_permute_array().
> + */
> +void igt_exchange_int64(void *array, unsigned i, unsigned j)
> +{
> +	int64_t *a = array;
> +
> +	igt_swap(a[i], a[j]);
> +}
> +
> +/**
> + * igt_permute_array:
> + * @array: pointer to array
> + * @size: size of the array
> + * @exchange_func: function to exchange array elements
> + *
> + * This function randomly permutes the array using random() as the PRNG source.
> + * The @exchange_func function is called to exchange two elements in the array
> + * when needed.
> + */
> +void igt_permute_array(void *array, unsigned size,
> +                       void (*exchange_func)(void *array,
> +                                             unsigned i,
> +                                             unsigned j))
> +{
> +	int i;
> +
> +	for (i = size - 1; i > 0; i--) {
> +		/* yes, not perfectly uniform, who cares */
> +		long l = hars_petruska_f54_1_random_unsafe() % (i +1);
> +		if (i != l)
> +			exchange_func(array, i, l);
> +	}
> +}
> +
> +__attribute__((format(printf, 1, 2)))
> +static void igt_interactive_info(const char *format, ...)
> +{
> +	va_list args;
> +
> +	if (!isatty(STDERR_FILENO) || __igt_plain_output) {
> +		errno = 0; /* otherwise would be either ENOTTY or EBADF */
> +		return;
> +	}
> +
> +	if (igt_log_level > IGT_LOG_INFO)
> +		return;
> +
> +	va_start(args, format);
> +	vfprintf(stderr, format, args);
> +	va_end(args);
> +}
> +
> +
> +/**
> + * igt_progress:
> + * @header: header string to prepend to the progress indicator
> + * @i: work processed thus far
> + * @total: total amount of work
> + *
> + * This function draws a progress indicator, which is useful for running
> + * long-winded tests manually on the console. To avoid spamming log files in
> + * automated runs the progress indicator is suppressed when not running on a
> + * terminal.
> + */
> +void igt_progress(const char *header, uint64_t i, uint64_t total)
> +{
> +	int divider = 200;
> +
> +	if (i+1 >= total) {
> +		igt_interactive_info("\r%s100%%\n", header);
> +		return;
> +	}
> +
> +	if (total / 200 == 0)
> +		divider = 1;
> +
> +	/* only bother updating about every 0.5% */
> +	if (i % (total / divider) == 0)
> +		igt_interactive_info("\r%s%3llu%%", header,
> +				     (long long unsigned)i * 100 / total);
> +}
> +
> +/**
> + * igt_print_activity:
> + *
> + * Print a '.' to indicate activity. This is printed without a newline and
> + * only if output is to a terminal.
> + */
> +void igt_print_activity(void)
> +{
> +	igt_interactive_info(".");
> +}
> +
> +static int autoresume_delay;
> +
> +static const char *suspend_state_name[] = {
> +	[SUSPEND_STATE_FREEZE] = "freeze",
> +	[SUSPEND_STATE_STANDBY] = "standby",
> +	[SUSPEND_STATE_S3] = "mem", /* Forces Suspend-to-Ram (S3) */
> +	[SUSPEND_STATE_MEM] = "mem", /* Respects system default */
> +	[SUSPEND_STATE_DISK] = "disk",
> +};
> +
> +static const char *suspend_test_name[] = {
> +	[SUSPEND_TEST_NONE] = "none",
> +	[SUSPEND_TEST_FREEZER] = "freezer",
> +	[SUSPEND_TEST_DEVICES] = "devices",
> +	[SUSPEND_TEST_PLATFORM] = "platform",
> +	[SUSPEND_TEST_PROCESSORS] = "processors",
> +	[SUSPEND_TEST_CORE] = "core",
> +};
> +
> +static const char *mem_sleep_name[] = {
> +	[MEM_SLEEP_S2IDLE] = "s2idle",
> +	[MEM_SLEEP_SHALLOW] = "shallow",
> +	[MEM_SLEEP_DEEP] = "deep"
> +};
> +
> +static enum igt_suspend_test get_suspend_test(int power_dir)
> +{
> +	char *test_line;
> +	char *test_name;
> +	enum igt_suspend_test test;
> +
> +	if (faccessat(power_dir, "pm_test", R_OK, 0))
> +		return SUSPEND_TEST_NONE;
> +
> +	igt_assert((test_line = igt_sysfs_get(power_dir, "pm_test")));
> +	for (test_name = strtok(test_line, " "); test_name;
> +	     test_name = strtok(NULL, " "))
> +		if (test_name[0] == '[') {
> +			test_name[strlen(test_name) - 1] = '\0';
> +			test_name++;
> +			break;
> +		}
> +
> +	if (!test_name) {
> +	  	free(test_line);
> +		return SUSPEND_TEST_NONE;
> +	}
> +
> +	for (test = SUSPEND_TEST_NONE; test < SUSPEND_TEST_NUM; test++)
> +		if (strcmp(suspend_test_name[test], test_name) == 0)
> +			break;
> +
> +	igt_assert(test < SUSPEND_TEST_NUM);
> +
> +	free(test_line);
> +	return test;
> +}
> +
> +static void set_suspend_test(int power_dir, enum igt_suspend_test test)
> +{
> +	igt_assert(test < SUSPEND_TEST_NUM);
> +
> +	if (faccessat(power_dir, "pm_test", W_OK, 0)) {
> +		igt_require(test == SUSPEND_TEST_NONE);
> +		return;
> +	}
> +
> +	igt_assert(igt_sysfs_set(power_dir, "pm_test", suspend_test_name[test]));
> +}
> +
> +#define SQUELCH ">/dev/null 2>&1"
> +
> +static void suspend_via_rtcwake(enum igt_suspend_state state)
> +{
> +	char cmd[128];
> +	int delay, ret;
> +
> +	igt_assert(state < SUSPEND_STATE_NUM);
> +
> +	delay = igt_get_autoresume_delay(state);
> +
> +	/*
> +	 * Skip if rtcwake would fail for a reason not related to the kernel's
> +	 * suspend functionality.
> +	 */
> +	snprintf(cmd, sizeof(cmd), "rtcwake -n -s %d -m %s " SQUELCH,
> +		 delay, suspend_state_name[state]);
> +	ret = igt_system(cmd);
> +	igt_require_f(ret == 0, "rtcwake test failed with %i\n"
> +		     "This failure could mean that something is wrong with "
> +		     "the rtcwake tool or how your distro is set up.\n",
> +		      ret);
> +
> +	snprintf(cmd, sizeof(cmd), "rtcwake -s %d -m %s ",
> +		 delay, suspend_state_name[state]);
> +	ret = igt_system(cmd);
> +	if (ret) {
> +		const char *path = "suspend_stats";
> +		char *info;
> +		int dir;
> +
> +		igt_warn("rtcwake failed with %i\n"
> +			 "Check dmesg for further details.\n",
> +			 ret);
> +
> +		dir = open(igt_debugfs_mount(), O_RDONLY);
> +		info = igt_sysfs_get(dir, path);
> +		close(dir);
> +		if (info) {
> +			igt_debug("%s:\n%s\n", path, info);
> +			free(info);
> +		}
> +	}
> +	igt_assert_eq(ret, 0);
> +}
> +
> +static void suspend_via_sysfs(int power_dir, enum igt_suspend_state state)
> +{
> +	igt_assert(state < SUSPEND_STATE_NUM);
> +	igt_assert(igt_sysfs_set(power_dir, "state",
> +				 suspend_state_name[state]));
> +}
> +
> +static bool is_state_supported(int power_dir, enum igt_suspend_state state)
> +{
> +	const char *str;
> +	char *states;
> +
> +	igt_assert((states = igt_sysfs_get(power_dir, "state")));
> +
> +	str = strstr(states, suspend_state_name[state]);
> +
> +	if (!str)
> +		igt_info("State %s not supported.\nSupported States: %s\n",
> +			 suspend_state_name[state], states);
> +
> +	free(states);
> +	return str;
> +}
> +
> +static int get_mem_sleep(void)
> +{
> +	char *mem_sleep_states;
> +	char *mem_sleep_state;
> +	enum igt_mem_sleep mem_sleep;
> +	int power_dir;
> +
> +	igt_require((power_dir = open("/sys/power", O_RDONLY)) >= 0);
> +
> +	if (faccessat(power_dir, "mem_sleep", R_OK, 0))
> +		return MEM_SLEEP_NONE;
> +
> +	igt_assert((mem_sleep_states = igt_sysfs_get(power_dir, "mem_sleep")));
> +	for (mem_sleep_state = strtok(mem_sleep_states, " "); mem_sleep_state;
> +	     mem_sleep_state = strtok(NULL, " ")) {
> +		if (mem_sleep_state[0] == '[') {
> +			mem_sleep_state[strlen(mem_sleep_state) - 1] = '\0';
> +			mem_sleep_state++;
> +			break;
> +		}
> +	}
> +
> +	if (!mem_sleep_state) {
> +		free(mem_sleep_states);
> +		return MEM_SLEEP_NONE;
> +	}
> +
> +	for (mem_sleep = MEM_SLEEP_S2IDLE; mem_sleep < MEM_SLEEP_NUM; mem_sleep++) {
> +		if (strcmp(mem_sleep_name[mem_sleep], mem_sleep_state) == 0)
> +			break;
> +	}
> +
> +	igt_assert_f(mem_sleep < MEM_SLEEP_NUM, "Invalid mem_sleep state\n");
> +
> +	free(mem_sleep_states);
> +	close(power_dir);
> +	return mem_sleep;
> +}
> +
> +static void set_mem_sleep(int power_dir, enum igt_mem_sleep sleep)
> +{
> +	igt_assert(sleep < MEM_SLEEP_NUM);
> +
> +	igt_assert_eq(faccessat(power_dir, "mem_sleep", W_OK, 0), 0);
> +
> +	igt_assert(igt_sysfs_set(power_dir, "mem_sleep",
> +				 mem_sleep_name[sleep]));
> +}
> +
> +static bool is_mem_sleep_state_supported(int power_dir, enum igt_mem_sleep state)
> +{
> +	const char *str;
> +	char *mem_sleep_states;
> +
> +	igt_assert((mem_sleep_states = igt_sysfs_get(power_dir, "mem_sleep")));
> +
> +	str = strstr(mem_sleep_states, mem_sleep_name[state]);
> +
> +	if (!str)
> +		igt_info("mem_sleep state %s not supported.\nSupported mem_sleep states: %s\n",
> +			 mem_sleep_name[state], mem_sleep_states);
> +
> +	free(mem_sleep_states);
> +	return str;
> +}
> +
> +/**
> + * igt_system_suspend_autoresume:
> + * @state: an #igt_suspend_state, the target suspend state
> + * @test: an #igt_suspend_test, test point at which to complete the suspend
> + *	  cycle
> + *
> + * Execute a system suspend cycle targeting the given @state optionally
> + * completing the cycle at the given @test point and automaically wake up
> + * again. Waking up is either achieved using the RTC wake-up alarm for a full
> + * suspend cycle or a kernel timer for a suspend test cycle. The kernel timer
> + * delay for a test cycle can be configured by the suspend.pm_test_delay
> + * kernel parameter (5 sec by default).
> + *
> + * #SUSPEND_TEST_NONE specifies a full suspend cycle.
> + * The #SUSPEND_TEST_FREEZER..#SUSPEND_TEST_CORE test points can make it
> + * possible to collect error logs in case a full suspend cycle would prevent
> + * this by hanging the machine, or they can provide an idea of the faulty
> + * component by comparing fail/no-fail results at different test points.
> + *
> + * This is very handy for implementing any kind of suspend/resume test.
> + */
> +void igt_system_suspend_autoresume(enum igt_suspend_state state,
> +				   enum igt_suspend_test test)
> +{
> +	int power_dir;
> +	enum igt_suspend_test orig_test;
> +	enum igt_mem_sleep orig_mem_sleep = MEM_SLEEP_NONE;
> +
> +	igt_require((power_dir = open("/sys/power", O_RDONLY)) >= 0);
> +	igt_require(is_state_supported(power_dir, state));
> +	igt_require(test == SUSPEND_TEST_NONE ||
> +		    faccessat(power_dir, "pm_test", R_OK | W_OK, 0) == 0);
> +
> +	igt_skip_on_f(state == SUSPEND_STATE_DISK &&
> +		      !igt_get_total_swap_mb(),
> +		      "Suspend to disk requires swap space.\n");
> +
> +	orig_test = get_suspend_test(power_dir);
> +
> +	if (state == SUSPEND_STATE_S3) {
> +		orig_mem_sleep = get_mem_sleep();
> +		igt_skip_on_f(!is_mem_sleep_state_supported(power_dir, MEM_SLEEP_DEEP),
> +			      "S3 not supported in this system.\n");
> +		set_mem_sleep(power_dir, MEM_SLEEP_DEEP);
> +		igt_skip_on_f(get_mem_sleep() != MEM_SLEEP_DEEP,
> +			      "S3 not possible in this system.\n");
> +	}
> +
> +	set_suspend_test(power_dir, test);
> +
> +	if (test == SUSPEND_TEST_NONE)
> +		suspend_via_rtcwake(state);
> +	else
> +		suspend_via_sysfs(power_dir, state);
> +
> +	if (orig_mem_sleep)
> +		set_mem_sleep(power_dir, orig_mem_sleep);
> +
> +	set_suspend_test(power_dir, orig_test);
> +	close(power_dir);
> +}
> +
> +static int original_autoresume_delay;
> +
> +static void igt_restore_autoresume_delay(int sig)
> +{
> +	int delay_fd;
> +	char delay_str[10];
> +
> +	igt_require((delay_fd = open("/sys/module/suspend/parameters/pm_test_delay",
> +				    O_WRONLY)) >= 0);
> +
> +	snprintf(delay_str, sizeof(delay_str), "%d", original_autoresume_delay);
> +	igt_require(write(delay_fd, delay_str, strlen(delay_str)));
> +
> +	close(delay_fd);
> +}
> +
> +/**
> + * igt_set_autoresume_delay:
> + * @delay_secs: The delay in seconds before resuming the system
> + *
> + * Sets how long we wait to resume the system after suspending it, using the
> + * suspend.pm_test_delay variable. On exit, the original delay value is
> + * restored.
> + */
> +void igt_set_autoresume_delay(int delay_secs)
> +{
> +	int delay_fd;
> +	char delay_str[10];
> +
> +	delay_fd = open("/sys/module/suspend/parameters/pm_test_delay", O_RDWR);
> +
> +	if (delay_fd >= 0) {
> +		if (!original_autoresume_delay) {
> +			igt_require(read(delay_fd, delay_str,
> +					 sizeof(delay_str)));
> +			original_autoresume_delay = atoi(delay_str);
> +			igt_install_exit_handler(igt_restore_autoresume_delay);
> +		}
> +
> +		snprintf(delay_str, sizeof(delay_str), "%d", delay_secs);
> +		igt_require(write(delay_fd, delay_str, strlen(delay_str)));
> +
> +		close(delay_fd);
> +	}
> +
> +	autoresume_delay = delay_secs;
> +}
> +
> +/**
> + * igt_get_autoresume_delay:
> + * @state: an #igt_suspend_state, the target suspend state
> + *
> + * Retrieves how long we wait to resume the system after suspending it.
> + * This can either be set through igt_set_autoresume_delay or be a default
> + * value that depends on the suspend state.
> + *
> + * Returns: The autoresume delay, in seconds.
> + */
> +int igt_get_autoresume_delay(enum igt_suspend_state state)
> +{
> +	int delay;
> +
> +	if (autoresume_delay)
> +		delay = autoresume_delay;
> +	else
> +		delay = state == SUSPEND_STATE_DISK ? 30 : 15;
> +
> +	return delay;
> +}
> +
> +/**
> + * igt_drop_root:
> + *
> + * Drop root privileges and make sure it actually worked. Useful for tests
> + * which need to check security constraints. Note that this should only be
> + * called from manually forked processes, since the lack of root privileges
> + * will wreak havoc with the automatic cleanup handlers.
> + */
> +void igt_drop_root(void)
> +{
> +	igt_assert_eq(getuid(), 0);
> +
> +	igt_assert_eq(setgroups(0, NULL), 0);
> +	igt_assert_eq(setgid(2), 0);
> +	igt_assert_eq(setuid(2), 0);
> +
> +	igt_assert_eq(getgroups(0, NULL), 0);
> +	igt_assert_eq(getgid(), 2);
> +	igt_assert_eq(getuid(), 2);
> +}
> +
> +/**
> + * igt_debug_wait_for_keypress:
> + * @var: var lookup to to enable this wait
> + *
> + * Waits for a key press when run interactively and when the corresponding debug
> + * var is set in the --interactive-debug=$var variable. Multiple keys
> + * can be specified as a comma-separated list or alternatively "all" if a wait
> + * should happen for all cases. Calling this function with "all" will assert.
> + *
> + * When not connected to a terminal interactive_debug is ignored
> + * and execution immediately continues.
> + *
> + * This is useful for display tests where under certain situation manual
> + * inspection of the display is useful. Or when running a testcase in the
> + * background.
> + */
> +void igt_debug_wait_for_keypress(const char *var)
> +{
> +	struct termios oldt, newt;
> +
> +	if (!isatty(STDIN_FILENO)) {
> +		errno = 0; /* otherwise would be either ENOTTY or EBADF */
> +		return;
> +	}
> +
> +	if (!igt_interactive_debug)
> +		return;
> +
> +	if (strstr(var, "all"))
> +		igt_assert_f(false, "Bug in test: Do not call igt_debug_wait_for_keypress with \"all\"\n");
> +
> +	if (!strstr(igt_interactive_debug, var) &&
> +	    !strstr(igt_interactive_debug, "all"))
> +		return;
> +
> +	igt_info("Press any key to continue ...\n");
> +
> +	tcgetattr ( STDIN_FILENO, &oldt );
> +	newt = oldt;
> +	newt.c_lflag &= ~( ICANON | ECHO );
> +	tcsetattr ( STDIN_FILENO, TCSANOW, &newt );
> +	getchar();
> +	tcsetattr ( STDIN_FILENO, TCSANOW, &oldt );
> +}
> +
> +/**
> + * igt_debug_interactive_mode_check:
> + * @var: var lookup to to enable this wait
> + * @expected: message to be printed as expected behaviour before wait for keys Y/n
> + *
> + * Waits for a key press when run interactively and when the corresponding debug
> + * var is set in the --interactive-debug=$var variable. Multiple vars
> + * can be specified as a comma-separated list or alternatively "all" if a wait
> + * should happen for all cases.
> + *
> + * This is useful for display tests where under certain situation manual
> + * inspection of the display is useful. Or when running a testcase in the
> + * background.
> + *
> + * When not connected to a terminal interactive_debug is ignored
> + * and execution immediately continues. For this reason by default this function
> + * returns true. It returns false only when N/n is pressed indicating the
> + * user isn't seeing what was expected.
> + *
> + * Force test fail when N/n is pressed.
> + */
> +void igt_debug_interactive_mode_check(const char *var, const char *expected)
> +{
> +	struct termios oldt, newt;
> +	char key;
> +
> +	if (!isatty(STDIN_FILENO)) {
> +		errno = 0; /* otherwise would be either ENOTTY or EBADF */
> +		return;
> +	}
> +
> +	if (!igt_interactive_debug)
> +		return;
> +
> +	if (!strstr(igt_interactive_debug, var) &&
> +	    !strstr(igt_interactive_debug, "all"))
> +		return;
> +
> +	igt_info("Is %s [Y/n]", expected);
> +
> +	tcgetattr ( STDIN_FILENO, &oldt );
> +	newt = oldt;
> +	newt.c_lflag &= ~ICANON;
> +	tcsetattr ( STDIN_FILENO, TCSANOW, &newt );
> +	key = getchar();
> +	tcsetattr ( STDIN_FILENO, TCSANOW, &oldt );
> +
> +	igt_info("\n");
> +
> +	igt_assert(key != 'n' && key != 'N');
> +}
> +
> +/**
> + * igt_lock_mem:
> + * @size: the amount of memory to lock into RAM, in MB
> + *
> + * Allocate @size MB of memory and lock it into RAM. This releases any
> + * previously locked memory.
> + *
> + * Use #igt_unlock_mem to release the currently locked memory.
> + */
> +static char *locked_mem;
> +static size_t locked_size;
> +
> +void igt_lock_mem(size_t size)
> +{
> +	long pagesize = sysconf(_SC_PAGESIZE);
> +	size_t i;
> +	int ret;
> +
> +	if (size == 0) {
> +		return;
> +	}
> +
> +	if (locked_mem) {
> +		igt_unlock_mem();
> +		igt_warn("Unlocking previously locked memory.\n");
> +	}
> +
> +	locked_size = size * 1024 * 1024;
> +
> +	locked_mem = malloc(locked_size);
> +	igt_require_f(locked_mem,
> +		      "Could not malloc %zdMiB for locking.\n", size);
> +
> +	/* write into each page to ensure it is allocated */
> +	for (i = 0; i < locked_size; i += pagesize)
> +		locked_mem[i] = i;
> +
> +	ret = mlock(locked_mem, locked_size);
> +	igt_assert_f(ret == 0, "Could not mlock %zdMiB.\n", size);
> +}
> +
> +/**
> + * igt_unlock_mem:
> + *
> + * Release and free the RAM used by #igt_lock_mem.
> + */
> +void igt_unlock_mem(void)
> +{
> +	if (!locked_mem)
> +		return;
> +
> +	munlock(locked_mem, locked_size);
> +
> +	free(locked_mem);
> +	locked_mem = NULL;
> +}
> +
> +/**
> + * igt_is_process_running:
> + * @comm: Name of process in the form found in /proc/pid/comm (limited to 15
> + * chars)
> + *
> + * Returns: true in case the process has been found, false otherwise.
> + *
> + * This function checks in the process table for an entry with the name @comm.
> + */
> +int igt_is_process_running(const char *comm)
> +{
> +	PROCTAB *proc;
> +	proc_t *proc_info;
> +	bool found = false;
> +
> +	proc = openproc(PROC_FILLCOM | PROC_FILLSTAT);
> +	igt_assert(proc != NULL);
> +
> +	while ((proc_info = readproc(proc, NULL))) {
> +		if (!strncasecmp(proc_info->cmd, comm, sizeof(proc_info->cmd))) {
> +			freeproc(proc_info);
> +			found = true;
> +			break;
> +		}
> +		freeproc(proc_info);
> +	}
> +
> +	closeproc(proc);
> +	return found;
> +}
> +
> +/**
> + * igt_terminate_process:
> + * @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_terminate_process(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)
> +{
> +	struct pinfo p = { .pid = info->tid, .comm = info->cmd, .fn = fn };
> +
> +	if (!*state)
> +		igt_show_stat_header();
> +
> +	__igt_show_stat(&p);
> +	++*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);
> +	}
> +
> +	closedir(dp);
> +}
> +
> +/*
> + * 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[30];
> +	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_warn("%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 void pulseaudio_unload_module(proc_t *proc_info)
> +{
> +	struct igt_helper_process pa_proc = {};
> +	char xdg_dir[PATH_MAX];
> +	const char *homedir;
> +	struct passwd *pw;
> +
> +	igt_fork_helper(&pa_proc) {
> +		pw = getpwuid(proc_info->euid);
> +		homedir = pw->pw_dir;
> +		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", proc_info->euid);
> +
> +		igt_info("Request 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_wait_helper(&pa_proc);
> +}
> +
> +static int pipewire_pulse_pid = 0;
> +static int pipewire_pw_reserve_pid = 0;
> +static struct igt_helper_process pw_reserve_proc = {};
> +
> +static void pipewire_reserve_wait(void)
> +{
> +	char xdg_dir[PATH_MAX];
> +	const char *homedir;
> +	struct passwd *pw;
> +	proc_t *proc_info;
> +	PROCTAB *proc;
> +
> +	igt_fork_helper(&pw_reserve_proc) {
> +		igt_info("Preventing pipewire-pulse to use the audio drivers\n");
> +
> +		proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
> +		igt_assert(proc != NULL);
> +
> +		while ((proc_info = readproc(proc, NULL))) {
> +			if (pipewire_pulse_pid == proc_info->tid)
> +				break;
> +			freeproc(proc_info);
> +		}
> +		closeproc(proc);
> +
> +		/* Sanity check: if it can't find the process, it means it has gone */
> +		if (pipewire_pulse_pid != proc_info->tid)
> +			exit(0);
> +
> +		pw = getpwuid(proc_info->euid);
> +		homedir = pw->pw_dir;
> +		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", proc_info->euid);
> +		setgid(proc_info->egid);
> +		setuid(proc_info->euid);
> +		clearenv();
> +		setenv("HOME", homedir, 1);
> +		setenv("XDG_RUNTIME_DIR",xdg_dir, 1);
> +		freeproc(proc_info);
> +
> +		/*
> +		 * pw-reserve will run in background. It will only exit when
> +		 * igt_kill_children() is called later on. So, it shouldn't
> +		 * call igt_waitchildren(). Instead, just exit with the return
> +		 * code from pw-reserve.
> +		 */
> +		exit(system("pw-reserve -n Audio0 -r"));
> +	}
> +}
> +
> +/* Maximum time waiting for pw-reserve to start running */
> +#define PIPEWIRE_RESERVE_MAX_TIME 1000 /* milisseconds */
> +
> +int pipewire_pulse_start_reserve(void)
> +{
> +	bool is_pw_reserve_running = false;
> +	proc_t *proc_info;
> +	int attempts = 0;
> +	PROCTAB *proc;
> +
> +	if (!pipewire_pulse_pid)
> +		return 0;
> +
> +	pipewire_reserve_wait();
> +
> +	/*
> +	 * Note: using pw-reserve to stop using audio only works with
> +	 * pipewire version 0.3.50 or upper.
> +	 */
> +	for (attempts = 0; attempts < PIPEWIRE_RESERVE_MAX_TIME; attempts++) {
> +		usleep(1000);
> +		proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
> +		igt_assert(proc != NULL);
> +
> +		while ((proc_info = readproc(proc, NULL))) {
> +			if (!strcmp(proc_info->cmd, "pw-reserve")) {
> +				is_pw_reserve_running = true;
> +				pipewire_pw_reserve_pid = proc_info->tid;
> +				freeproc(proc_info);
> +				break;
> +			}
> +			freeproc(proc_info);
> +		}
> +		closeproc(proc);
> +		if (is_pw_reserve_running)
> +			break;
> +	}
> +	if (!is_pw_reserve_running) {
> +		igt_warn("Failed to remove audio drivers from pipewire\n");
> +		return 1;
> +	}
> +	/* Let's grant some time for pw_reserve to notify pipewire via D-BUS */
> +	usleep(50000);
> +
> +	/*
> +	 * pw-reserve is running, and should have stopped using the audio
> +	 * drivers. We can now remove the driver.
> +	 */
> +
> +	return 0;
> +}
> +
> +void pipewire_pulse_stop_reserve(void)
> +{
> +	if (!pipewire_pulse_pid)
> +		return;
> +
> +	igt_stop_helper(&pw_reserve_proc);
> +}
> +
> +/**
> + * __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
> + * @pipewire_pulse_pid: PID of pipewire-pulse process
> + *
> + * 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 process is pipewire-pulse, it can't be killed, as systemd will
> + *	  respawn it. So, instead, the caller should call pw-reserve, remove
> + *	  the kernel driver and then stop pw-reserve. On such case, this
> + *	  function returns the PID of pipewire-pulse, but won't touch it.
> + * 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;
> +
> +	/*
> +	 * Terminating pipewire-pulse require an special procedure, which
> +	 * is only available at version 0.3.50 and upper. 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.
> +	 */
> +	if (!strcmp(proc_info->cmd, "pipewire-pulse")) {
> +		igt_info("process %d (%s) is using audio device. Should be requested to stop using them.\n",
> +			 proc_info->tid, proc_info->cmd);
> +		pipewire_pulse_pid = proc_info->tid;
> +		return 0;
> +	}
> +	/*
> +	 * pipewire-pulse itself doesn't hook into a /dev/snd device. Instead,
> +	 * the actual binding happens at the Pipewire Session Manager, e.g.
> +	 * either wireplumber or pipewire media-session.
> +	 *
> +	 * Just killing such processes won't produce any effect, as systemd
> +	 * will respawn them. So, just ignore here, they'll honor pw-reserve,
> +	 * when the time comes.
> +	 */
> +	if (!strcmp(proc_info->cmd, "pipewire-media-session"))
> +		return 0;
> +	if (!strcmp(proc_info->cmd, "wireplumber"))
> +		return 0;
> +
> +	dp = opendir(proc_path);
> +	if (!dp && errno == ENOENT)
> +		return 0;
> +	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;
> +		}
> +
> +		/* 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);
> +	pipewire_pulse_pid = 0;
> +
> +	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;
> +	struct sigaction oldact;
> +	struct igt_mean mean;
> +
> +	int sig;
> +} igt_siglatency;
> +
> +static long delay(void)
> +{
> +	return hars_petruska_f54_1_random_unsafe() % (NSEC_PER_SEC / 1000);
> +}
> +
> +static double elapsed(const struct timespec *now, const struct timespec *last)
> +{
> +	double nsecs;
> +
> +	nsecs = now->tv_nsec - last ->tv_nsec;
> +	nsecs += 1e9*(now->tv_sec - last->tv_sec);
> +
> +	return nsecs;
> +}
> +
> +static void siglatency(int sig, siginfo_t *info, void *arg)
> +{
> +	struct itimerspec its;
> +
> +	clock_gettime(CLOCK_MONOTONIC, &its.it_value);
> +	if (info)
> +		igt_mean_add(&igt_siglatency.mean,
> +			     elapsed(&its.it_value, &igt_siglatency.target));
> +	igt_siglatency.target = its.it_value;
> +
> +	its.it_value.tv_nsec += 100 * 1000;
> +	its.it_value.tv_nsec += delay();
> +	if (its.it_value.tv_nsec >= NSEC_PER_SEC) {
> +		its.it_value.tv_nsec -= NSEC_PER_SEC;
> +		its.it_value.tv_sec += 1;
> +	}
> +	its.it_interval.tv_sec = its.it_interval.tv_nsec = 0;
> +	timer_settime(igt_siglatency.timer, TIMER_ABSTIME, &its, NULL);
> +}
> +
> +void igt_start_siglatency(int sig)
> +{
> +	struct sigevent sev;
> +	struct sigaction act;
> +
> +	if (sig <= 0)
> +		sig = SIGRTMIN;
> +
> +	if (igt_siglatency.sig)
> +		(void)igt_stop_siglatency(NULL);
> +	igt_assert(igt_siglatency.sig == 0);
> +	igt_siglatency.sig = sig;
> +
> +	memset(&sev, 0, sizeof(sev));
> +	sev.sigev_notify = SIGEV_SIGNAL | SIGEV_THREAD_ID;
> +	sev.sigev_notify_thread_id = gettid();
> +	sev.sigev_signo = sig;
> +	timer_create(CLOCK_MONOTONIC, &sev, &igt_siglatency.timer);
> +
> +	memset(&act, 0, sizeof(act));
> +	act.sa_sigaction = siglatency;
> +	sigaction(sig, &act, &igt_siglatency.oldact);
> +
> +	siglatency(sig, NULL, NULL);
> +}
> +
> +double igt_stop_siglatency(struct igt_mean *result)
> +{
> +	double mean = igt_mean_get(&igt_siglatency.mean);
> +
> +	if (result)
> +		*result = igt_siglatency.mean;
> +
> +	sigaction(igt_siglatency.sig, &igt_siglatency.oldact, NULL);
> +	timer_delete(igt_siglatency.timer);
> +	memset(&igt_siglatency, 0, sizeof(igt_siglatency));
> +
> +	return mean;
> +}
> +
> +bool igt_allow_unlimited_files(void)
> +{
> +	struct rlimit rlim;
> +	unsigned nofile_rlim = 1024*1024;
> +
> +	FILE *file = fopen("/proc/sys/fs/nr_open", "r");
> +	if (file) {
> +		igt_assert(fscanf(file, "%u", &nofile_rlim) == 1);
> +		igt_info("System limit for open files is %u\n", nofile_rlim);
> +		fclose(file);
> +	}
> +
> +	if (getrlimit(RLIMIT_NOFILE, &rlim))
> +		return false;
> +
> +	rlim.rlim_cur = nofile_rlim;
> +	rlim.rlim_max = nofile_rlim;
> +	return setrlimit(RLIMIT_NOFILE, &rlim) == 0;
> +}
> +
> +/**
> + * vfs_file_max: report maximum number of files
> + *
> + * Get the global system-wide maximum of open files the kernel allows,
> + * by reading /proc/sys/fs/file-max. Fails the current subtest if
> + * reading the file fails, and returns a suitable best guess if it
> + * cannot be opened.
> + *
> + * Returns: System-wide maximum of open files, or a best effort guess.
> + */
> +uint64_t vfs_file_max(void)
> +{
> +	static long long unsigned max;
> +	if (max == 0) {
> +		FILE *file = fopen("/proc/sys/fs/file-max", "r");
> +		max = 80000;
> +		if (file) {
> +			igt_assert(fscanf(file, "%llu", &max) == 1);
> +			fclose(file);
> +		}
> +	}
> +	return max;
> +}
> +
> +void *igt_memdup(const void *ptr, size_t len)
> +{
> +	void *dup;
> +
> +	dup = malloc(len);
> +	if (dup)
> +		memcpy(dup, ptr, len);
> +
> +	return dup;
> +}
> diff --git a/lib/meson.build b/lib/meson.build
> index cef2d0ff..cb83ee99 100644
> --- a/lib/meson.build
> +++ b/lib/meson.build
> @@ -103,7 +103,6 @@ lib_deps = [
>  	libdrm,
>  	libdw,
>  	libkmod,
> -	libprocps,
>  	libudev,
>  	math,
>  	pciaccess,
> @@ -173,6 +172,12 @@ if chamelium.found()
>  	lib_sources += 'monitor_edids/monitor_edids_helper.c'
>  endif
>  
> +if libprocps.found()
> +	lib_deps += libprocps
> +else
> +	lib_deps += libproc2
> +endif
> +
>  if get_option('srcdir') != ''
>      srcdir = join_paths(get_option('srcdir'), 'tests')
>  else
> diff --git a/lib/meson.build.orig b/lib/meson.build.orig
> new file mode 100644
> index 00000000..cef2d0ff
> --- /dev/null
> +++ b/lib/meson.build.orig
> @@ -0,0 +1,358 @@
> +lib_sources = [
> +	'drmtest.c',
> +	'huc_copy.c',
> +	'i915/gem.c',
> +	'i915/gem_context.c',
> +	'i915/gem_create.c',
> +	'i915/gem_engine_topology.c',
> +	'i915/gem_scheduler.c',
> +	'i915/gem_submission.c',
> +	'i915/gem_ring.c',
> +	'i915/gem_mman.c',
> +	'i915/gem_vm.c',
> +	'i915/intel_memory_region.c',
> +	'i915/intel_mocs.c',
> +	'i915/i915_blt.c',
> +	'i915/i915_crc.c',
> +	'igt_collection.c',
> +	'igt_color_encoding.c',
> +	'igt_crc.c',
> +	'igt_debugfs.c',
> +	'igt_device.c',
> +	'igt_device_scan.c',
> +	'igt_drm_fdinfo.c',
> +	'igt_aux.c',
> +	'igt_gt.c',
> +	'igt_halffloat.c',
> +	'igt_hwmon.c',
> +	'igt_io.c',
> +	'igt_matrix.c',
> +	'igt_os.c',
> +	'igt_params.c',
> +	'igt_perf.c',
> +	'igt_pipe_crc.c',
> +	'igt_power.c',
> +	'igt_primes.c',
> +	'igt_rand.c',
> +	'igt_stats.c',
> +	'igt_syncobj.c',
> +	'igt_sysfs.c',
> +	'igt_sysrq.c',
> +	'igt_taints.c',
> +	'igt_thread.c',
> +	'igt_types.c',
> +	'igt_vec.c',
> +	'igt_vgem.c',
> +	'igt_x86.c',
> +	'instdone.c',
> +	'intel_allocator.c',
> +	'intel_allocator_msgchannel.c',
> +	'intel_allocator_random.c',
> +	'intel_allocator_reloc.c',
> +	'intel_allocator_simple.c',
> +	'intel_batchbuffer.c',
> +	'intel_bufops.c',
> +	'intel_chipset.c',
> +	'intel_ctx.c',
> +	'intel_device_info.c',
> +	'intel_mmio.c',
> +	'ioctl_wrappers.c',
> +	'media_spin.c',
> +	'media_fill.c',
> +	'gpgpu_fill.c',
> +	'gpu_cmds.c',
> +	'rendercopy_i915.c',
> +	'rendercopy_i830.c',
> +	'rendercopy_gen4.c',
> +	'rendercopy_gen6.c',
> +	'rendercopy_gen7.c',
> +	'rendercopy_gen8.c',
> +	'rendercopy_gen9.c',
> +	'runnercomms.c',
> +	'sw_sync.c',
> +	'intel_aux_pgtable.c',
> +	'intel_reg_map.c',
> +	'intel_iosf.c',
> +	'igt_kms.c',
> +	'igt_fb.c',
> +	'igt_core.c',
> +	'igt_draw.c',
> +	'igt_list.c',
> +	'igt_map.c',
> +	'igt_pm.c',
> +	'igt_dummyload.c',
> +	'igt_store.c',
> +	'uwildmat/uwildmat.c',
> +	'igt_kmod.c',
> +	'igt_panfrost.c',
> +	'igt_v3d.c',
> +	'igt_vc4.c',
> +	'igt_psr.c',
> +	'igt_amd.c',
> +	'igt_edid.c',
> +	'igt_eld.c',
> +	'igt_infoframe.c',
> +	'veboxcopy_gen12.c',
> +	'igt_msm.c',
> +]
> +
> +lib_deps = [
> +	cairo,
> +	glib,
> +	libatomic,
> +	libdrm,
> +	libdw,
> +	libkmod,
> +	libprocps,
> +	libudev,
> +	math,
> +	pciaccess,
> +	pixman,
> +	pthreads,
> +	realtime,
> +	zlib
> +]
> +
> +if libdrm_intel.found()
> +	lib_deps += libdrm_intel
> +else
> +	lib_sources += 'stubs/drm/intel_bufmgr.c'
> +	inc = [ inc, include_directories('stubs/drm') ]
> +endif
> +
> +if libdrm_nouveau.found()
> +	lib_deps += libdrm_nouveau
> +	lib_sources += [
> +		'igt_nouveau.c',
> +		'nouveau/cea0b5.c'
> +	]
> +endif
> +
> +if libdrm_amdgpu.found()
> +	lib_deps += libdrm_amdgpu
> +	lib_sources += [
> +		'amdgpu/amd_memory.c',
> +		'amdgpu/amd_command_submission.c',
> +		'amdgpu/amd_compute.c',
> +		'amdgpu/amd_gfx.c',
> +		'amdgpu/amd_ip_blocks.c',
> +		'amdgpu/amd_shaders.c',
> +		'amdgpu/amd_gfx_v8_0.c',
> +		'amdgpu/amd_gfx_v9_0.c',
> +		'amdgpu/amd_dispatch_helpers.c',
> +		'amdgpu/amd_dispatch.c',
> +		'amdgpu/amd_deadlock_helpers.c',
> +		'amdgpu/amd_pci_unplug.c',
> +		'amdgpu/xalloc.h'
> +	]
> +endif
> +
> +if libunwind.found()
> +	lib_deps += libunwind
> +else
> +	inc = [ inc, include_directories('stubs/libunwind') ]
> +endif
> +
> +if valgrind.found()
> +	lib_deps += valgrind
> +endif
> +
> +if gsl.found()
> +	lib_deps += gsl
> +	lib_sources += [ 'igt_frame.c', 'igt_audio.c' ]
> +endif
> +
> +if alsa.found()
> +	lib_deps += alsa
> +	lib_sources += 'igt_alsa.c'
> +endif
> +
> +if chamelium.found()
> +	lib_deps += chamelium
> +	lib_sources += [ 'igt_chamelium.c', 'igt_chamelium_stream.c' ]
> +	lib_sources += 'monitor_edids/monitor_edids_helper.c'
> +endif
> +
> +if get_option('srcdir') != ''
> +    srcdir = join_paths(get_option('srcdir'), 'tests')
> +else
> +    srcdir = join_paths(meson.source_root(), 'tests')
> +endif
> +
> +if get_option('version_hash') != ''
> +    vcs_command = ['echo', get_option('version_hash')]
> +else
> +    vcs_command = [ 'git', 'log', '-n1', '--pretty=format:g%h' ]
> +endif
> +
> +lib_version = vcs_tag(input : 'version.h.in', output : 'version.h',
> +		      fallback : 'NO-GIT',
> +		      command : vcs_command )
> +
> +lib_intermediates = []
> +foreach f: lib_sources
> +    name = f.underscorify()
> +    lib = static_library('igt-' + name,
> +	[ f, lib_version ],
> +	include_directories: inc,
> +	dependencies : lib_deps,
> +	c_args : [
> +	    '-DIGT_DATADIR="@0@"'.format(join_paths(prefix, datadir)),
> +	    '-DIGT_SRCDIR="@0@"'.format(srcdir),
> +	    '-DIGT_LOG_DOMAIN="@0@"'.format(f.split('.')[0]),
> +	])
> +
> +    lib_intermediates += lib
> +endforeach
> +
> +lib_igt_build = shared_library('igt',
> +    ['dummy.c'],
> +    link_whole: lib_intermediates,
> +    dependencies: lib_deps,
> +    install : true,
> +    soversion : '0',
> +)
> +
> +lib_igt = declare_dependency(link_with : lib_igt_build,
> +			    include_directories : inc)
> +
> +igt_deps = [ lib_igt ] + lib_deps
> +
> +lin_igt_chipset_build = static_library('igt_chipset',
> +                                       ['intel_chipset.c',
> +                                        'intel_device_info.c'],
> +                                       include_directories : inc)
> +
> +lib_igt_chipset = declare_dependency(link_with : lin_igt_chipset_build,
> +                                     include_directories : inc)
> +
> +lib_igt_perf_build = static_library('igt_perf',
> +	['igt_perf.c'],
> +	include_directories : inc)
> +
> +lib_igt_perf = declare_dependency(link_with : lib_igt_perf_build,
> +				  include_directories : inc)
> +
> +scan_dep = [
> +	glib,
> +	libudev,
> +]
> +
> +lib_igt_device_scan_build = static_library('igt_device_scan',
> +	['igt_device_scan.c',
> +	'igt_list.c',
> +	'igt_tools_stub.c',
> +	'intel_device_info.c',
> +	],
> +	dependencies : scan_dep,
> +	include_directories : inc)
> +
> +lib_igt_device_scan = declare_dependency(link_with : lib_igt_device_scan_build,
> +				  include_directories : inc)
> +
> +lib_igt_drm_fdinfo_build = static_library('igt_drm_fdinfo',
> +	['igt_drm_fdinfo.c'],
> +	include_directories : inc)
> +
> +lib_igt_drm_fdinfo = declare_dependency(link_with : lib_igt_drm_fdinfo_build,
> +				  include_directories : inc)
> +i915_perf_files = [
> +  'igt_list.c',
> +  'i915/perf.c',
> +  'i915/perf_data_reader.c',
> +]
> +
> +i915_perf_hardware = [
> +  'hsw',
> +  'bdw', 'chv',
> +  'sklgt2', 'sklgt3', 'sklgt4',
> +  'kblgt2', 'kblgt3',
> +  'cflgt2', 'cflgt3',
> +  'bxt', 'glk',
> +  'cnl',
> +  'icl', 'ehl',
> +  'tglgt1', 'tglgt2', 'rkl', 'dg1', 'adl',
> +  'acmgt1', 'acmgt2', 'acmgt3',
> +]
> +
> +i915_xml_files = []
> +foreach hw : i915_perf_hardware
> +  i915_xml_files += files('i915/perf-configs/oa-@0@.xml'.format(hw))
> +endforeach
> +
> +i915_perf_files += custom_target(
> +  'i915-perf-equations',
> +  input : [ 'i915/perf-configs/perf-equations-codegen.py' ] + i915_xml_files,
> +  output : [ 'i915_perf_equations.c', 'i915_perf_equations.h' ],
> +  command : [
> +    python3, '@INPUT0@',
> +    '--code', '@OUTPUT0@',
> +    '--header', '@OUTPUT1@',
> +    i915_xml_files,
> +  ])
> +
> +foreach hw : i915_perf_hardware
> +  i915_perf_files += custom_target(
> +    'i915-perf-registers-@0@'.format(hw),
> +    input : [ 'i915/perf-configs/perf-registers-codegen.py',
> +              'i915/perf-configs/oa-@0@.xml'.format(hw) ],
> +    output : [ 'i915_perf_registers_@0@.c'.format(hw),
> +               'i915_perf_registers_@0@.h'.format(hw), ],
> +    command : [
> +      python3, '@INPUT0@',
> +      '--code', '@OUTPUT0@',
> +      '--header', '@OUTPUT1@',
> +      '--xml-file', '@INPUT1@'
> +    ])
> +  i915_perf_files += custom_target(
> +    'i915-perf-metrics-@0@'.format(hw),
> +    input : [ 'i915/perf-configs/perf-metricset-codegen.py',
> +              'i915/perf-configs/oa-@0@.xml'.format(hw) ],
> +    output : [ 'i915_perf_metrics_@0@.c'.format(hw),
> +               'i915_perf_metrics_@0@.h'.format(hw), ],
> +    command : [
> +      python3, '@INPUT0@',
> +      '--code', '@OUTPUT0@',
> +      '--header', '@OUTPUT1@',
> +      '--equations-include', 'i915_perf_equations.h',
> +      '--registers-include', 'i915_perf_registers_@0@.h'.format(hw),
> +      '--xml-file', '@INPUT1@',
> +    ])
> +endforeach
> +
> +lib_igt_i915_perf_build = shared_library(
> +  'i915_perf',
> +  i915_perf_files,
> +  dependencies: lib_igt_chipset,
> +  include_directories : inc,
> +  install: true,
> +  soversion: '1.5')
> +
> +lib_igt_i915_perf = declare_dependency(
> +  link_with : lib_igt_i915_perf_build,
> +  include_directories : inc)
> +
> +install_headers(
> +  'igt_list.h',
> +  'intel_chipset.h',
> +  'i915/perf.h',
> +  'i915/perf_data.h',
> +  'i915/perf_data_reader.h',
> +  subdir : 'i915-perf'
> +)
> +
> +pkgconf = configuration_data()
> +
> +pkgconf.set('prefix', get_option('prefix'))
> +pkgconf.set('exec_prefix', '${prefix}')
> +pkgconf.set('libdir', '${prefix}/@0@'.format(get_option('libdir')))
> +pkgconf.set('includedir', '${prefix}/@0@'.format(get_option('includedir')))
> +pkgconf.set('i915_perf_version', '1.5.1')
> +
> +configure_file(
> +  input : 'i915-perf.pc.in',
> +  output : 'i915-perf.pc',
> +  configuration : pkgconf,
> +  install_dir : pkgconfigdir)
> +
> +subdir('tests')
> diff --git a/meson.build b/meson.build
> index 3e937f5a..475680ac 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -121,7 +121,15 @@ build_info += 'With libdrm: ' + ','.join(libdrm_info)
>  
>  pciaccess = dependency('pciaccess', version : '>=0.10')
>  libkmod = dependency('libkmod')
> -libprocps = dependency('libprocps', required : true)
> +libprocps = dependency('libprocps', required : false)
> +libproc2 = dependency('libproc2', required : false)
> +if libprocps.found()
> +  config.set('HAVE_LIBPROCPS', 1)
> +elif libproc2.found()
> +  config.set('HAVE_LIBPROC2', 1)
> +else
> +  error('Either libprocps or libproc2 is required')
> +endif
>  
>  libunwind = dependency('libunwind', required : get_option('libunwind'))
>  build_info += 'With libunwind: @0@'.format(libunwind.found())
> -- 
> 2.30.2
> 

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [igt-dev] [PATCH i-g-t] Use the new procps library libproc2
  2022-11-17  8:46 ` Petri Latvala
@ 2022-11-17  9:05   ` Craig Small
  0 siblings, 0 replies; 11+ messages in thread
From: Craig Small @ 2022-11-17  9:05 UTC (permalink / raw)
  To: Petri Latvala; +Cc: igt-dev

[-- Attachment #1: Type: text/plain, Size: 486 bytes --]

Hi Petri,
  Yes I am the author of the patch.

Feel free to adjust the follow, for example I'm not 100% sure how you
section your program up.

igt_core: Link to libproc2

libprocps, the library that provides information out of the proc filesystem,
has been renamed to libproc2 and has a new API.

This change will use either library and, for the newer one extracts the
values
once, rather than parsing the pids_info over and over.

Signed-off-by: Craig Small <csmall@dropbear.xyz>



>

[-- Attachment #2: Type: text/html, Size: 831 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [igt-dev] ✓ Fi.CI.BAT: success for Use the new procps library libproc2
  2022-11-17  8:41 [igt-dev] [PATCH i-g-t] Use the new procps library libproc2 Petri Latvala
  2022-11-17  8:46 ` Petri Latvala
@ 2022-11-17 10:06 ` Patchwork
  2022-11-17 11:17 ` [igt-dev] [PATCH i-g-t] " Kamil Konieczny
  2022-11-17 20:08 ` [igt-dev] ✗ Fi.CI.IGT: failure for " Patchwork
  3 siblings, 0 replies; 11+ messages in thread
From: Patchwork @ 2022-11-17 10:06 UTC (permalink / raw)
  To: Petri Latvala; +Cc: igt-dev

[-- Attachment #1: Type: text/plain, Size: 9033 bytes --]

== Series Details ==

Series: Use the new procps library libproc2
URL   : https://patchwork.freedesktop.org/series/111015/
State : success

== Summary ==

CI Bug Log - changes from CI_DRM_12392 -> IGTPW_8124
====================================================

Summary
-------

  **WARNING**

  Minor unknown changes coming with IGTPW_8124 need to be verified
  manually.
  
  If you think the reported changes have nothing to do with the changes
  introduced in IGTPW_8124, please notify your bug team to allow them
  to document this new failure mode, which will reduce false positives in CI.

  External URL: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/index.html

Participating hosts (27 -> 37)
------------------------------

  Additional (12): bat-dg1-7 bat-dg2-8 bat-adlm-1 bat-dg2-9 bat-adlp-6 bat-adlp-4 bat-adln-1 bat-rplp-1 bat-rpls-2 bat-dg2-11 fi-bsw-kefka bat-jsl-1 
  Missing    (2): fi-kbl-soraka fi-hsw-4770 

Possible new issues
-------------------

  Here are the unknown changes that may have been introduced in IGTPW_8124:

### IGT changes ###

#### Warnings ####

  * igt@fbdev@eof:
    - fi-kbl-8809g:       [DMESG-WARN][1] ([i915#7552]) -> [DMESG-WARN][2]
   [1]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/fi-kbl-8809g/igt@fbdev@eof.html
   [2]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/fi-kbl-8809g/igt@fbdev@eof.html

  
Known issues
------------

  Here are the changes found in IGTPW_8124 that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@debugfs_test@basic-hwmon:
    - bat-adlp-4:         NOTRUN -> [SKIP][3] ([i915#7456])
   [3]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/bat-adlp-4/igt@debugfs_test@basic-hwmon.html

  * igt@gem_lmem_swapping@verify-random:
    - bat-adlp-4:         NOTRUN -> [SKIP][4] ([i915#4613]) +3 similar issues
   [4]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/bat-adlp-4/igt@gem_lmem_swapping@verify-random.html

  * igt@gem_tiled_pread_basic:
    - bat-adlp-4:         NOTRUN -> [SKIP][5] ([i915#3282])
   [5]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/bat-adlp-4/igt@gem_tiled_pread_basic.html

  * igt@i915_pm_rps@basic-api:
    - bat-adlp-4:         NOTRUN -> [SKIP][6] ([i915#6621])
   [6]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/bat-adlp-4/igt@i915_pm_rps@basic-api.html

  * igt@i915_selftest@live@migrate:
    - bat-adlp-4:         NOTRUN -> [INCOMPLETE][7] ([i915#7308] / [i915#7348])
   [7]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/bat-adlp-4/igt@i915_selftest@live@migrate.html

  * igt@kms_chamelium@common-hpd-after-suspend:
    - fi-rkl-guc:         NOTRUN -> [SKIP][8] ([fdo#111827])
   [8]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/fi-rkl-guc/igt@kms_chamelium@common-hpd-after-suspend.html

  * igt@kms_chamelium@dp-crc-fast:
    - bat-adlp-4:         NOTRUN -> [SKIP][9] ([fdo#111827]) +7 similar issues
   [9]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/bat-adlp-4/igt@kms_chamelium@dp-crc-fast.html

  * igt@kms_chamelium@dp-edid-read:
    - fi-bsw-kefka:       NOTRUN -> [SKIP][10] ([fdo#109271] / [fdo#111827]) +8 similar issues
   [10]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/fi-bsw-kefka/igt@kms_chamelium@dp-edid-read.html

  * igt@kms_cursor_legacy@basic-busy-flip-before-cursor:
    - bat-adlp-4:         NOTRUN -> [SKIP][11] ([i915#4103])
   [11]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/bat-adlp-4/igt@kms_cursor_legacy@basic-busy-flip-before-cursor.html

  * igt@kms_force_connector_basic@prune-stale-modes:
    - bat-adlp-4:         NOTRUN -> [SKIP][12] ([i915#4093]) +3 similar issues
   [12]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/bat-adlp-4/igt@kms_force_connector_basic@prune-stale-modes.html

  * igt@kms_setmode@basic-clone-single-crtc:
    - bat-adlp-4:         NOTRUN -> [SKIP][13] ([i915#3555] / [i915#4579])
   [13]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/bat-adlp-4/igt@kms_setmode@basic-clone-single-crtc.html

  * igt@prime_vgem@basic-fence-flip:
    - fi-bsw-kefka:       NOTRUN -> [SKIP][14] ([fdo#109271]) +17 similar issues
   [14]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/fi-bsw-kefka/igt@prime_vgem@basic-fence-flip.html

  * igt@prime_vgem@basic-userptr:
    - bat-adlp-4:         NOTRUN -> [SKIP][15] ([fdo#109295] / [i915#3301] / [i915#3708])
   [15]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/bat-adlp-4/igt@prime_vgem@basic-userptr.html

  * igt@prime_vgem@basic-write:
    - bat-adlp-4:         NOTRUN -> [SKIP][16] ([fdo#109295] / [i915#3291] / [i915#3708]) +2 similar issues
   [16]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/bat-adlp-4/igt@prime_vgem@basic-write.html

  * igt@runner@aborted:
    - bat-adlp-4:         NOTRUN -> [FAIL][17] ([i915#4312])
   [17]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/bat-adlp-4/igt@runner@aborted.html

  
#### Possible fixes ####

  * igt@i915_selftest@live@hangcheck:
    - fi-rkl-guc:         [INCOMPLETE][18] ([i915#4983]) -> [PASS][19]
   [18]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/fi-rkl-guc/igt@i915_selftest@live@hangcheck.html
   [19]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/fi-rkl-guc/igt@i915_selftest@live@hangcheck.html

  
  {name}: This element is suppressed. This means it is ignored when computing
          the status of the difference (SUCCESS, WARNING, or FAILURE).

  [fdo#109271]: https://bugs.freedesktop.org/show_bug.cgi?id=109271
  [fdo#109285]: https://bugs.freedesktop.org/show_bug.cgi?id=109285
  [fdo#109295]: https://bugs.freedesktop.org/show_bug.cgi?id=109295
  [fdo#111827]: https://bugs.freedesktop.org/show_bug.cgi?id=111827
  [i915#1072]: https://gitlab.freedesktop.org/drm/intel/issues/1072
  [i915#1155]: https://gitlab.freedesktop.org/drm/intel/issues/1155
  [i915#1845]: https://gitlab.freedesktop.org/drm/intel/issues/1845
  [i915#1849]: https://gitlab.freedesktop.org/drm/intel/issues/1849
  [i915#2190]: https://gitlab.freedesktop.org/drm/intel/issues/2190
  [i915#2582]: https://gitlab.freedesktop.org/drm/intel/issues/2582
  [i915#3003]: https://gitlab.freedesktop.org/drm/intel/issues/3003
  [i915#3282]: https://gitlab.freedesktop.org/drm/intel/issues/3282
  [i915#3291]: https://gitlab.freedesktop.org/drm/intel/issues/3291
  [i915#3301]: https://gitlab.freedesktop.org/drm/intel/issues/3301
  [i915#3555]: https://gitlab.freedesktop.org/drm/intel/issues/3555
  [i915#3637]: https://gitlab.freedesktop.org/drm/intel/issues/3637
  [i915#3708]: https://gitlab.freedesktop.org/drm/intel/issues/3708
  [i915#4077]: https://gitlab.freedesktop.org/drm/intel/issues/4077
  [i915#4078]: https://gitlab.freedesktop.org/drm/intel/issues/4078
  [i915#4079]: https://gitlab.freedesktop.org/drm/intel/issues/4079
  [i915#4083]: https://gitlab.freedesktop.org/drm/intel/issues/4083
  [i915#4093]: https://gitlab.freedesktop.org/drm/intel/issues/4093
  [i915#4103]: https://gitlab.freedesktop.org/drm/intel/issues/4103
  [i915#4212]: https://gitlab.freedesktop.org/drm/intel/issues/4212
  [i915#4213]: https://gitlab.freedesktop.org/drm/intel/issues/4213
  [i915#4215]: https://gitlab.freedesktop.org/drm/intel/issues/4215
  [i915#4258]: https://gitlab.freedesktop.org/drm/intel/issues/4258
  [i915#4303]: https://gitlab.freedesktop.org/drm/intel/issues/4303
  [i915#4312]: https://gitlab.freedesktop.org/drm/intel/issues/4312
  [i915#4579]: https://gitlab.freedesktop.org/drm/intel/issues/4579
  [i915#4613]: https://gitlab.freedesktop.org/drm/intel/issues/4613
  [i915#4817]: https://gitlab.freedesktop.org/drm/intel/issues/4817
  [i915#4873]: https://gitlab.freedesktop.org/drm/intel/issues/4873
  [i915#4983]: https://gitlab.freedesktop.org/drm/intel/issues/4983
  [i915#5190]: https://gitlab.freedesktop.org/drm/intel/issues/5190
  [i915#5274]: https://gitlab.freedesktop.org/drm/intel/issues/5274
  [i915#5354]: https://gitlab.freedesktop.org/drm/intel/issues/5354
  [i915#6621]: https://gitlab.freedesktop.org/drm/intel/issues/6621
  [i915#6645]: https://gitlab.freedesktop.org/drm/intel/issues/6645
  [i915#7308]: https://gitlab.freedesktop.org/drm/intel/issues/7308
  [i915#7346]: https://gitlab.freedesktop.org/drm/intel/issues/7346
  [i915#7348]: https://gitlab.freedesktop.org/drm/intel/issues/7348
  [i915#7351]: https://gitlab.freedesktop.org/drm/intel/issues/7351
  [i915#7456]: https://gitlab.freedesktop.org/drm/intel/issues/7456
  [i915#7498]: https://gitlab.freedesktop.org/drm/intel/issues/7498
  [i915#7552]: https://gitlab.freedesktop.org/drm/intel/issues/7552


Build changes
-------------

  * CI: CI-20190529 -> None
  * IGT: IGT_7064 -> IGTPW_8124

  CI-20190529: 20190529
  CI_DRM_12392: b837a6bcf4fb8432fa1c21d8b7fa707b128d6bfd @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_8124: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/index.html
  IGT_7064: f5cc3abb3d99e9ab5da2285b184e045da95c35a2 @ https://gitlab.freedesktop.org/drm/igt-gpu-tools.git

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/index.html

[-- Attachment #2: Type: text/html, Size: 8555 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [igt-dev] [PATCH i-g-t] Use the new procps library libproc2
  2022-11-17  8:41 [igt-dev] [PATCH i-g-t] Use the new procps library libproc2 Petri Latvala
  2022-11-17  8:46 ` Petri Latvala
  2022-11-17 10:06 ` [igt-dev] ✓ Fi.CI.BAT: success for " Patchwork
@ 2022-11-17 11:17 ` Kamil Konieczny
  2022-11-17 11:29   ` Petri Latvala
  2022-11-17 21:13   ` Craig Small
  2022-11-17 20:08 ` [igt-dev] ✗ Fi.CI.IGT: failure for " Patchwork
  3 siblings, 2 replies; 11+ messages in thread
From: Kamil Konieczny @ 2022-11-17 11:17 UTC (permalink / raw)
  To: igt-dev; +Cc: Petri Latvala, Craig Small

Hi Craig,

On 2022-11-17 at 10:41:29 +0200, Petri Latvala wrote:
> From: Craig Small <csmall@dropbear.xyz>
> 
> ---
>  lib/igt_aux.c        |  246 +++++-
>  lib/igt_aux.c.orig   | 1921 ++++++++++++++++++++++++++++++++++++++++++
-- ^^^^^^^^^^^^^^^^^^

>  lib/meson.build      |    7 +-
>  lib/meson.build.orig |  358 ++++++++
-- ^^^^^^^^^^^^^^^^^^^^

These should not be there ?

>  meson.build          |   10 +-
>  5 files changed, 2500 insertions(+), 42 deletions(-)
>  create mode 100644 lib/igt_aux.c.orig
>  create mode 100644 lib/meson.build.orig
> 
> diff --git a/lib/igt_aux.c b/lib/igt_aux.c
> index 15e30440..2aee1275 100644
> --- a/lib/igt_aux.c
> +++ b/lib/igt_aux.c
> @@ -52,8 +52,16 @@
>  #include <assert.h>
>  #include <grp.h>
>  
> +#ifdef HAVE_LIBPROCPS
>  #include <proc/readproc.h>
> +#endif
> +#ifdef HAVE_LIBPROC2
-- ^
Maybe just #elif ?

> +#include <libproc2/pids.h>
> +#endif
> +
>  #include <libudev.h>
> +#include <linux/limits.h>
-- ^^^^^^^^^^^^^^^^^^
> +#include <dirent.h>
-- ^^^^^^^^^^^^^^^^^^
What this has with libproc2 ?
Note also that FreeBSD have its own limits.h

>  
>  #include "drmtest.h"
>  #include "i915_drm.h"
> @@ -1217,6 +1225,7 @@ void igt_unlock_mem(void)
>   */
>  int igt_is_process_running(const char *comm)
>  {
> +#if HAVE_LIBPROCPS
>  	PROCTAB *proc;
>  	proc_t *proc_info;
>  	bool found = false;
> @@ -1235,6 +1244,26 @@ int igt_is_process_running(const char *comm)
>  
>  	closeproc(proc);
>  	return found;
> +#endif
> +#if HAVE_LIBPROC2
> +	enum pids_item Item[] = { PIDS_CMD };
> +	struct pids_info *info = NULL;
> +	struct pids_stack *stack;
> +	char *pid_comm;
> +	bool found = false;
> +
> +	if (procps_pids_new(&info, Item, 1) < 0)
> +	    return false;
> +	while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
> +	    pid_comm = PIDS_VAL(0, str, stack, info);
> +	    if (!strncasecmp(pid_comm, comm, strlen(pid_comm))) {

Processes AA and aa are different, why do you use strncasecmp ?

> +		found = true;
> +		break;
> +	    }
> +	}
> +	procps_pids_unref(&info);
> +	return found;
> +#endif
>  }
>  
>  /**
> @@ -1251,6 +1280,7 @@ int igt_is_process_running(const char *comm)
>   */
>  int igt_terminate_process(int sig, const char *comm)
>  {
> +#if HAVE_LIBPROCPS
>  	PROCTAB *proc;
>  	proc_t *proc_info;
>  	int err = 0;
> @@ -1272,6 +1302,29 @@ int igt_terminate_process(int sig, const char *comm)
>  
>  	closeproc(proc);
>  	return err;
> +#endif
> +#if HAVE_LIBPROC2
> +	enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD };
> +	struct pids_info *info = NULL;
> +	struct pids_stack *stack;
> +	char *pid_comm;
> +	int pid;
> +	int err = 0;
> +
> +	if (procps_pids_new(&info, Items, 2) < 0)
> +		return -errno;
> +	while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
> +		pid = PIDS_VAL(0, s_int, stack, info);
> +		pid_comm = PIDS_VAL(1, str, stack, info);
> +		if (!strncasecmp(pid_comm, comm, strlen(pid_comm))) {
> +			if (kill(pid, sig) < 0)
> +				err = -errno;
> +			break;
> +		}
> +	}
> +	procps_pids_unref(&info);
> +	return err;
> +#endif
>  }
>  
>  struct pinfo {
> @@ -1341,9 +1394,9 @@ igt_show_stat_header(void)
>  }
>  
>  static void
> -igt_show_stat(proc_t *info, int *state, const char *fn)
> +igt_show_stat(const pid_t tid, const char *cmd, int *state, const char *fn)
>  {
> -	struct pinfo p = { .pid = info->tid, .comm = info->cmd, .fn = fn };
> +	struct pinfo p = { .pid = tid, .comm = cmd, .fn = fn };
>  
>  	if (!*state)
>  		igt_show_stat_header();
> @@ -1353,7 +1406,7 @@ igt_show_stat(proc_t *info, int *state, const char *fn)
>  }
>  
>  static void
> -__igt_lsof_fds(proc_t *proc_info, int *state, char *proc_path, const char *dir)
> +__igt_lsof_fds(const pid_t tid, const char *cmd, int *state, char *proc_path, const char *dir)
>  {
>  	struct dirent *d;
>  	struct stat st;
> @@ -1400,7 +1453,7 @@ again:
>  		dirn = dirname(copy_fd_lnk);
>  
>  		if (!strncmp(dir, dirn, strlen(dir)))
> -			igt_show_stat(proc_info, state, fd_lnk);
> +			igt_show_stat(tid, cmd, state, fd_lnk);
>  
>  		free(copy_fd_lnk);
>  		free(fd_lnk);
> @@ -1416,13 +1469,14 @@ again:
>  static void
>  __igt_lsof(const char *dir)
>  {
> -	PROCTAB *proc;
> -	proc_t *proc_info;
> -
>  	char path[30];
>  	char *name_lnk;
>  	struct stat st;
>  	int state = 0;
> +#ifdef HAVE_LIBPROCPS
> +	PROCTAB *proc;
> +	proc_t *proc_info;
> +
>  
>  	proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
>  	igt_assert(proc != NULL);
> @@ -1443,19 +1497,57 @@ __igt_lsof(const char *dir)
>  		name_lnk[read] = '\0';
>  
>  		if (!strncmp(dir, name_lnk, strlen(dir)))
> -			igt_show_stat(proc_info, &state, name_lnk);
> +			igt_show_stat(proc_info->tid, proc_info->cmd, &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);
> +		__igt_lsof_fds(proc_info->tid, proc_info->cmd, &state, path, dir);
>  
>  		free(name_lnk);
>  		freeproc(proc_info);
>  	}
>  
>  	closeproc(proc);
> +#endif
> +#ifdef HAVE_LIBPROC2
> +	enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD };
> +	struct pids_info *info = NULL;
> +	struct pids_stack *stack;
> +
> +	if (procps_pids_new(&info, Items, 2) < 0)
> +		return;
> +	while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
> +		ssize_t read;
> +		int tid = PIDS_VAL(0, s_int, stack, info);
> +		char *pid_comm = PIDS_VAL(1, str, stack, info);
> +
> +		/* check current working directory */
> +		memset(path, 0, sizeof(path));
> +		snprintf(path, sizeof(path), "/proc/%d/cwd", 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(tid, pid_comm, &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", tid);
> +
> +		__igt_lsof_fds(tid, pid_comm, &state, path, dir);
> +
> +		free(name_lnk);
> +	}
> +	procps_pids_unref(&info);
> +#endif
>  }
>  
>  /**
> @@ -1490,7 +1582,7 @@ igt_lsof(const char *dpath)
>  	free(sanitized);
>  }
>  
> -static void pulseaudio_unload_module(proc_t *proc_info)
> +static void pulseaudio_unload_module(const uid_t euid, const gid_t egid)
>  {
>  	struct igt_helper_process pa_proc = {};
>  	char xdg_dir[PATH_MAX];
> @@ -1498,14 +1590,14 @@ static void pulseaudio_unload_module(proc_t *proc_info)
>  	struct passwd *pw;
>  
>  	igt_fork_helper(&pa_proc) {
> -		pw = getpwuid(proc_info->euid);
> +		pw = getpwuid(euid);
>  		homedir = pw->pw_dir;
> -		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", proc_info->euid);
> +		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", euid);
>  
>  		igt_info("Request pulseaudio to stop using audio device\n");
>  
> -		setgid(proc_info->egid);
> -		setuid(proc_info->euid);
> +		setgid(egid);
> +		setuid(euid);
>  		clearenv();
>  		setenv("HOME", homedir, 1);
>  		setenv("XDG_RUNTIME_DIR",xdg_dir, 1);
> @@ -1524,10 +1616,13 @@ static void pipewire_reserve_wait(void)
>  	char xdg_dir[PATH_MAX];
>  	const char *homedir;
>  	struct passwd *pw;
> -	proc_t *proc_info;
> -	PROCTAB *proc;
> +	int tid=0, euid, egid;
>  
> +#ifdef HAVE_LIBPROCPS
>  	igt_fork_helper(&pw_reserve_proc) {
> +		proc_t *proc_info;
> +		PROCTAB *proc;
> +
>  		igt_info("Preventing pipewire-pulse to use the audio drivers\n");
>  
>  		proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
> @@ -1540,19 +1635,44 @@ static void pipewire_reserve_wait(void)
>  		}
>  		closeproc(proc);
>  
> +		tid = proc_info->tid;
> +		euid = proc_info->euid;
> +		egid = proc_info->egid;
> +		freeproc(proc_info);
> +#endif
> +#ifdef HAVE_LIBPROC2
> +	igt_fork(child, 1) {

This should be fork_helper like above.

> +		enum pids_item Items[] = { PIDS_ID_PID, PIDS_ID_EUID, PIDS_ID_EGID };
> +		enum rel_items { EU_PID, EU_EUID, EU_EGID };
> +		struct pids_info *info = NULL;
> +		struct pids_stack *stack;
> +
> +		igt_info("Preventing pipewire-pulse to use the audio drivers\n");
> +
> +		if (procps_pids_new(&info, Items, 3) < 0)
> +		    return;
------------------- ^
Use exit(0) here plus maybe some ing_info why it failed ?

> +		while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
> +			tid = PIDS_VAL(EU_PID, s_int, stack, info);
> +			if (pipewire_pulse_pid == tid)
> +				break;
> +		}
> +		euid = PIDS_VAL(EU_EUID, s_int, stack, info);
> +		egid = PIDS_VAL(EU_EGID, s_int, stack, info);
> +		procps_pids_unref(&info);
> +#endif
> +
>  		/* Sanity check: if it can't find the process, it means it has gone */
> -		if (pipewire_pulse_pid != proc_info->tid)
> +		if (pipewire_pulse_pid != tid)
>  			exit(0);
>  
> -		pw = getpwuid(proc_info->euid);
> +		pw = getpwuid(euid);
>  		homedir = pw->pw_dir;
> -		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", proc_info->euid);
> -		setgid(proc_info->egid);
> -		setuid(proc_info->euid);
> +		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", euid);
> +		setgid(egid);
> +		setuid(euid);
>  		clearenv();
>  		setenv("HOME", homedir, 1);
>  		setenv("XDG_RUNTIME_DIR",xdg_dir, 1);
> -		freeproc(proc_info);
>  
>  		/*
>  		 * pw-reserve will run in background. It will only exit when
> @@ -1570,9 +1690,7 @@ static void pipewire_reserve_wait(void)
>  int pipewire_pulse_start_reserve(void)
>  {
>  	bool is_pw_reserve_running = false;
> -	proc_t *proc_info;
>  	int attempts = 0;
> -	PROCTAB *proc;
>  
>  	if (!pipewire_pulse_pid)
>  		return 0;
> @@ -1584,6 +1702,10 @@ int pipewire_pulse_start_reserve(void)
>  	 * pipewire version 0.3.50 or upper.
>  	 */
>  	for (attempts = 0; attempts < PIPEWIRE_RESERVE_MAX_TIME; attempts++) {
> +#ifdef HAVE_LIBPROCPS
> +		PROCTAB *proc;
> +		proc_t *proc_info;
> +
>  		usleep(1000);
>  		proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
>  		igt_assert(proc != NULL);
> @@ -1598,6 +1720,25 @@ int pipewire_pulse_start_reserve(void)
>  			freeproc(proc_info);
>  		}
>  		closeproc(proc);
> +#endif
> +#ifdef HAVE_LIBPROC2
> +		enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD };
> +		struct pids_info *info = NULL;
> +		struct pids_stack *stack;
> +
> +		usleep(1000);
> +
> +		if (procps_pids_new(&info, Items, 2) < 0)
> +			return 1;
----------------------- ^
break or igt_assert_f(errno == 0, "Getting procps failed\n"); here.

> +		while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
> +			if (!strcmp(PIDS_VAL(1, str, stack, info), "pw-reserve")) {
> +				is_pw_reserve_running = true;
> +				pipewire_pw_reserve_pid = PIDS_VAL(0, s_int, stack, info);
> +				break;
> +			}
> +		}
> +		procps_pids_unref(&info);
> +#endif
>  		if (is_pw_reserve_running)
>  			break;
>  	}
> @@ -1645,7 +1786,7 @@ void pipewire_pulse_stop_reserve(void)
>   * 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)
> +__igt_lsof_audio_and_kill_proc(const pid_t tid, const char *cmd, const uid_t euid, const gid_t egid, char *proc_path)
>  {
>  	const char *audio_dev = "/dev/snd/";
>  	char path[PATH_MAX * 2];
> @@ -1670,10 +1811,10 @@ __igt_lsof_audio_and_kill_proc(proc_t *proc_info, char *proc_path)
>  	 * 2) unload/unbind the the audio driver(s);
>  	 * 3) stop the pw-reserve thread.
>  	 */
> -	if (!strcmp(proc_info->cmd, "pipewire-pulse")) {
> +	if (!strcmp(cmd, "pipewire-pulse")) {
>  		igt_info("process %d (%s) is using audio device. Should be requested to stop using them.\n",
> -			 proc_info->tid, proc_info->cmd);
> -		pipewire_pulse_pid = proc_info->tid;
> +			 tid, cmd);
> +		pipewire_pulse_pid = tid;
>  		return 0;
>  	}
>  	/*
> @@ -1685,9 +1826,9 @@ __igt_lsof_audio_and_kill_proc(proc_t *proc_info, char *proc_path)
>  	 * will respawn them. So, just ignore here, they'll honor pw-reserve,
>  	 * when the time comes.
>  	 */
> -	if (!strcmp(proc_info->cmd, "pipewire-media-session"))
> +	if (!strcmp(cmd, "pipewire-media-session"))
>  		return 0;
> -	if (!strcmp(proc_info->cmd, "wireplumber"))
> +	if (!strcmp(cmd, "wireplumber"))
>  		return 0;
>  
>  	dp = opendir(proc_path);
> @@ -1723,22 +1864,22 @@ __igt_lsof_audio_and_kill_proc(proc_t *proc_info, char *proc_path)
>  		 * 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);
> +		if (!strcmp(cmd, "pulseaudio")) {
> +			pulseaudio_unload_module(euid, egid);
>  			break;
>  		}
>  
>  		/* 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);
> +				tid, cmd);
>  
> -		if (kill(proc_info->tid, SIGTERM) < 0) {
> +		if (kill(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) {
> +				cmd, tid);
> +			if (kill(tid, SIGABRT) < 0) {
>  				fail++;
>  				igt_info("Fail to terminate %s (pid: %d) with SIGABRT\n",
> -					proc_info->cmd, proc_info->tid);
> +					cmd, tid);
>  			}
>  		}
>  
> @@ -1760,23 +1901,48 @@ int
>  igt_lsof_kill_audio_processes(void)
>  {
>  	char path[PATH_MAX];
> +	int fail = 0;
> +
> +#ifdef HAVE_LIBPROCPS
>  	proc_t *proc_info;
>  	PROCTAB *proc;
> -	int fail = 0;
>  
>  	proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
>  	igt_assert(proc != NULL);
>  	pipewire_pulse_pid = 0;
> -
>  	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);
> +			fail += __igt_lsof_audio_and_kill_proc(proc_info->tid, proc_info->cmd, proc_info->euid, proc_info->egid, path);
>  
>  		freeproc(proc_info);
>  	}
>  	closeproc(proc);
> +#endif
> +#ifdef HAVE_LIBPROC2
> +	enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD, PIDS_ID_EUID, PIDS_ID_EGID };
> +	enum rel_items { EU_PID, EU_CMD, EU_EUID, EU_EGID };
> +	struct pids_info *info = NULL;
> +	struct pids_stack *stack;
> +	pid_t tid;
> +
> +	if (procps_pids_new(&info, Items, 4) < 0)
> +		return 1;
> +	while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
> +		tid = PIDS_VAL(EU_PID, s_int, stack, info);
> +
> +		if (snprintf(path, sizeof(path), "/proc/%d/fd", tid) < 1)
> +			fail++;
> +		else
> +			fail += __igt_lsof_audio_and_kill_proc(tid,
> +				PIDS_VAL(EU_CMD, str, stack, info),
> +				PIDS_VAL(EU_EUID, s_int, stack, info),
> +				PIDS_VAL(EU_EGID, s_int, stack, info),
> +				path);
> +	}
> +	procps_pids_unref(&info);
> +#endif
>  
>  	return fail;
>  }
> diff --git a/lib/igt_aux.c.orig b/lib/igt_aux.c.orig
> new file mode 100644
> index 00000000..15e30440
> --- /dev/null
> +++ b/lib/igt_aux.c.orig
> @@ -0,0 +1,1921 @@
> +/*
> + * Copyright © 2007, 2011, 2013, 2014, 2015 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.
> + *
> + * Authors:
> + *    Eric Anholt <eric@anholt.net>
> + *    Daniel Vetter <daniel.vetter@ffwll.ch>
> + *
> + */
> +
> +#ifdef HAVE_LIBGEN_H
> +#include <libgen.h>
> +#endif
> +#include <stdio.h>
> +#include <fcntl.h>
> +#include <pwd.h>
> +#include <sys/stat.h>
> +#include <sys/ioctl.h>
> +#include <string.h>
> +#include <sys/mman.h>
> +#include <signal.h>
> +#include <pciaccess.h>
> +#include <stdlib.h>
> +#include <time.h>
> +#include <unistd.h>
> +#include <sys/poll.h>
> +#include <sys/wait.h>
> +#include <sys/resource.h>
> +#include <sys/time.h>
> +#include <sys/types.h>
> +#include <sys/syscall.h>
> +#include <sys/utsname.h>
> +#include <termios.h>
> +#include <assert.h>
> +#include <grp.h>
> +
> +#include <proc/readproc.h>
> +#include <libudev.h>
> +
> +#include "drmtest.h"
> +#include "i915_drm.h"
> +#include "intel_chipset.h"
> +#include "igt_aux.h"
> +#include "igt_debugfs.h"
> +#include "igt_gt.h"
> +#include "igt_params.h"
> +#include "igt_rand.h"
> +#include "igt_sysfs.h"
> +#include "config.h"
> +#include "intel_reg.h"
> +#include "ioctl_wrappers.h"
> +#include "igt_kms.h"
> +#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
> + * @title: aux
> + * @include: igt.h
> + *
> + * This library provides various auxiliary helper functions that don't really
> + * fit into any other topic.
> + */
> +
> +static struct __igt_sigiter_global {
> +	pid_t tid;
> +	timer_t timer;
> +	struct timespec offset;
> +	struct {
> +		long hit, miss;
> +		long ioctls, signals;
> +	} stat;
> +} __igt_sigiter;
> +
> +static void sigiter(int sig, siginfo_t *info, void *arg)
> +{
> +	__igt_sigiter.stat.signals++;
> +}
> +
> +#if 0
> +#define SIG_ASSERT(expr) igt_assert(expr)
> +#else
> +#define SIG_ASSERT(expr)
> +#endif
> +
> +static int
> +sig_ioctl(int fd, unsigned long request, void *arg)
> +{
> +	struct itimerspec its;
> +	int ret;
> +
> +	SIG_ASSERT(__igt_sigiter.timer);
> +	SIG_ASSERT(__igt_sigiter.tid == gettid());
> +
> +	memset(&its, 0, sizeof(its));
> +	if (timer_settime(__igt_sigiter.timer, 0, &its, NULL)) {
> +		/* oops, we didn't undo the interrupter (i.e. !unwound abort) */
> +		igt_ioctl = drmIoctl;
> +		return drmIoctl(fd, request, arg);
> +	}
> +
> +	its.it_value = __igt_sigiter.offset;
> +	do {
> +		long serial;
> +
> +		__igt_sigiter.stat.ioctls++;
> +
> +		ret = 0;
> +		serial = __igt_sigiter.stat.signals;
> +		igt_assert(timer_settime(__igt_sigiter.timer, 0, &its, NULL) == 0);
> +		if (ioctl(fd, request, arg))
> +			ret = errno;
> +		if (__igt_sigiter.stat.signals == serial)
> +			__igt_sigiter.stat.miss++;
> +		if (ret == 0)
> +			break;
> +
> +		if (ret == EINTR) {
> +			__igt_sigiter.stat.hit++;
> +
> +			its.it_value.tv_sec *= 2;
> +			its.it_value.tv_nsec *= 2;
> +			while (its.it_value.tv_nsec >= NSEC_PER_SEC) {
> +				its.it_value.tv_nsec -= NSEC_PER_SEC;
> +				its.it_value.tv_sec += 1;
> +			}
> +
> +			SIG_ASSERT(its.it_value.tv_nsec >= 0);
> +			SIG_ASSERT(its.it_value.tv_sec >= 0);
> +		}
> +	} while (ret == EAGAIN || ret == EINTR);
> +
> +	memset(&its, 0, sizeof(its));
> +	timer_settime(__igt_sigiter.timer, 0, &its, NULL);
> +
> +	errno = ret;
> +	return ret ? -1 : 0;
> +}
> +
> +static bool igt_sigiter_start(struct __igt_sigiter *iter, bool enable)
> +{
> +	/* Note that until we can automatically clean up on failed/skipped
> +	 * tests, we cannot assume the state of the igt_ioctl indirection.
> +	 */
> +	SIG_ASSERT(igt_ioctl == drmIoctl);
> +	igt_ioctl = drmIoctl;
> +
> +	if (enable) {
> +		struct timespec start, end;
> +		struct sigevent sev;
> +		struct sigaction act;
> +		struct itimerspec its;
> +
> +		igt_ioctl = sig_ioctl;
> +		__igt_sigiter.tid = gettid();
> +
> +		memset(&sev, 0, sizeof(sev));
> +		sev.sigev_notify = SIGEV_SIGNAL | SIGEV_THREAD_ID;
> +		sev.sigev_notify_thread_id = __igt_sigiter.tid;
> +		sev.sigev_signo = SIGRTMIN;
> +		igt_assert(timer_create(CLOCK_MONOTONIC, &sev, &__igt_sigiter.timer) == 0);
> +
> +		memset(&its, 0, sizeof(its));
> +		igt_assert(timer_settime(__igt_sigiter.timer, 0, &its, NULL) == 0);
> +
> +		memset(&act, 0, sizeof(act));
> +		act.sa_sigaction = sigiter;
> +		act.sa_flags = SA_SIGINFO;
> +		igt_assert(sigaction(SIGRTMIN, &act, NULL) == 0);
> +
> +		/* Try to find the approximate delay required to skip over
> +		 * the timer_setttime and into the following ioctl() to try
> +		 * and avoid the timer firing before we enter the drmIoctl.
> +		 */
> +		igt_assert(clock_gettime(CLOCK_MONOTONIC, &start) == 0);
> +		igt_assert(timer_settime(__igt_sigiter.timer, 0, &its, NULL) == 0);
> +		igt_assert(clock_gettime(CLOCK_MONOTONIC, &end) == 0);
> +
> +		__igt_sigiter.offset.tv_sec = end.tv_sec - start.tv_sec;
> +		__igt_sigiter.offset.tv_nsec = end.tv_nsec - start.tv_nsec;
> +		if (__igt_sigiter.offset.tv_nsec < 0) {
> +			__igt_sigiter.offset.tv_nsec += NSEC_PER_SEC;
> +			__igt_sigiter.offset.tv_sec -= 1;
> +		}
> +		if (__igt_sigiter.offset.tv_sec < 0) {
> +			__igt_sigiter.offset.tv_nsec = 0;
> +			__igt_sigiter.offset.tv_sec = 0;
> +		}
> +		igt_assert(__igt_sigiter.offset.tv_sec == 0);
> +
> +		igt_debug("Initial delay for interruption: %ld.%09lds\n",
> +			  __igt_sigiter.offset.tv_sec,
> +			  __igt_sigiter.offset.tv_nsec);
> +	}
> +
> +	return true;
> +}
> +
> +static bool igt_sigiter_stop(struct __igt_sigiter *iter, bool enable)
> +{
> +	if (enable) {
> +		struct sigaction act;
> +
> +		SIG_ASSERT(igt_ioctl == sig_ioctl);
> +		SIG_ASSERT(__igt_sigiter.tid == gettid());
> +		igt_ioctl = drmIoctl;
> +
> +		timer_delete(__igt_sigiter.timer);
> +
> +		memset(&act, 0, sizeof(act));
> +		act.sa_handler = SIG_IGN;
> +		sigaction(SIGRTMIN, &act, NULL);
> +
> +		memset(&__igt_sigiter, 0, sizeof(__igt_sigiter));
> +	}
> +
> +	memset(iter, 0, sizeof(*iter));
> +	return false;
> +}
> +
> +bool __igt_sigiter_continue(struct __igt_sigiter *iter, bool enable)
> +{
> +	if (iter->pass++ == 0)
> +		return igt_sigiter_start(iter, enable);
> +
> +	/* If nothing reported SIGINT, nothing will on the next pass, so
> +	 * give up! Also give up if everything is now executing faster
> +	 * than current sigtimer.
> +	 */
> +	if (__igt_sigiter.stat.hit == 0 ||
> +	    __igt_sigiter.stat.miss == __igt_sigiter.stat.ioctls)
> +		return igt_sigiter_stop(iter, enable);
> +
> +	igt_debug("%s: pass %d, missed %ld/%ld\n",
> +		  __func__, iter->pass - 1,
> +		  __igt_sigiter.stat.miss,
> +		  __igt_sigiter.stat.ioctls);
> +
> +	SIG_ASSERT(igt_ioctl == sig_ioctl);
> +	SIG_ASSERT(__igt_sigiter.timer);
> +
> +	__igt_sigiter.offset.tv_sec *= 2;
> +	__igt_sigiter.offset.tv_nsec *= 2;
> +	while (__igt_sigiter.offset.tv_nsec >= NSEC_PER_SEC) {
> +		__igt_sigiter.offset.tv_nsec -= NSEC_PER_SEC;
> +		__igt_sigiter.offset.tv_sec += 1;
> +	}
> +	SIG_ASSERT(__igt_sigiter.offset.tv_nsec >= 0);
> +	SIG_ASSERT(__igt_sigiter.offset.tv_sec >= 0);
> +
> +	memset(&__igt_sigiter.stat, 0, sizeof(__igt_sigiter.stat));
> +	return true;
> +}
> +
> +static struct igt_helper_process signal_helper;
> +long long int sig_stat;
> +__noreturn static void signal_helper_process(pid_t pid)
> +{
> +	/* Interrupt the parent process at 500Hz, just to be annoying */
> +	while (1) {
> +		usleep(1000 * 1000 / 500);
> +		if (kill(pid, SIGCONT)) /* Parent has died, so must we. */
> +			exit(0);
> +	}
> +}
> +
> +static void sig_handler(int i)
> +{
> +	sig_stat++;
> +}
> +
> +/**
> + * igt_fork_signal_helper:
> + *
> + * Fork a child process using #igt_fork_helper to interrupt the parent process
> + * with a SIGCONT signal at regular quick intervals. The corresponding dummy
> + * signal handler is installed in the parent process.
> + *
> + * This is useful to exercise ioctl error paths, at least where those can be
> + * exercises by interrupting blocking waits, like stalling for the gpu. This
> + * helper can also be used from children spawned with #igt_fork.
> + *
> + * In tests with subtests this function can be called outside of failure
> + * catching code blocks like #igt_fixture or #igt_subtest.
> + *
> + * Note that this just spews signals at the current process unconditionally and
> + * hence incurs quite a bit of overhead. For a more focused approach, with less
> + * overhead, look at the #igt_while_interruptible code block macro.
> + */
> +void igt_fork_signal_helper(void)
> +{
> +	if (igt_only_list_subtests())
> +		return;
> +
> +	/* We pick SIGCONT as it is a "safe" signal - if we send SIGCONT to
> +	 * an unexpecting process it spuriously wakes up and does nothing.
> +	 * Most other signals (e.g. SIGUSR1) cause the process to die if they
> +	 * are not handled. This is an issue in case the sighandler is not
> +	 * inherited correctly (or if there is a race in the inheritance
> +	 * and we send the signal at exactly the wrong time).
> +	 */
> +	signal(SIGCONT, sig_handler);
> +	setpgid(0, 0); /* define a new process group for the tests */
> +
> +	igt_fork_helper(&signal_helper) {
> +		setpgid(0, 0); /* Escape from the test process group */
> +
> +		/* Pass along the test process group identifier,
> +		 * negative pid => send signal to everyone in the group.
> +		 */
> +		signal_helper_process(-getppid());
> +	}
> +}
> +
> +/**
> + * igt_stop_signal_helper:
> + *
> + * Stops the child process spawned with igt_fork_signal_helper() again.
> + *
> + * In tests with subtests this function can be called outside of failure
> + * catching code blocks like #igt_fixture or #igt_subtest.
> + */
> +void igt_stop_signal_helper(void)
> +{
> +	if (igt_only_list_subtests())
> +		return;
> +
> +	igt_stop_helper(&signal_helper);
> +
> +	sig_stat = 0;
> +}
> +
> +/**
> + * igt_suspend_signal_helper:
> + *
> + * Suspends the child process spawned with igt_fork_signal_helper(). This
> + * should be called before a critical section of code that has difficulty to
> + * make progress if interrupted frequently, like the clone() syscall called
> + * from a largish executable. igt_resume_signal_helper() must be called after
> + * the critical section to restart interruptions for the test.
> + */
> +void igt_suspend_signal_helper(void)
> +{
> +	int status;
> +
> +	if (!signal_helper.running)
> +		return;
> +
> +	kill(signal_helper.pid, SIGSTOP);
> +	while (waitpid(signal_helper.pid, &status, WUNTRACED) == -1 &&
> +	       errno == EINTR)
> +		;
> +}
> +
> +/**
> + * igt_resume_signal_helper:
> + *
> + * Resumes the child process spawned with igt_fork_signal_helper().
> + *
> + * This should be paired with igt_suspend_signal_helper() and called after the
> + * problematic code sensitive to signals.
> + */
> +void igt_resume_signal_helper(void)
> +{
> +	if (!signal_helper.running)
> +		return;
> +
> +	kill(signal_helper.pid, SIGCONT);
> +}
> +
> +static struct igt_helper_process shrink_helper;
> +__noreturn static void shrink_helper_process(int fd, pid_t pid)
> +{
> +	while (1) {
> +		igt_drop_caches_set(fd, DROP_SHRINK_ALL);
> +		usleep(1000 * 1000 / 50);
> +		if (kill(pid, 0)) /* Parent has died, so must we. */
> +			exit(0);
> +	}
> +}
> +
> +/**
> + * igt_fork_shrink_helper:
> + *
> + * Fork a child process using #igt_fork_helper to force all available objects
> + * to be paged out (via i915_gem_shrink()).
> + *
> + * This is useful to exercise swapping paths, without requiring us to hit swap.
> + *
> + * This should only be used from an igt_fixture.
> + */
> +void igt_fork_shrink_helper(int drm_fd)
> +{
> +	assert(!igt_only_list_subtests());
> +	igt_require(igt_drop_caches_has(drm_fd, DROP_SHRINK_ALL));
> +	igt_fork_helper(&shrink_helper)
> +		shrink_helper_process(drm_fd, getppid());
> +}
> +
> +/**
> + * igt_stop_shrink_helper:
> + *
> + * Stops the child process spawned with igt_fork_shrink_helper().
> + */
> +void igt_stop_shrink_helper(void)
> +{
> +	igt_stop_helper(&shrink_helper);
> +}
> +
> +static void show_kernel_stack(pid_t pid)
> +{
> +	char buf[80], *str;
> +	int dir;
> +
> +	snprintf(buf, sizeof(buf), "/proc/%d", pid);
> +	dir = open(buf, O_RDONLY);
> +	if (dir < 0)
> +		return;
> +
> +	str = igt_sysfs_get(dir, "stack");
> +	if (str) {
> +		igt_debug("Kernel stack for pid %d:\n%s\n", pid, str);
> +		free(str);
> +	}
> +
> +	close(dir);
> +}
> +
> +static struct igt_helper_process hang_detector;
> +__noreturn static void
> +hang_detector_process(int fd, pid_t pid, dev_t rdev)
> +{
> +	struct udev_monitor *mon =
> +		udev_monitor_new_from_netlink(udev_new(), "kernel");
> +	struct pollfd pfd;
> +	int ret;
> +
> +	udev_monitor_filter_add_match_subsystem_devtype(mon, "drm", NULL);
> +	udev_monitor_enable_receiving(mon);
> +
> +	pfd.fd = udev_monitor_get_fd(mon);
> +	pfd.events = POLLIN;
> +
> +	while ((ret = poll(&pfd, 1, 2000)) >= 0) {
> +		struct udev_device *dev;
> +		dev_t devnum;
> +
> +		if (kill(pid, 0)) { /* Parent has died, so must we. */
> +			igt_warn("Parent died without killing its children (%s)\n",
> +				 __func__);
> +			break;
> +		}
> +
> +		dev = NULL;
> +		if (ret > 0)
> +			dev = udev_monitor_receive_device(mon);
> +		if (dev == NULL)
> +			continue;
> +
> +		devnum = udev_device_get_devnum(dev);
> +		if (memcmp(&rdev, &devnum, sizeof(dev_t)) == 0) {
> +			const char *str;
> +
> +			str = udev_device_get_property_value(dev, "ERROR");
> +			if (str && atoi(str) == 1) {
> +				show_kernel_stack(pid);
> +				kill(pid, SIGIO);
> +			}
> +		}
> +
> +		udev_device_unref(dev);
> +	}
> +
> +	exit(0);
> +}
> +
> +__noreturn static void sig_abort(int sig)
> +{
> +	errno = 0; /* inside a signal, last errno reporting is confusing */
> +	igt_assert(!"GPU hung");
> +}
> +
> +void igt_fork_hang_detector(int fd)
> +{
> +	struct stat st;
> +
> +	igt_assert(fstat(fd, &st) == 0);
> +
> +	/*
> +	 * Disable per-engine reset to force an error uevent. We don't
> +	 * expect to get any hangs whilst the detector is enabled (if we do
> +	 * they are a test failure!) and so the loss of per-engine reset
> +	 * functionality is not an issue.
> +	 */
> +	igt_assert(igt_params_set(fd, "reset", "%d", 1 /* only global reset */));
> +
> +	signal(SIGIO, sig_abort);
> +	igt_fork_helper(&hang_detector)
> +		hang_detector_process(fd, getppid(), st.st_rdev);
> +}
> +
> +void igt_stop_hang_detector(void)
> +{
> +	/*
> +	 * Give the uevent time to arrive. No sleep at all misses about 20% of
> +	 * hangs (at least, in the i915_hangman/detector test). A sleep of 1ms
> +	 * seems to miss about 2%, 10ms loses <1%, so 100ms should be safe.
> +	 */
> +	usleep(100 * 1000);
> +
> +	igt_stop_helper(&hang_detector);
> +}
> +
> +/**
> + * igt_check_boolean_env_var:
> + * @env_var: environment variable name
> + * @default_value: default value for the environment variable
> + *
> + * This function should be used to parse boolean environment variable options.
> + *
> + * Returns:
> + * The boolean value of the environment variable @env_var as decoded by atoi()
> + * if it is set and @default_value if the variable is not set.
> + */
> +bool igt_check_boolean_env_var(const char *env_var, bool default_value)
> +{
> +	char *val;
> +
> +	val = getenv(env_var);
> +	if (!val)
> +		return default_value;
> +
> +	return atoi(val) != 0;
> +}
> +
> +/**
> + * igt_aub_dump_enabled:
> + *
> + * Returns:
> + * True if AUB dumping is enabled with IGT_DUMP_AUB=1 in the environment, false
> + * otherwise.
> + */
> +bool igt_aub_dump_enabled(void)
> +{
> +	static int dump_aub = -1;
> +
> +	if (dump_aub == -1)
> +		dump_aub = igt_check_boolean_env_var("IGT_DUMP_AUB", false);
> +
> +	return dump_aub;
> +}
> +
> +/* other helpers */
> +/**
> + * igt_exchange_int:
> + * @array: pointer to the array of integers
> + * @i: first position
> + * @j: second position
> + *
> + * Exchanges the two values at array indices @i and @j. Useful as an exchange
> + * function for igt_permute_array().
> + */
> +void igt_exchange_int(void *array, unsigned i, unsigned j)
> +{
> +	int *int_arr, tmp;
> +	int_arr = array;
> +
> +	tmp = int_arr[i];
> +	int_arr[i] = int_arr[j];
> +	int_arr[j] = tmp;
> +}
> +
> +/**
> + * igt_exchange_int64:
> + * @array: pointer to the array of int64_t
> + * @i: first position
> + * @j: second position
> + *
> + * Exchanges the two values at array indices @i and @j. Useful as an exchange
> + * function for igt_permute_array().
> + */
> +void igt_exchange_int64(void *array, unsigned i, unsigned j)
> +{
> +	int64_t *a = array;
> +
> +	igt_swap(a[i], a[j]);
> +}
> +
> +/**
> + * igt_permute_array:
> + * @array: pointer to array
> + * @size: size of the array
> + * @exchange_func: function to exchange array elements
> + *
> + * This function randomly permutes the array using random() as the PRNG source.
> + * The @exchange_func function is called to exchange two elements in the array
> + * when needed.
> + */
> +void igt_permute_array(void *array, unsigned size,
> +                       void (*exchange_func)(void *array,
> +                                             unsigned i,
> +                                             unsigned j))
> +{
> +	int i;
> +
> +	for (i = size - 1; i > 0; i--) {
> +		/* yes, not perfectly uniform, who cares */
> +		long l = hars_petruska_f54_1_random_unsafe() % (i +1);
> +		if (i != l)
> +			exchange_func(array, i, l);
> +	}
> +}
> +
> +__attribute__((format(printf, 1, 2)))
> +static void igt_interactive_info(const char *format, ...)
> +{
> +	va_list args;
> +
> +	if (!isatty(STDERR_FILENO) || __igt_plain_output) {
> +		errno = 0; /* otherwise would be either ENOTTY or EBADF */
> +		return;
> +	}
> +
> +	if (igt_log_level > IGT_LOG_INFO)
> +		return;
> +
> +	va_start(args, format);
> +	vfprintf(stderr, format, args);
> +	va_end(args);
> +}
> +
> +
> +/**
> + * igt_progress:
> + * @header: header string to prepend to the progress indicator
> + * @i: work processed thus far
> + * @total: total amount of work
> + *
> + * This function draws a progress indicator, which is useful for running
> + * long-winded tests manually on the console. To avoid spamming log files in
> + * automated runs the progress indicator is suppressed when not running on a
> + * terminal.
> + */
> +void igt_progress(const char *header, uint64_t i, uint64_t total)
> +{
> +	int divider = 200;
> +
> +	if (i+1 >= total) {
> +		igt_interactive_info("\r%s100%%\n", header);
> +		return;
> +	}
> +
> +	if (total / 200 == 0)
> +		divider = 1;
> +
> +	/* only bother updating about every 0.5% */
> +	if (i % (total / divider) == 0)
> +		igt_interactive_info("\r%s%3llu%%", header,
> +				     (long long unsigned)i * 100 / total);
> +}
> +
> +/**
> + * igt_print_activity:
> + *
> + * Print a '.' to indicate activity. This is printed without a newline and
> + * only if output is to a terminal.
> + */
> +void igt_print_activity(void)
> +{
> +	igt_interactive_info(".");
> +}
> +
> +static int autoresume_delay;
> +
> +static const char *suspend_state_name[] = {
> +	[SUSPEND_STATE_FREEZE] = "freeze",
> +	[SUSPEND_STATE_STANDBY] = "standby",
> +	[SUSPEND_STATE_S3] = "mem", /* Forces Suspend-to-Ram (S3) */
> +	[SUSPEND_STATE_MEM] = "mem", /* Respects system default */
> +	[SUSPEND_STATE_DISK] = "disk",
> +};
> +
> +static const char *suspend_test_name[] = {
> +	[SUSPEND_TEST_NONE] = "none",
> +	[SUSPEND_TEST_FREEZER] = "freezer",
> +	[SUSPEND_TEST_DEVICES] = "devices",
> +	[SUSPEND_TEST_PLATFORM] = "platform",
> +	[SUSPEND_TEST_PROCESSORS] = "processors",
> +	[SUSPEND_TEST_CORE] = "core",
> +};
> +
> +static const char *mem_sleep_name[] = {
> +	[MEM_SLEEP_S2IDLE] = "s2idle",
> +	[MEM_SLEEP_SHALLOW] = "shallow",
> +	[MEM_SLEEP_DEEP] = "deep"
> +};
> +
> +static enum igt_suspend_test get_suspend_test(int power_dir)
> +{
> +	char *test_line;
> +	char *test_name;
> +	enum igt_suspend_test test;
> +
> +	if (faccessat(power_dir, "pm_test", R_OK, 0))
> +		return SUSPEND_TEST_NONE;
> +
> +	igt_assert((test_line = igt_sysfs_get(power_dir, "pm_test")));
> +	for (test_name = strtok(test_line, " "); test_name;
> +	     test_name = strtok(NULL, " "))
> +		if (test_name[0] == '[') {
> +			test_name[strlen(test_name) - 1] = '\0';
> +			test_name++;
> +			break;
> +		}
> +
> +	if (!test_name) {
> +	  	free(test_line);
> +		return SUSPEND_TEST_NONE;
> +	}
> +
> +	for (test = SUSPEND_TEST_NONE; test < SUSPEND_TEST_NUM; test++)
> +		if (strcmp(suspend_test_name[test], test_name) == 0)
> +			break;
> +
> +	igt_assert(test < SUSPEND_TEST_NUM);
> +
> +	free(test_line);
> +	return test;
> +}
> +
> +static void set_suspend_test(int power_dir, enum igt_suspend_test test)
> +{
> +	igt_assert(test < SUSPEND_TEST_NUM);
> +
> +	if (faccessat(power_dir, "pm_test", W_OK, 0)) {
> +		igt_require(test == SUSPEND_TEST_NONE);
> +		return;
> +	}
> +
> +	igt_assert(igt_sysfs_set(power_dir, "pm_test", suspend_test_name[test]));
> +}
> +
> +#define SQUELCH ">/dev/null 2>&1"
> +
> +static void suspend_via_rtcwake(enum igt_suspend_state state)
> +{
> +	char cmd[128];
> +	int delay, ret;
> +
> +	igt_assert(state < SUSPEND_STATE_NUM);
> +
> +	delay = igt_get_autoresume_delay(state);
> +
> +	/*
> +	 * Skip if rtcwake would fail for a reason not related to the kernel's
> +	 * suspend functionality.
> +	 */
> +	snprintf(cmd, sizeof(cmd), "rtcwake -n -s %d -m %s " SQUELCH,
> +		 delay, suspend_state_name[state]);
> +	ret = igt_system(cmd);
> +	igt_require_f(ret == 0, "rtcwake test failed with %i\n"
> +		     "This failure could mean that something is wrong with "
> +		     "the rtcwake tool or how your distro is set up.\n",
> +		      ret);
> +
> +	snprintf(cmd, sizeof(cmd), "rtcwake -s %d -m %s ",
> +		 delay, suspend_state_name[state]);
> +	ret = igt_system(cmd);
> +	if (ret) {
> +		const char *path = "suspend_stats";
> +		char *info;
> +		int dir;
> +
> +		igt_warn("rtcwake failed with %i\n"
> +			 "Check dmesg for further details.\n",
> +			 ret);
> +
> +		dir = open(igt_debugfs_mount(), O_RDONLY);
> +		info = igt_sysfs_get(dir, path);
> +		close(dir);
> +		if (info) {
> +			igt_debug("%s:\n%s\n", path, info);
> +			free(info);
> +		}
> +	}
> +	igt_assert_eq(ret, 0);
> +}
> +
> +static void suspend_via_sysfs(int power_dir, enum igt_suspend_state state)
> +{
> +	igt_assert(state < SUSPEND_STATE_NUM);
> +	igt_assert(igt_sysfs_set(power_dir, "state",
> +				 suspend_state_name[state]));
> +}
> +
> +static bool is_state_supported(int power_dir, enum igt_suspend_state state)
> +{
> +	const char *str;
> +	char *states;
> +
> +	igt_assert((states = igt_sysfs_get(power_dir, "state")));
> +
> +	str = strstr(states, suspend_state_name[state]);
> +
> +	if (!str)
> +		igt_info("State %s not supported.\nSupported States: %s\n",
> +			 suspend_state_name[state], states);
> +
> +	free(states);
> +	return str;
> +}
> +
> +static int get_mem_sleep(void)
> +{
> +	char *mem_sleep_states;
> +	char *mem_sleep_state;
> +	enum igt_mem_sleep mem_sleep;
> +	int power_dir;
> +
> +	igt_require((power_dir = open("/sys/power", O_RDONLY)) >= 0);
> +
> +	if (faccessat(power_dir, "mem_sleep", R_OK, 0))
> +		return MEM_SLEEP_NONE;
> +
> +	igt_assert((mem_sleep_states = igt_sysfs_get(power_dir, "mem_sleep")));
> +	for (mem_sleep_state = strtok(mem_sleep_states, " "); mem_sleep_state;
> +	     mem_sleep_state = strtok(NULL, " ")) {
> +		if (mem_sleep_state[0] == '[') {
> +			mem_sleep_state[strlen(mem_sleep_state) - 1] = '\0';
> +			mem_sleep_state++;
> +			break;
> +		}
> +	}
> +
> +	if (!mem_sleep_state) {
> +		free(mem_sleep_states);
> +		return MEM_SLEEP_NONE;
> +	}
> +
> +	for (mem_sleep = MEM_SLEEP_S2IDLE; mem_sleep < MEM_SLEEP_NUM; mem_sleep++) {
> +		if (strcmp(mem_sleep_name[mem_sleep], mem_sleep_state) == 0)
> +			break;
> +	}
> +
> +	igt_assert_f(mem_sleep < MEM_SLEEP_NUM, "Invalid mem_sleep state\n");
> +
> +	free(mem_sleep_states);
> +	close(power_dir);
> +	return mem_sleep;
> +}
> +
> +static void set_mem_sleep(int power_dir, enum igt_mem_sleep sleep)
> +{
> +	igt_assert(sleep < MEM_SLEEP_NUM);
> +
> +	igt_assert_eq(faccessat(power_dir, "mem_sleep", W_OK, 0), 0);
> +
> +	igt_assert(igt_sysfs_set(power_dir, "mem_sleep",
> +				 mem_sleep_name[sleep]));
> +}
> +
> +static bool is_mem_sleep_state_supported(int power_dir, enum igt_mem_sleep state)
> +{
> +	const char *str;
> +	char *mem_sleep_states;
> +
> +	igt_assert((mem_sleep_states = igt_sysfs_get(power_dir, "mem_sleep")));
> +
> +	str = strstr(mem_sleep_states, mem_sleep_name[state]);
> +
> +	if (!str)
> +		igt_info("mem_sleep state %s not supported.\nSupported mem_sleep states: %s\n",
> +			 mem_sleep_name[state], mem_sleep_states);
> +
> +	free(mem_sleep_states);
> +	return str;
> +}
> +
> +/**
> + * igt_system_suspend_autoresume:
> + * @state: an #igt_suspend_state, the target suspend state
> + * @test: an #igt_suspend_test, test point at which to complete the suspend
> + *	  cycle
> + *
> + * Execute a system suspend cycle targeting the given @state optionally
> + * completing the cycle at the given @test point and automaically wake up
> + * again. Waking up is either achieved using the RTC wake-up alarm for a full
> + * suspend cycle or a kernel timer for a suspend test cycle. The kernel timer
> + * delay for a test cycle can be configured by the suspend.pm_test_delay
> + * kernel parameter (5 sec by default).
> + *
> + * #SUSPEND_TEST_NONE specifies a full suspend cycle.
> + * The #SUSPEND_TEST_FREEZER..#SUSPEND_TEST_CORE test points can make it
> + * possible to collect error logs in case a full suspend cycle would prevent
> + * this by hanging the machine, or they can provide an idea of the faulty
> + * component by comparing fail/no-fail results at different test points.
> + *
> + * This is very handy for implementing any kind of suspend/resume test.
> + */
> +void igt_system_suspend_autoresume(enum igt_suspend_state state,
> +				   enum igt_suspend_test test)
> +{
> +	int power_dir;
> +	enum igt_suspend_test orig_test;
> +	enum igt_mem_sleep orig_mem_sleep = MEM_SLEEP_NONE;
> +
> +	igt_require((power_dir = open("/sys/power", O_RDONLY)) >= 0);
> +	igt_require(is_state_supported(power_dir, state));
> +	igt_require(test == SUSPEND_TEST_NONE ||
> +		    faccessat(power_dir, "pm_test", R_OK | W_OK, 0) == 0);
> +
> +	igt_skip_on_f(state == SUSPEND_STATE_DISK &&
> +		      !igt_get_total_swap_mb(),
> +		      "Suspend to disk requires swap space.\n");
> +
> +	orig_test = get_suspend_test(power_dir);
> +
> +	if (state == SUSPEND_STATE_S3) {
> +		orig_mem_sleep = get_mem_sleep();
> +		igt_skip_on_f(!is_mem_sleep_state_supported(power_dir, MEM_SLEEP_DEEP),
> +			      "S3 not supported in this system.\n");
> +		set_mem_sleep(power_dir, MEM_SLEEP_DEEP);
> +		igt_skip_on_f(get_mem_sleep() != MEM_SLEEP_DEEP,
> +			      "S3 not possible in this system.\n");
> +	}
> +
> +	set_suspend_test(power_dir, test);
> +
> +	if (test == SUSPEND_TEST_NONE)
> +		suspend_via_rtcwake(state);
> +	else
> +		suspend_via_sysfs(power_dir, state);
> +
> +	if (orig_mem_sleep)
> +		set_mem_sleep(power_dir, orig_mem_sleep);
> +
> +	set_suspend_test(power_dir, orig_test);
> +	close(power_dir);
> +}
> +
> +static int original_autoresume_delay;
> +
> +static void igt_restore_autoresume_delay(int sig)
> +{
> +	int delay_fd;
> +	char delay_str[10];
> +
> +	igt_require((delay_fd = open("/sys/module/suspend/parameters/pm_test_delay",
> +				    O_WRONLY)) >= 0);
> +
> +	snprintf(delay_str, sizeof(delay_str), "%d", original_autoresume_delay);
> +	igt_require(write(delay_fd, delay_str, strlen(delay_str)));
> +
> +	close(delay_fd);
> +}
> +
> +/**
> + * igt_set_autoresume_delay:
> + * @delay_secs: The delay in seconds before resuming the system
> + *
> + * Sets how long we wait to resume the system after suspending it, using the
> + * suspend.pm_test_delay variable. On exit, the original delay value is
> + * restored.
> + */
> +void igt_set_autoresume_delay(int delay_secs)
> +{
> +	int delay_fd;
> +	char delay_str[10];
> +
> +	delay_fd = open("/sys/module/suspend/parameters/pm_test_delay", O_RDWR);
> +
> +	if (delay_fd >= 0) {
> +		if (!original_autoresume_delay) {
> +			igt_require(read(delay_fd, delay_str,
> +					 sizeof(delay_str)));
> +			original_autoresume_delay = atoi(delay_str);
> +			igt_install_exit_handler(igt_restore_autoresume_delay);
> +		}
> +
> +		snprintf(delay_str, sizeof(delay_str), "%d", delay_secs);
> +		igt_require(write(delay_fd, delay_str, strlen(delay_str)));
> +
> +		close(delay_fd);
> +	}
> +
> +	autoresume_delay = delay_secs;
> +}
> +
> +/**
> + * igt_get_autoresume_delay:
> + * @state: an #igt_suspend_state, the target suspend state
> + *
> + * Retrieves how long we wait to resume the system after suspending it.
> + * This can either be set through igt_set_autoresume_delay or be a default
> + * value that depends on the suspend state.
> + *
> + * Returns: The autoresume delay, in seconds.
> + */
> +int igt_get_autoresume_delay(enum igt_suspend_state state)
> +{
> +	int delay;
> +
> +	if (autoresume_delay)
> +		delay = autoresume_delay;
> +	else
> +		delay = state == SUSPEND_STATE_DISK ? 30 : 15;
> +
> +	return delay;
> +}
> +
> +/**
> + * igt_drop_root:
> + *
> + * Drop root privileges and make sure it actually worked. Useful for tests
> + * which need to check security constraints. Note that this should only be
> + * called from manually forked processes, since the lack of root privileges
> + * will wreak havoc with the automatic cleanup handlers.
> + */
> +void igt_drop_root(void)
> +{
> +	igt_assert_eq(getuid(), 0);
> +
> +	igt_assert_eq(setgroups(0, NULL), 0);
> +	igt_assert_eq(setgid(2), 0);
> +	igt_assert_eq(setuid(2), 0);
> +
> +	igt_assert_eq(getgroups(0, NULL), 0);
> +	igt_assert_eq(getgid(), 2);
> +	igt_assert_eq(getuid(), 2);
> +}
> +
> +/**
> + * igt_debug_wait_for_keypress:
> + * @var: var lookup to to enable this wait
> + *
> + * Waits for a key press when run interactively and when the corresponding debug
> + * var is set in the --interactive-debug=$var variable. Multiple keys
> + * can be specified as a comma-separated list or alternatively "all" if a wait
> + * should happen for all cases. Calling this function with "all" will assert.
> + *
> + * When not connected to a terminal interactive_debug is ignored
> + * and execution immediately continues.
> + *
> + * This is useful for display tests where under certain situation manual
> + * inspection of the display is useful. Or when running a testcase in the
> + * background.
> + */
> +void igt_debug_wait_for_keypress(const char *var)
> +{
> +	struct termios oldt, newt;
> +
> +	if (!isatty(STDIN_FILENO)) {
> +		errno = 0; /* otherwise would be either ENOTTY or EBADF */
> +		return;
> +	}
> +
> +	if (!igt_interactive_debug)
> +		return;
> +
> +	if (strstr(var, "all"))
> +		igt_assert_f(false, "Bug in test: Do not call igt_debug_wait_for_keypress with \"all\"\n");
> +
> +	if (!strstr(igt_interactive_debug, var) &&
> +	    !strstr(igt_interactive_debug, "all"))
> +		return;
> +
> +	igt_info("Press any key to continue ...\n");
> +
> +	tcgetattr ( STDIN_FILENO, &oldt );
> +	newt = oldt;
> +	newt.c_lflag &= ~( ICANON | ECHO );
> +	tcsetattr ( STDIN_FILENO, TCSANOW, &newt );
> +	getchar();
> +	tcsetattr ( STDIN_FILENO, TCSANOW, &oldt );
> +}
> +
> +/**
> + * igt_debug_interactive_mode_check:
> + * @var: var lookup to to enable this wait
> + * @expected: message to be printed as expected behaviour before wait for keys Y/n
> + *
> + * Waits for a key press when run interactively and when the corresponding debug
> + * var is set in the --interactive-debug=$var variable. Multiple vars
> + * can be specified as a comma-separated list or alternatively "all" if a wait
> + * should happen for all cases.
> + *
> + * This is useful for display tests where under certain situation manual
> + * inspection of the display is useful. Or when running a testcase in the
> + * background.
> + *
> + * When not connected to a terminal interactive_debug is ignored
> + * and execution immediately continues. For this reason by default this function
> + * returns true. It returns false only when N/n is pressed indicating the
> + * user isn't seeing what was expected.
> + *
> + * Force test fail when N/n is pressed.
> + */
> +void igt_debug_interactive_mode_check(const char *var, const char *expected)
> +{
> +	struct termios oldt, newt;
> +	char key;
> +
> +	if (!isatty(STDIN_FILENO)) {
> +		errno = 0; /* otherwise would be either ENOTTY or EBADF */
> +		return;
> +	}
> +
> +	if (!igt_interactive_debug)
> +		return;
> +
> +	if (!strstr(igt_interactive_debug, var) &&
> +	    !strstr(igt_interactive_debug, "all"))
> +		return;
> +
> +	igt_info("Is %s [Y/n]", expected);
> +
> +	tcgetattr ( STDIN_FILENO, &oldt );
> +	newt = oldt;
> +	newt.c_lflag &= ~ICANON;
> +	tcsetattr ( STDIN_FILENO, TCSANOW, &newt );
> +	key = getchar();
> +	tcsetattr ( STDIN_FILENO, TCSANOW, &oldt );
> +
> +	igt_info("\n");
> +
> +	igt_assert(key != 'n' && key != 'N');
> +}
> +
> +/**
> + * igt_lock_mem:
> + * @size: the amount of memory to lock into RAM, in MB
> + *
> + * Allocate @size MB of memory and lock it into RAM. This releases any
> + * previously locked memory.
> + *
> + * Use #igt_unlock_mem to release the currently locked memory.
> + */
> +static char *locked_mem;
> +static size_t locked_size;
> +
> +void igt_lock_mem(size_t size)
> +{
> +	long pagesize = sysconf(_SC_PAGESIZE);
> +	size_t i;
> +	int ret;
> +
> +	if (size == 0) {
> +		return;
> +	}
> +
> +	if (locked_mem) {
> +		igt_unlock_mem();
> +		igt_warn("Unlocking previously locked memory.\n");
> +	}
> +
> +	locked_size = size * 1024 * 1024;
> +
> +	locked_mem = malloc(locked_size);
> +	igt_require_f(locked_mem,
> +		      "Could not malloc %zdMiB for locking.\n", size);
> +
> +	/* write into each page to ensure it is allocated */
> +	for (i = 0; i < locked_size; i += pagesize)
> +		locked_mem[i] = i;
> +
> +	ret = mlock(locked_mem, locked_size);
> +	igt_assert_f(ret == 0, "Could not mlock %zdMiB.\n", size);
> +}
> +
> +/**
> + * igt_unlock_mem:
> + *
> + * Release and free the RAM used by #igt_lock_mem.
> + */
> +void igt_unlock_mem(void)
> +{
> +	if (!locked_mem)
> +		return;
> +
> +	munlock(locked_mem, locked_size);
> +
> +	free(locked_mem);
> +	locked_mem = NULL;
> +}
> +
> +/**
> + * igt_is_process_running:
> + * @comm: Name of process in the form found in /proc/pid/comm (limited to 15
> + * chars)
> + *
> + * Returns: true in case the process has been found, false otherwise.
> + *
> + * This function checks in the process table for an entry with the name @comm.
> + */
> +int igt_is_process_running(const char *comm)
> +{
> +	PROCTAB *proc;
> +	proc_t *proc_info;
> +	bool found = false;
> +
> +	proc = openproc(PROC_FILLCOM | PROC_FILLSTAT);
> +	igt_assert(proc != NULL);
> +
> +	while ((proc_info = readproc(proc, NULL))) {
> +		if (!strncasecmp(proc_info->cmd, comm, sizeof(proc_info->cmd))) {
> +			freeproc(proc_info);
> +			found = true;
> +			break;
> +		}
> +		freeproc(proc_info);
> +	}
> +
> +	closeproc(proc);
> +	return found;
> +}
> +
> +/**
> + * igt_terminate_process:
> + * @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_terminate_process(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)
> +{
> +	struct pinfo p = { .pid = info->tid, .comm = info->cmd, .fn = fn };
> +
> +	if (!*state)
> +		igt_show_stat_header();
> +
> +	__igt_show_stat(&p);
> +	++*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);
> +	}
> +
> +	closedir(dp);
> +}
> +
> +/*
> + * 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[30];
> +	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_warn("%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 void pulseaudio_unload_module(proc_t *proc_info)
> +{
> +	struct igt_helper_process pa_proc = {};
> +	char xdg_dir[PATH_MAX];
> +	const char *homedir;
> +	struct passwd *pw;
> +
> +	igt_fork_helper(&pa_proc) {
> +		pw = getpwuid(proc_info->euid);
> +		homedir = pw->pw_dir;
> +		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", proc_info->euid);
> +
> +		igt_info("Request 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_wait_helper(&pa_proc);
> +}
> +
> +static int pipewire_pulse_pid = 0;
> +static int pipewire_pw_reserve_pid = 0;
> +static struct igt_helper_process pw_reserve_proc = {};
> +
> +static void pipewire_reserve_wait(void)
> +{
> +	char xdg_dir[PATH_MAX];
> +	const char *homedir;
> +	struct passwd *pw;
> +	proc_t *proc_info;
> +	PROCTAB *proc;
> +
> +	igt_fork_helper(&pw_reserve_proc) {
> +		igt_info("Preventing pipewire-pulse to use the audio drivers\n");
> +
> +		proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
> +		igt_assert(proc != NULL);
> +
> +		while ((proc_info = readproc(proc, NULL))) {
> +			if (pipewire_pulse_pid == proc_info->tid)
> +				break;
> +			freeproc(proc_info);
> +		}
> +		closeproc(proc);
> +
> +		/* Sanity check: if it can't find the process, it means it has gone */
> +		if (pipewire_pulse_pid != proc_info->tid)
> +			exit(0);
> +
> +		pw = getpwuid(proc_info->euid);
> +		homedir = pw->pw_dir;
> +		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", proc_info->euid);
> +		setgid(proc_info->egid);
> +		setuid(proc_info->euid);
> +		clearenv();
> +		setenv("HOME", homedir, 1);
> +		setenv("XDG_RUNTIME_DIR",xdg_dir, 1);
> +		freeproc(proc_info);
> +
> +		/*
> +		 * pw-reserve will run in background. It will only exit when
> +		 * igt_kill_children() is called later on. So, it shouldn't
> +		 * call igt_waitchildren(). Instead, just exit with the return
> +		 * code from pw-reserve.
> +		 */
> +		exit(system("pw-reserve -n Audio0 -r"));
> +	}
> +}
> +
> +/* Maximum time waiting for pw-reserve to start running */
> +#define PIPEWIRE_RESERVE_MAX_TIME 1000 /* milisseconds */
> +
> +int pipewire_pulse_start_reserve(void)
> +{
> +	bool is_pw_reserve_running = false;
> +	proc_t *proc_info;
> +	int attempts = 0;
> +	PROCTAB *proc;
> +
> +	if (!pipewire_pulse_pid)
> +		return 0;
> +
> +	pipewire_reserve_wait();
> +
> +	/*
> +	 * Note: using pw-reserve to stop using audio only works with
> +	 * pipewire version 0.3.50 or upper.
> +	 */
> +	for (attempts = 0; attempts < PIPEWIRE_RESERVE_MAX_TIME; attempts++) {
> +		usleep(1000);
> +		proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
> +		igt_assert(proc != NULL);
> +
> +		while ((proc_info = readproc(proc, NULL))) {
> +			if (!strcmp(proc_info->cmd, "pw-reserve")) {
> +				is_pw_reserve_running = true;
> +				pipewire_pw_reserve_pid = proc_info->tid;
> +				freeproc(proc_info);
> +				break;
> +			}
> +			freeproc(proc_info);
> +		}
> +		closeproc(proc);
> +		if (is_pw_reserve_running)
> +			break;
> +	}
> +	if (!is_pw_reserve_running) {
> +		igt_warn("Failed to remove audio drivers from pipewire\n");
> +		return 1;
> +	}
> +	/* Let's grant some time for pw_reserve to notify pipewire via D-BUS */
> +	usleep(50000);
> +
> +	/*
> +	 * pw-reserve is running, and should have stopped using the audio
> +	 * drivers. We can now remove the driver.
> +	 */
> +
> +	return 0;
> +}
> +
> +void pipewire_pulse_stop_reserve(void)
> +{
> +	if (!pipewire_pulse_pid)
> +		return;
> +
> +	igt_stop_helper(&pw_reserve_proc);
> +}
> +
> +/**
> + * __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
> + * @pipewire_pulse_pid: PID of pipewire-pulse process
> + *
> + * 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 process is pipewire-pulse, it can't be killed, as systemd will
> + *	  respawn it. So, instead, the caller should call pw-reserve, remove
> + *	  the kernel driver and then stop pw-reserve. On such case, this
> + *	  function returns the PID of pipewire-pulse, but won't touch it.
> + * 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;
> +
> +	/*
> +	 * Terminating pipewire-pulse require an special procedure, which
> +	 * is only available at version 0.3.50 and upper. 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.
> +	 */
> +	if (!strcmp(proc_info->cmd, "pipewire-pulse")) {
> +		igt_info("process %d (%s) is using audio device. Should be requested to stop using them.\n",
> +			 proc_info->tid, proc_info->cmd);
> +		pipewire_pulse_pid = proc_info->tid;
> +		return 0;
> +	}
> +	/*
> +	 * pipewire-pulse itself doesn't hook into a /dev/snd device. Instead,
> +	 * the actual binding happens at the Pipewire Session Manager, e.g.
> +	 * either wireplumber or pipewire media-session.
> +	 *
> +	 * Just killing such processes won't produce any effect, as systemd
> +	 * will respawn them. So, just ignore here, they'll honor pw-reserve,
> +	 * when the time comes.
> +	 */
> +	if (!strcmp(proc_info->cmd, "pipewire-media-session"))
> +		return 0;
> +	if (!strcmp(proc_info->cmd, "wireplumber"))
> +		return 0;
> +
> +	dp = opendir(proc_path);
> +	if (!dp && errno == ENOENT)
> +		return 0;
> +	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;
> +		}
> +
> +		/* 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);
> +	pipewire_pulse_pid = 0;
> +
> +	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;
> +	struct sigaction oldact;
> +	struct igt_mean mean;
> +
> +	int sig;
> +} igt_siglatency;
> +
> +static long delay(void)
> +{
> +	return hars_petruska_f54_1_random_unsafe() % (NSEC_PER_SEC / 1000);
> +}
> +
> +static double elapsed(const struct timespec *now, const struct timespec *last)
> +{
> +	double nsecs;
> +
> +	nsecs = now->tv_nsec - last ->tv_nsec;
> +	nsecs += 1e9*(now->tv_sec - last->tv_sec);
> +
> +	return nsecs;
> +}
> +
> +static void siglatency(int sig, siginfo_t *info, void *arg)
> +{
> +	struct itimerspec its;
> +
> +	clock_gettime(CLOCK_MONOTONIC, &its.it_value);
> +	if (info)
> +		igt_mean_add(&igt_siglatency.mean,
> +			     elapsed(&its.it_value, &igt_siglatency.target));
> +	igt_siglatency.target = its.it_value;
> +
> +	its.it_value.tv_nsec += 100 * 1000;
> +	its.it_value.tv_nsec += delay();
> +	if (its.it_value.tv_nsec >= NSEC_PER_SEC) {
> +		its.it_value.tv_nsec -= NSEC_PER_SEC;
> +		its.it_value.tv_sec += 1;
> +	}
> +	its.it_interval.tv_sec = its.it_interval.tv_nsec = 0;
> +	timer_settime(igt_siglatency.timer, TIMER_ABSTIME, &its, NULL);
> +}
> +
> +void igt_start_siglatency(int sig)
> +{
> +	struct sigevent sev;
> +	struct sigaction act;
> +
> +	if (sig <= 0)
> +		sig = SIGRTMIN;
> +
> +	if (igt_siglatency.sig)
> +		(void)igt_stop_siglatency(NULL);
> +	igt_assert(igt_siglatency.sig == 0);
> +	igt_siglatency.sig = sig;
> +
> +	memset(&sev, 0, sizeof(sev));
> +	sev.sigev_notify = SIGEV_SIGNAL | SIGEV_THREAD_ID;
> +	sev.sigev_notify_thread_id = gettid();
> +	sev.sigev_signo = sig;
> +	timer_create(CLOCK_MONOTONIC, &sev, &igt_siglatency.timer);
> +
> +	memset(&act, 0, sizeof(act));
> +	act.sa_sigaction = siglatency;
> +	sigaction(sig, &act, &igt_siglatency.oldact);
> +
> +	siglatency(sig, NULL, NULL);
> +}
> +
> +double igt_stop_siglatency(struct igt_mean *result)
> +{
> +	double mean = igt_mean_get(&igt_siglatency.mean);
> +
> +	if (result)
> +		*result = igt_siglatency.mean;
> +
> +	sigaction(igt_siglatency.sig, &igt_siglatency.oldact, NULL);
> +	timer_delete(igt_siglatency.timer);
> +	memset(&igt_siglatency, 0, sizeof(igt_siglatency));
> +
> +	return mean;
> +}
> +
> +bool igt_allow_unlimited_files(void)
> +{
> +	struct rlimit rlim;
> +	unsigned nofile_rlim = 1024*1024;
> +
> +	FILE *file = fopen("/proc/sys/fs/nr_open", "r");
> +	if (file) {
> +		igt_assert(fscanf(file, "%u", &nofile_rlim) == 1);
> +		igt_info("System limit for open files is %u\n", nofile_rlim);
> +		fclose(file);
> +	}
> +
> +	if (getrlimit(RLIMIT_NOFILE, &rlim))
> +		return false;
> +
> +	rlim.rlim_cur = nofile_rlim;
> +	rlim.rlim_max = nofile_rlim;
> +	return setrlimit(RLIMIT_NOFILE, &rlim) == 0;
> +}
> +
> +/**
> + * vfs_file_max: report maximum number of files
> + *
> + * Get the global system-wide maximum of open files the kernel allows,
> + * by reading /proc/sys/fs/file-max. Fails the current subtest if
> + * reading the file fails, and returns a suitable best guess if it
> + * cannot be opened.
> + *
> + * Returns: System-wide maximum of open files, or a best effort guess.
> + */
> +uint64_t vfs_file_max(void)
> +{
> +	static long long unsigned max;
> +	if (max == 0) {
> +		FILE *file = fopen("/proc/sys/fs/file-max", "r");
> +		max = 80000;
> +		if (file) {
> +			igt_assert(fscanf(file, "%llu", &max) == 1);
> +			fclose(file);
> +		}
> +	}
> +	return max;
> +}
> +
> +void *igt_memdup(const void *ptr, size_t len)
> +{
> +	void *dup;
> +
> +	dup = malloc(len);
> +	if (dup)
> +		memcpy(dup, ptr, len);
> +
> +	return dup;
> +}
> diff --git a/lib/meson.build b/lib/meson.build
> index cef2d0ff..cb83ee99 100644
> --- a/lib/meson.build
> +++ b/lib/meson.build
> @@ -103,7 +103,6 @@ lib_deps = [
>  	libdrm,
>  	libdw,
>  	libkmod,
> -	libprocps,
>  	libudev,
>  	math,
>  	pciaccess,
> @@ -173,6 +172,12 @@ if chamelium.found()
>  	lib_sources += 'monitor_edids/monitor_edids_helper.c'
>  endif
>  
> +if libprocps.found()
> +	lib_deps += libprocps
> +else
> +	lib_deps += libproc2
> +endif
> +

If we want to use newer version maybe this should be other way:
if libprocps2.found()
	lib_deps += libproc2
else
	lib_deps += libprocps
endif

Regards,
Kamil

>  if get_option('srcdir') != ''
>      srcdir = join_paths(get_option('srcdir'), 'tests')
>  else
> diff --git a/lib/meson.build.orig b/lib/meson.build.orig
> new file mode 100644
> index 00000000..cef2d0ff
> --- /dev/null
> +++ b/lib/meson.build.orig
> @@ -0,0 +1,358 @@
> +lib_sources = [
> +	'drmtest.c',
> +	'huc_copy.c',
> +	'i915/gem.c',
> +	'i915/gem_context.c',
> +	'i915/gem_create.c',
> +	'i915/gem_engine_topology.c',
> +	'i915/gem_scheduler.c',
> +	'i915/gem_submission.c',
> +	'i915/gem_ring.c',
> +	'i915/gem_mman.c',
> +	'i915/gem_vm.c',
> +	'i915/intel_memory_region.c',
> +	'i915/intel_mocs.c',
> +	'i915/i915_blt.c',
> +	'i915/i915_crc.c',
> +	'igt_collection.c',
> +	'igt_color_encoding.c',
> +	'igt_crc.c',
> +	'igt_debugfs.c',
> +	'igt_device.c',
> +	'igt_device_scan.c',
> +	'igt_drm_fdinfo.c',
> +	'igt_aux.c',
> +	'igt_gt.c',
> +	'igt_halffloat.c',
> +	'igt_hwmon.c',
> +	'igt_io.c',
> +	'igt_matrix.c',
> +	'igt_os.c',
> +	'igt_params.c',
> +	'igt_perf.c',
> +	'igt_pipe_crc.c',
> +	'igt_power.c',
> +	'igt_primes.c',
> +	'igt_rand.c',
> +	'igt_stats.c',
> +	'igt_syncobj.c',
> +	'igt_sysfs.c',
> +	'igt_sysrq.c',
> +	'igt_taints.c',
> +	'igt_thread.c',
> +	'igt_types.c',
> +	'igt_vec.c',
> +	'igt_vgem.c',
> +	'igt_x86.c',
> +	'instdone.c',
> +	'intel_allocator.c',
> +	'intel_allocator_msgchannel.c',
> +	'intel_allocator_random.c',
> +	'intel_allocator_reloc.c',
> +	'intel_allocator_simple.c',
> +	'intel_batchbuffer.c',
> +	'intel_bufops.c',
> +	'intel_chipset.c',
> +	'intel_ctx.c',
> +	'intel_device_info.c',
> +	'intel_mmio.c',
> +	'ioctl_wrappers.c',
> +	'media_spin.c',
> +	'media_fill.c',
> +	'gpgpu_fill.c',
> +	'gpu_cmds.c',
> +	'rendercopy_i915.c',
> +	'rendercopy_i830.c',
> +	'rendercopy_gen4.c',
> +	'rendercopy_gen6.c',
> +	'rendercopy_gen7.c',
> +	'rendercopy_gen8.c',
> +	'rendercopy_gen9.c',
> +	'runnercomms.c',
> +	'sw_sync.c',
> +	'intel_aux_pgtable.c',
> +	'intel_reg_map.c',
> +	'intel_iosf.c',
> +	'igt_kms.c',
> +	'igt_fb.c',
> +	'igt_core.c',
> +	'igt_draw.c',
> +	'igt_list.c',
> +	'igt_map.c',
> +	'igt_pm.c',
> +	'igt_dummyload.c',
> +	'igt_store.c',
> +	'uwildmat/uwildmat.c',
> +	'igt_kmod.c',
> +	'igt_panfrost.c',
> +	'igt_v3d.c',
> +	'igt_vc4.c',
> +	'igt_psr.c',
> +	'igt_amd.c',
> +	'igt_edid.c',
> +	'igt_eld.c',
> +	'igt_infoframe.c',
> +	'veboxcopy_gen12.c',
> +	'igt_msm.c',
> +]
> +
> +lib_deps = [
> +	cairo,
> +	glib,
> +	libatomic,
> +	libdrm,
> +	libdw,
> +	libkmod,
> +	libprocps,
> +	libudev,
> +	math,
> +	pciaccess,
> +	pixman,
> +	pthreads,
> +	realtime,
> +	zlib
> +]
> +
> +if libdrm_intel.found()
> +	lib_deps += libdrm_intel
> +else
> +	lib_sources += 'stubs/drm/intel_bufmgr.c'
> +	inc = [ inc, include_directories('stubs/drm') ]
> +endif
> +
> +if libdrm_nouveau.found()
> +	lib_deps += libdrm_nouveau
> +	lib_sources += [
> +		'igt_nouveau.c',
> +		'nouveau/cea0b5.c'
> +	]
> +endif
> +
> +if libdrm_amdgpu.found()
> +	lib_deps += libdrm_amdgpu
> +	lib_sources += [
> +		'amdgpu/amd_memory.c',
> +		'amdgpu/amd_command_submission.c',
> +		'amdgpu/amd_compute.c',
> +		'amdgpu/amd_gfx.c',
> +		'amdgpu/amd_ip_blocks.c',
> +		'amdgpu/amd_shaders.c',
> +		'amdgpu/amd_gfx_v8_0.c',
> +		'amdgpu/amd_gfx_v9_0.c',
> +		'amdgpu/amd_dispatch_helpers.c',
> +		'amdgpu/amd_dispatch.c',
> +		'amdgpu/amd_deadlock_helpers.c',
> +		'amdgpu/amd_pci_unplug.c',
> +		'amdgpu/xalloc.h'
> +	]
> +endif
> +
> +if libunwind.found()
> +	lib_deps += libunwind
> +else
> +	inc = [ inc, include_directories('stubs/libunwind') ]
> +endif
> +
> +if valgrind.found()
> +	lib_deps += valgrind
> +endif
> +
> +if gsl.found()
> +	lib_deps += gsl
> +	lib_sources += [ 'igt_frame.c', 'igt_audio.c' ]
> +endif
> +
> +if alsa.found()
> +	lib_deps += alsa
> +	lib_sources += 'igt_alsa.c'
> +endif
> +
> +if chamelium.found()
> +	lib_deps += chamelium
> +	lib_sources += [ 'igt_chamelium.c', 'igt_chamelium_stream.c' ]
> +	lib_sources += 'monitor_edids/monitor_edids_helper.c'
> +endif
> +
> +if get_option('srcdir') != ''
> +    srcdir = join_paths(get_option('srcdir'), 'tests')
> +else
> +    srcdir = join_paths(meson.source_root(), 'tests')
> +endif
> +
> +if get_option('version_hash') != ''
> +    vcs_command = ['echo', get_option('version_hash')]
> +else
> +    vcs_command = [ 'git', 'log', '-n1', '--pretty=format:g%h' ]
> +endif
> +
> +lib_version = vcs_tag(input : 'version.h.in', output : 'version.h',
> +		      fallback : 'NO-GIT',
> +		      command : vcs_command )
> +
> +lib_intermediates = []
> +foreach f: lib_sources
> +    name = f.underscorify()
> +    lib = static_library('igt-' + name,
> +	[ f, lib_version ],
> +	include_directories: inc,
> +	dependencies : lib_deps,
> +	c_args : [
> +	    '-DIGT_DATADIR="@0@"'.format(join_paths(prefix, datadir)),
> +	    '-DIGT_SRCDIR="@0@"'.format(srcdir),
> +	    '-DIGT_LOG_DOMAIN="@0@"'.format(f.split('.')[0]),
> +	])
> +
> +    lib_intermediates += lib
> +endforeach
> +
> +lib_igt_build = shared_library('igt',
> +    ['dummy.c'],
> +    link_whole: lib_intermediates,
> +    dependencies: lib_deps,
> +    install : true,
> +    soversion : '0',
> +)
> +
> +lib_igt = declare_dependency(link_with : lib_igt_build,
> +			    include_directories : inc)
> +
> +igt_deps = [ lib_igt ] + lib_deps
> +
> +lin_igt_chipset_build = static_library('igt_chipset',
> +                                       ['intel_chipset.c',
> +                                        'intel_device_info.c'],
> +                                       include_directories : inc)
> +
> +lib_igt_chipset = declare_dependency(link_with : lin_igt_chipset_build,
> +                                     include_directories : inc)
> +
> +lib_igt_perf_build = static_library('igt_perf',
> +	['igt_perf.c'],
> +	include_directories : inc)
> +
> +lib_igt_perf = declare_dependency(link_with : lib_igt_perf_build,
> +				  include_directories : inc)
> +
> +scan_dep = [
> +	glib,
> +	libudev,
> +]
> +
> +lib_igt_device_scan_build = static_library('igt_device_scan',
> +	['igt_device_scan.c',
> +	'igt_list.c',
> +	'igt_tools_stub.c',
> +	'intel_device_info.c',
> +	],
> +	dependencies : scan_dep,
> +	include_directories : inc)
> +
> +lib_igt_device_scan = declare_dependency(link_with : lib_igt_device_scan_build,
> +				  include_directories : inc)
> +
> +lib_igt_drm_fdinfo_build = static_library('igt_drm_fdinfo',
> +	['igt_drm_fdinfo.c'],
> +	include_directories : inc)
> +
> +lib_igt_drm_fdinfo = declare_dependency(link_with : lib_igt_drm_fdinfo_build,
> +				  include_directories : inc)
> +i915_perf_files = [
> +  'igt_list.c',
> +  'i915/perf.c',
> +  'i915/perf_data_reader.c',
> +]
> +
> +i915_perf_hardware = [
> +  'hsw',
> +  'bdw', 'chv',
> +  'sklgt2', 'sklgt3', 'sklgt4',
> +  'kblgt2', 'kblgt3',
> +  'cflgt2', 'cflgt3',
> +  'bxt', 'glk',
> +  'cnl',
> +  'icl', 'ehl',
> +  'tglgt1', 'tglgt2', 'rkl', 'dg1', 'adl',
> +  'acmgt1', 'acmgt2', 'acmgt3',
> +]
> +
> +i915_xml_files = []
> +foreach hw : i915_perf_hardware
> +  i915_xml_files += files('i915/perf-configs/oa-@0@.xml'.format(hw))
> +endforeach
> +
> +i915_perf_files += custom_target(
> +  'i915-perf-equations',
> +  input : [ 'i915/perf-configs/perf-equations-codegen.py' ] + i915_xml_files,
> +  output : [ 'i915_perf_equations.c', 'i915_perf_equations.h' ],
> +  command : [
> +    python3, '@INPUT0@',
> +    '--code', '@OUTPUT0@',
> +    '--header', '@OUTPUT1@',
> +    i915_xml_files,
> +  ])
> +
> +foreach hw : i915_perf_hardware
> +  i915_perf_files += custom_target(
> +    'i915-perf-registers-@0@'.format(hw),
> +    input : [ 'i915/perf-configs/perf-registers-codegen.py',
> +              'i915/perf-configs/oa-@0@.xml'.format(hw) ],
> +    output : [ 'i915_perf_registers_@0@.c'.format(hw),
> +               'i915_perf_registers_@0@.h'.format(hw), ],
> +    command : [
> +      python3, '@INPUT0@',
> +      '--code', '@OUTPUT0@',
> +      '--header', '@OUTPUT1@',
> +      '--xml-file', '@INPUT1@'
> +    ])
> +  i915_perf_files += custom_target(
> +    'i915-perf-metrics-@0@'.format(hw),
> +    input : [ 'i915/perf-configs/perf-metricset-codegen.py',
> +              'i915/perf-configs/oa-@0@.xml'.format(hw) ],
> +    output : [ 'i915_perf_metrics_@0@.c'.format(hw),
> +               'i915_perf_metrics_@0@.h'.format(hw), ],
> +    command : [
> +      python3, '@INPUT0@',
> +      '--code', '@OUTPUT0@',
> +      '--header', '@OUTPUT1@',
> +      '--equations-include', 'i915_perf_equations.h',
> +      '--registers-include', 'i915_perf_registers_@0@.h'.format(hw),
> +      '--xml-file', '@INPUT1@',
> +    ])
> +endforeach
> +
> +lib_igt_i915_perf_build = shared_library(
> +  'i915_perf',
> +  i915_perf_files,
> +  dependencies: lib_igt_chipset,
> +  include_directories : inc,
> +  install: true,
> +  soversion: '1.5')
> +
> +lib_igt_i915_perf = declare_dependency(
> +  link_with : lib_igt_i915_perf_build,
> +  include_directories : inc)
> +
> +install_headers(
> +  'igt_list.h',
> +  'intel_chipset.h',
> +  'i915/perf.h',
> +  'i915/perf_data.h',
> +  'i915/perf_data_reader.h',
> +  subdir : 'i915-perf'
> +)
> +
> +pkgconf = configuration_data()
> +
> +pkgconf.set('prefix', get_option('prefix'))
> +pkgconf.set('exec_prefix', '${prefix}')
> +pkgconf.set('libdir', '${prefix}/@0@'.format(get_option('libdir')))
> +pkgconf.set('includedir', '${prefix}/@0@'.format(get_option('includedir')))
> +pkgconf.set('i915_perf_version', '1.5.1')
> +
> +configure_file(
> +  input : 'i915-perf.pc.in',
> +  output : 'i915-perf.pc',
> +  configuration : pkgconf,
> +  install_dir : pkgconfigdir)
> +
> +subdir('tests')
> diff --git a/meson.build b/meson.build
> index 3e937f5a..475680ac 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -121,7 +121,15 @@ build_info += 'With libdrm: ' + ','.join(libdrm_info)
>  
>  pciaccess = dependency('pciaccess', version : '>=0.10')
>  libkmod = dependency('libkmod')
> -libprocps = dependency('libprocps', required : true)
> +libprocps = dependency('libprocps', required : false)
> +libproc2 = dependency('libproc2', required : false)
> +if libprocps.found()
> +  config.set('HAVE_LIBPROCPS', 1)
> +elif libproc2.found()
> +  config.set('HAVE_LIBPROC2', 1)
> +else
> +  error('Either libprocps or libproc2 is required')
> +endif
>  
>  libunwind = dependency('libunwind', required : get_option('libunwind'))
>  build_info += 'With libunwind: @0@'.format(libunwind.found())
> -- 
> 2.30.2
> 

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [igt-dev] [PATCH i-g-t] Use the new procps library libproc2
  2022-11-17 11:17 ` [igt-dev] [PATCH i-g-t] " Kamil Konieczny
@ 2022-11-17 11:29   ` Petri Latvala
  2022-11-17 21:13   ` Craig Small
  1 sibling, 0 replies; 11+ messages in thread
From: Petri Latvala @ 2022-11-17 11:29 UTC (permalink / raw)
  To: Kamil Konieczny, igt-dev, Craig Small

On Thu, Nov 17, 2022 at 12:17:38PM +0100, Kamil Konieczny wrote:
> Hi Craig,
> 
> On 2022-11-17 at 10:41:29 +0200, Petri Latvala wrote:
> > From: Craig Small <csmall@dropbear.xyz>
> > 
> > ---
> >  lib/igt_aux.c        |  246 +++++-
> >  lib/igt_aux.c.orig   | 1921 ++++++++++++++++++++++++++++++++++++++++++
> -- ^^^^^^^^^^^^^^^^^^
> 
> >  lib/meson.build      |    7 +-
> >  lib/meson.build.orig |  358 ++++++++
> -- ^^^^^^^^^^^^^^^^^^^^
> 
> These should not be there ?

Those are my mistakes.


-- 
Petri Latvala


> 
> >  meson.build          |   10 +-
> >  5 files changed, 2500 insertions(+), 42 deletions(-)
> >  create mode 100644 lib/igt_aux.c.orig
> >  create mode 100644 lib/meson.build.orig
> > 
> > diff --git a/lib/igt_aux.c b/lib/igt_aux.c
> > index 15e30440..2aee1275 100644
> > --- a/lib/igt_aux.c
> > +++ b/lib/igt_aux.c
> > @@ -52,8 +52,16 @@
> >  #include <assert.h>
> >  #include <grp.h>
> >  
> > +#ifdef HAVE_LIBPROCPS
> >  #include <proc/readproc.h>
> > +#endif
> > +#ifdef HAVE_LIBPROC2
> -- ^
> Maybe just #elif ?
> 
> > +#include <libproc2/pids.h>
> > +#endif
> > +
> >  #include <libudev.h>
> > +#include <linux/limits.h>
> -- ^^^^^^^^^^^^^^^^^^
> > +#include <dirent.h>
> -- ^^^^^^^^^^^^^^^^^^
> What this has with libproc2 ?
> Note also that FreeBSD have its own limits.h
> 
> >  
> >  #include "drmtest.h"
> >  #include "i915_drm.h"
> > @@ -1217,6 +1225,7 @@ void igt_unlock_mem(void)
> >   */
> >  int igt_is_process_running(const char *comm)
> >  {
> > +#if HAVE_LIBPROCPS
> >  	PROCTAB *proc;
> >  	proc_t *proc_info;
> >  	bool found = false;
> > @@ -1235,6 +1244,26 @@ int igt_is_process_running(const char *comm)
> >  
> >  	closeproc(proc);
> >  	return found;
> > +#endif
> > +#if HAVE_LIBPROC2
> > +	enum pids_item Item[] = { PIDS_CMD };
> > +	struct pids_info *info = NULL;
> > +	struct pids_stack *stack;
> > +	char *pid_comm;
> > +	bool found = false;
> > +
> > +	if (procps_pids_new(&info, Item, 1) < 0)
> > +	    return false;
> > +	while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
> > +	    pid_comm = PIDS_VAL(0, str, stack, info);
> > +	    if (!strncasecmp(pid_comm, comm, strlen(pid_comm))) {
> 
> Processes AA and aa are different, why do you use strncasecmp ?
> 
> > +		found = true;
> > +		break;
> > +	    }
> > +	}
> > +	procps_pids_unref(&info);
> > +	return found;
> > +#endif
> >  }
> >  
> >  /**
> > @@ -1251,6 +1280,7 @@ int igt_is_process_running(const char *comm)
> >   */
> >  int igt_terminate_process(int sig, const char *comm)
> >  {
> > +#if HAVE_LIBPROCPS
> >  	PROCTAB *proc;
> >  	proc_t *proc_info;
> >  	int err = 0;
> > @@ -1272,6 +1302,29 @@ int igt_terminate_process(int sig, const char *comm)
> >  
> >  	closeproc(proc);
> >  	return err;
> > +#endif
> > +#if HAVE_LIBPROC2
> > +	enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD };
> > +	struct pids_info *info = NULL;
> > +	struct pids_stack *stack;
> > +	char *pid_comm;
> > +	int pid;
> > +	int err = 0;
> > +
> > +	if (procps_pids_new(&info, Items, 2) < 0)
> > +		return -errno;
> > +	while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
> > +		pid = PIDS_VAL(0, s_int, stack, info);
> > +		pid_comm = PIDS_VAL(1, str, stack, info);
> > +		if (!strncasecmp(pid_comm, comm, strlen(pid_comm))) {
> > +			if (kill(pid, sig) < 0)
> > +				err = -errno;
> > +			break;
> > +		}
> > +	}
> > +	procps_pids_unref(&info);
> > +	return err;
> > +#endif
> >  }
> >  
> >  struct pinfo {
> > @@ -1341,9 +1394,9 @@ igt_show_stat_header(void)
> >  }
> >  
> >  static void
> > -igt_show_stat(proc_t *info, int *state, const char *fn)
> > +igt_show_stat(const pid_t tid, const char *cmd, int *state, const char *fn)
> >  {
> > -	struct pinfo p = { .pid = info->tid, .comm = info->cmd, .fn = fn };
> > +	struct pinfo p = { .pid = tid, .comm = cmd, .fn = fn };
> >  
> >  	if (!*state)
> >  		igt_show_stat_header();
> > @@ -1353,7 +1406,7 @@ igt_show_stat(proc_t *info, int *state, const char *fn)
> >  }
> >  
> >  static void
> > -__igt_lsof_fds(proc_t *proc_info, int *state, char *proc_path, const char *dir)
> > +__igt_lsof_fds(const pid_t tid, const char *cmd, int *state, char *proc_path, const char *dir)
> >  {
> >  	struct dirent *d;
> >  	struct stat st;
> > @@ -1400,7 +1453,7 @@ again:
> >  		dirn = dirname(copy_fd_lnk);
> >  
> >  		if (!strncmp(dir, dirn, strlen(dir)))
> > -			igt_show_stat(proc_info, state, fd_lnk);
> > +			igt_show_stat(tid, cmd, state, fd_lnk);
> >  
> >  		free(copy_fd_lnk);
> >  		free(fd_lnk);
> > @@ -1416,13 +1469,14 @@ again:
> >  static void
> >  __igt_lsof(const char *dir)
> >  {
> > -	PROCTAB *proc;
> > -	proc_t *proc_info;
> > -
> >  	char path[30];
> >  	char *name_lnk;
> >  	struct stat st;
> >  	int state = 0;
> > +#ifdef HAVE_LIBPROCPS
> > +	PROCTAB *proc;
> > +	proc_t *proc_info;
> > +
> >  
> >  	proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
> >  	igt_assert(proc != NULL);
> > @@ -1443,19 +1497,57 @@ __igt_lsof(const char *dir)
> >  		name_lnk[read] = '\0';
> >  
> >  		if (!strncmp(dir, name_lnk, strlen(dir)))
> > -			igt_show_stat(proc_info, &state, name_lnk);
> > +			igt_show_stat(proc_info->tid, proc_info->cmd, &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);
> > +		__igt_lsof_fds(proc_info->tid, proc_info->cmd, &state, path, dir);
> >  
> >  		free(name_lnk);
> >  		freeproc(proc_info);
> >  	}
> >  
> >  	closeproc(proc);
> > +#endif
> > +#ifdef HAVE_LIBPROC2
> > +	enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD };
> > +	struct pids_info *info = NULL;
> > +	struct pids_stack *stack;
> > +
> > +	if (procps_pids_new(&info, Items, 2) < 0)
> > +		return;
> > +	while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
> > +		ssize_t read;
> > +		int tid = PIDS_VAL(0, s_int, stack, info);
> > +		char *pid_comm = PIDS_VAL(1, str, stack, info);
> > +
> > +		/* check current working directory */
> > +		memset(path, 0, sizeof(path));
> > +		snprintf(path, sizeof(path), "/proc/%d/cwd", 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(tid, pid_comm, &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", tid);
> > +
> > +		__igt_lsof_fds(tid, pid_comm, &state, path, dir);
> > +
> > +		free(name_lnk);
> > +	}
> > +	procps_pids_unref(&info);
> > +#endif
> >  }
> >  
> >  /**
> > @@ -1490,7 +1582,7 @@ igt_lsof(const char *dpath)
> >  	free(sanitized);
> >  }
> >  
> > -static void pulseaudio_unload_module(proc_t *proc_info)
> > +static void pulseaudio_unload_module(const uid_t euid, const gid_t egid)
> >  {
> >  	struct igt_helper_process pa_proc = {};
> >  	char xdg_dir[PATH_MAX];
> > @@ -1498,14 +1590,14 @@ static void pulseaudio_unload_module(proc_t *proc_info)
> >  	struct passwd *pw;
> >  
> >  	igt_fork_helper(&pa_proc) {
> > -		pw = getpwuid(proc_info->euid);
> > +		pw = getpwuid(euid);
> >  		homedir = pw->pw_dir;
> > -		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", proc_info->euid);
> > +		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", euid);
> >  
> >  		igt_info("Request pulseaudio to stop using audio device\n");
> >  
> > -		setgid(proc_info->egid);
> > -		setuid(proc_info->euid);
> > +		setgid(egid);
> > +		setuid(euid);
> >  		clearenv();
> >  		setenv("HOME", homedir, 1);
> >  		setenv("XDG_RUNTIME_DIR",xdg_dir, 1);
> > @@ -1524,10 +1616,13 @@ static void pipewire_reserve_wait(void)
> >  	char xdg_dir[PATH_MAX];
> >  	const char *homedir;
> >  	struct passwd *pw;
> > -	proc_t *proc_info;
> > -	PROCTAB *proc;
> > +	int tid=0, euid, egid;
> >  
> > +#ifdef HAVE_LIBPROCPS
> >  	igt_fork_helper(&pw_reserve_proc) {
> > +		proc_t *proc_info;
> > +		PROCTAB *proc;
> > +
> >  		igt_info("Preventing pipewire-pulse to use the audio drivers\n");
> >  
> >  		proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
> > @@ -1540,19 +1635,44 @@ static void pipewire_reserve_wait(void)
> >  		}
> >  		closeproc(proc);
> >  
> > +		tid = proc_info->tid;
> > +		euid = proc_info->euid;
> > +		egid = proc_info->egid;
> > +		freeproc(proc_info);
> > +#endif
> > +#ifdef HAVE_LIBPROC2
> > +	igt_fork(child, 1) {
> 
> This should be fork_helper like above.
> 
> > +		enum pids_item Items[] = { PIDS_ID_PID, PIDS_ID_EUID, PIDS_ID_EGID };
> > +		enum rel_items { EU_PID, EU_EUID, EU_EGID };
> > +		struct pids_info *info = NULL;
> > +		struct pids_stack *stack;
> > +
> > +		igt_info("Preventing pipewire-pulse to use the audio drivers\n");
> > +
> > +		if (procps_pids_new(&info, Items, 3) < 0)
> > +		    return;
> ------------------- ^
> Use exit(0) here plus maybe some ing_info why it failed ?
> 
> > +		while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
> > +			tid = PIDS_VAL(EU_PID, s_int, stack, info);
> > +			if (pipewire_pulse_pid == tid)
> > +				break;
> > +		}
> > +		euid = PIDS_VAL(EU_EUID, s_int, stack, info);
> > +		egid = PIDS_VAL(EU_EGID, s_int, stack, info);
> > +		procps_pids_unref(&info);
> > +#endif
> > +
> >  		/* Sanity check: if it can't find the process, it means it has gone */
> > -		if (pipewire_pulse_pid != proc_info->tid)
> > +		if (pipewire_pulse_pid != tid)
> >  			exit(0);
> >  
> > -		pw = getpwuid(proc_info->euid);
> > +		pw = getpwuid(euid);
> >  		homedir = pw->pw_dir;
> > -		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", proc_info->euid);
> > -		setgid(proc_info->egid);
> > -		setuid(proc_info->euid);
> > +		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", euid);
> > +		setgid(egid);
> > +		setuid(euid);
> >  		clearenv();
> >  		setenv("HOME", homedir, 1);
> >  		setenv("XDG_RUNTIME_DIR",xdg_dir, 1);
> > -		freeproc(proc_info);
> >  
> >  		/*
> >  		 * pw-reserve will run in background. It will only exit when
> > @@ -1570,9 +1690,7 @@ static void pipewire_reserve_wait(void)
> >  int pipewire_pulse_start_reserve(void)
> >  {
> >  	bool is_pw_reserve_running = false;
> > -	proc_t *proc_info;
> >  	int attempts = 0;
> > -	PROCTAB *proc;
> >  
> >  	if (!pipewire_pulse_pid)
> >  		return 0;
> > @@ -1584,6 +1702,10 @@ int pipewire_pulse_start_reserve(void)
> >  	 * pipewire version 0.3.50 or upper.
> >  	 */
> >  	for (attempts = 0; attempts < PIPEWIRE_RESERVE_MAX_TIME; attempts++) {
> > +#ifdef HAVE_LIBPROCPS
> > +		PROCTAB *proc;
> > +		proc_t *proc_info;
> > +
> >  		usleep(1000);
> >  		proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
> >  		igt_assert(proc != NULL);
> > @@ -1598,6 +1720,25 @@ int pipewire_pulse_start_reserve(void)
> >  			freeproc(proc_info);
> >  		}
> >  		closeproc(proc);
> > +#endif
> > +#ifdef HAVE_LIBPROC2
> > +		enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD };
> > +		struct pids_info *info = NULL;
> > +		struct pids_stack *stack;
> > +
> > +		usleep(1000);
> > +
> > +		if (procps_pids_new(&info, Items, 2) < 0)
> > +			return 1;
> ----------------------- ^
> break or igt_assert_f(errno == 0, "Getting procps failed\n"); here.
> 
> > +		while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
> > +			if (!strcmp(PIDS_VAL(1, str, stack, info), "pw-reserve")) {
> > +				is_pw_reserve_running = true;
> > +				pipewire_pw_reserve_pid = PIDS_VAL(0, s_int, stack, info);
> > +				break;
> > +			}
> > +		}
> > +		procps_pids_unref(&info);
> > +#endif
> >  		if (is_pw_reserve_running)
> >  			break;
> >  	}
> > @@ -1645,7 +1786,7 @@ void pipewire_pulse_stop_reserve(void)
> >   * 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)
> > +__igt_lsof_audio_and_kill_proc(const pid_t tid, const char *cmd, const uid_t euid, const gid_t egid, char *proc_path)
> >  {
> >  	const char *audio_dev = "/dev/snd/";
> >  	char path[PATH_MAX * 2];
> > @@ -1670,10 +1811,10 @@ __igt_lsof_audio_and_kill_proc(proc_t *proc_info, char *proc_path)
> >  	 * 2) unload/unbind the the audio driver(s);
> >  	 * 3) stop the pw-reserve thread.
> >  	 */
> > -	if (!strcmp(proc_info->cmd, "pipewire-pulse")) {
> > +	if (!strcmp(cmd, "pipewire-pulse")) {
> >  		igt_info("process %d (%s) is using audio device. Should be requested to stop using them.\n",
> > -			 proc_info->tid, proc_info->cmd);
> > -		pipewire_pulse_pid = proc_info->tid;
> > +			 tid, cmd);
> > +		pipewire_pulse_pid = tid;
> >  		return 0;
> >  	}
> >  	/*
> > @@ -1685,9 +1826,9 @@ __igt_lsof_audio_and_kill_proc(proc_t *proc_info, char *proc_path)
> >  	 * will respawn them. So, just ignore here, they'll honor pw-reserve,
> >  	 * when the time comes.
> >  	 */
> > -	if (!strcmp(proc_info->cmd, "pipewire-media-session"))
> > +	if (!strcmp(cmd, "pipewire-media-session"))
> >  		return 0;
> > -	if (!strcmp(proc_info->cmd, "wireplumber"))
> > +	if (!strcmp(cmd, "wireplumber"))
> >  		return 0;
> >  
> >  	dp = opendir(proc_path);
> > @@ -1723,22 +1864,22 @@ __igt_lsof_audio_and_kill_proc(proc_t *proc_info, char *proc_path)
> >  		 * 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);
> > +		if (!strcmp(cmd, "pulseaudio")) {
> > +			pulseaudio_unload_module(euid, egid);
> >  			break;
> >  		}
> >  
> >  		/* 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);
> > +				tid, cmd);
> >  
> > -		if (kill(proc_info->tid, SIGTERM) < 0) {
> > +		if (kill(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) {
> > +				cmd, tid);
> > +			if (kill(tid, SIGABRT) < 0) {
> >  				fail++;
> >  				igt_info("Fail to terminate %s (pid: %d) with SIGABRT\n",
> > -					proc_info->cmd, proc_info->tid);
> > +					cmd, tid);
> >  			}
> >  		}
> >  
> > @@ -1760,23 +1901,48 @@ int
> >  igt_lsof_kill_audio_processes(void)
> >  {
> >  	char path[PATH_MAX];
> > +	int fail = 0;
> > +
> > +#ifdef HAVE_LIBPROCPS
> >  	proc_t *proc_info;
> >  	PROCTAB *proc;
> > -	int fail = 0;
> >  
> >  	proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
> >  	igt_assert(proc != NULL);
> >  	pipewire_pulse_pid = 0;
> > -
> >  	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);
> > +			fail += __igt_lsof_audio_and_kill_proc(proc_info->tid, proc_info->cmd, proc_info->euid, proc_info->egid, path);
> >  
> >  		freeproc(proc_info);
> >  	}
> >  	closeproc(proc);
> > +#endif
> > +#ifdef HAVE_LIBPROC2
> > +	enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD, PIDS_ID_EUID, PIDS_ID_EGID };
> > +	enum rel_items { EU_PID, EU_CMD, EU_EUID, EU_EGID };
> > +	struct pids_info *info = NULL;
> > +	struct pids_stack *stack;
> > +	pid_t tid;
> > +
> > +	if (procps_pids_new(&info, Items, 4) < 0)
> > +		return 1;
> > +	while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
> > +		tid = PIDS_VAL(EU_PID, s_int, stack, info);
> > +
> > +		if (snprintf(path, sizeof(path), "/proc/%d/fd", tid) < 1)
> > +			fail++;
> > +		else
> > +			fail += __igt_lsof_audio_and_kill_proc(tid,
> > +				PIDS_VAL(EU_CMD, str, stack, info),
> > +				PIDS_VAL(EU_EUID, s_int, stack, info),
> > +				PIDS_VAL(EU_EGID, s_int, stack, info),
> > +				path);
> > +	}
> > +	procps_pids_unref(&info);
> > +#endif
> >  
> >  	return fail;
> >  }
> > diff --git a/lib/igt_aux.c.orig b/lib/igt_aux.c.orig
> > new file mode 100644
> > index 00000000..15e30440
> > --- /dev/null
> > +++ b/lib/igt_aux.c.orig
> > @@ -0,0 +1,1921 @@
> > +/*
> > + * Copyright © 2007, 2011, 2013, 2014, 2015 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.
> > + *
> > + * Authors:
> > + *    Eric Anholt <eric@anholt.net>
> > + *    Daniel Vetter <daniel.vetter@ffwll.ch>
> > + *
> > + */
> > +
> > +#ifdef HAVE_LIBGEN_H
> > +#include <libgen.h>
> > +#endif
> > +#include <stdio.h>
> > +#include <fcntl.h>
> > +#include <pwd.h>
> > +#include <sys/stat.h>
> > +#include <sys/ioctl.h>
> > +#include <string.h>
> > +#include <sys/mman.h>
> > +#include <signal.h>
> > +#include <pciaccess.h>
> > +#include <stdlib.h>
> > +#include <time.h>
> > +#include <unistd.h>
> > +#include <sys/poll.h>
> > +#include <sys/wait.h>
> > +#include <sys/resource.h>
> > +#include <sys/time.h>
> > +#include <sys/types.h>
> > +#include <sys/syscall.h>
> > +#include <sys/utsname.h>
> > +#include <termios.h>
> > +#include <assert.h>
> > +#include <grp.h>
> > +
> > +#include <proc/readproc.h>
> > +#include <libudev.h>
> > +
> > +#include "drmtest.h"
> > +#include "i915_drm.h"
> > +#include "intel_chipset.h"
> > +#include "igt_aux.h"
> > +#include "igt_debugfs.h"
> > +#include "igt_gt.h"
> > +#include "igt_params.h"
> > +#include "igt_rand.h"
> > +#include "igt_sysfs.h"
> > +#include "config.h"
> > +#include "intel_reg.h"
> > +#include "ioctl_wrappers.h"
> > +#include "igt_kms.h"
> > +#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
> > + * @title: aux
> > + * @include: igt.h
> > + *
> > + * This library provides various auxiliary helper functions that don't really
> > + * fit into any other topic.
> > + */
> > +
> > +static struct __igt_sigiter_global {
> > +	pid_t tid;
> > +	timer_t timer;
> > +	struct timespec offset;
> > +	struct {
> > +		long hit, miss;
> > +		long ioctls, signals;
> > +	} stat;
> > +} __igt_sigiter;
> > +
> > +static void sigiter(int sig, siginfo_t *info, void *arg)
> > +{
> > +	__igt_sigiter.stat.signals++;
> > +}
> > +
> > +#if 0
> > +#define SIG_ASSERT(expr) igt_assert(expr)
> > +#else
> > +#define SIG_ASSERT(expr)
> > +#endif
> > +
> > +static int
> > +sig_ioctl(int fd, unsigned long request, void *arg)
> > +{
> > +	struct itimerspec its;
> > +	int ret;
> > +
> > +	SIG_ASSERT(__igt_sigiter.timer);
> > +	SIG_ASSERT(__igt_sigiter.tid == gettid());
> > +
> > +	memset(&its, 0, sizeof(its));
> > +	if (timer_settime(__igt_sigiter.timer, 0, &its, NULL)) {
> > +		/* oops, we didn't undo the interrupter (i.e. !unwound abort) */
> > +		igt_ioctl = drmIoctl;
> > +		return drmIoctl(fd, request, arg);
> > +	}
> > +
> > +	its.it_value = __igt_sigiter.offset;
> > +	do {
> > +		long serial;
> > +
> > +		__igt_sigiter.stat.ioctls++;
> > +
> > +		ret = 0;
> > +		serial = __igt_sigiter.stat.signals;
> > +		igt_assert(timer_settime(__igt_sigiter.timer, 0, &its, NULL) == 0);
> > +		if (ioctl(fd, request, arg))
> > +			ret = errno;
> > +		if (__igt_sigiter.stat.signals == serial)
> > +			__igt_sigiter.stat.miss++;
> > +		if (ret == 0)
> > +			break;
> > +
> > +		if (ret == EINTR) {
> > +			__igt_sigiter.stat.hit++;
> > +
> > +			its.it_value.tv_sec *= 2;
> > +			its.it_value.tv_nsec *= 2;
> > +			while (its.it_value.tv_nsec >= NSEC_PER_SEC) {
> > +				its.it_value.tv_nsec -= NSEC_PER_SEC;
> > +				its.it_value.tv_sec += 1;
> > +			}
> > +
> > +			SIG_ASSERT(its.it_value.tv_nsec >= 0);
> > +			SIG_ASSERT(its.it_value.tv_sec >= 0);
> > +		}
> > +	} while (ret == EAGAIN || ret == EINTR);
> > +
> > +	memset(&its, 0, sizeof(its));
> > +	timer_settime(__igt_sigiter.timer, 0, &its, NULL);
> > +
> > +	errno = ret;
> > +	return ret ? -1 : 0;
> > +}
> > +
> > +static bool igt_sigiter_start(struct __igt_sigiter *iter, bool enable)
> > +{
> > +	/* Note that until we can automatically clean up on failed/skipped
> > +	 * tests, we cannot assume the state of the igt_ioctl indirection.
> > +	 */
> > +	SIG_ASSERT(igt_ioctl == drmIoctl);
> > +	igt_ioctl = drmIoctl;
> > +
> > +	if (enable) {
> > +		struct timespec start, end;
> > +		struct sigevent sev;
> > +		struct sigaction act;
> > +		struct itimerspec its;
> > +
> > +		igt_ioctl = sig_ioctl;
> > +		__igt_sigiter.tid = gettid();
> > +
> > +		memset(&sev, 0, sizeof(sev));
> > +		sev.sigev_notify = SIGEV_SIGNAL | SIGEV_THREAD_ID;
> > +		sev.sigev_notify_thread_id = __igt_sigiter.tid;
> > +		sev.sigev_signo = SIGRTMIN;
> > +		igt_assert(timer_create(CLOCK_MONOTONIC, &sev, &__igt_sigiter.timer) == 0);
> > +
> > +		memset(&its, 0, sizeof(its));
> > +		igt_assert(timer_settime(__igt_sigiter.timer, 0, &its, NULL) == 0);
> > +
> > +		memset(&act, 0, sizeof(act));
> > +		act.sa_sigaction = sigiter;
> > +		act.sa_flags = SA_SIGINFO;
> > +		igt_assert(sigaction(SIGRTMIN, &act, NULL) == 0);
> > +
> > +		/* Try to find the approximate delay required to skip over
> > +		 * the timer_setttime and into the following ioctl() to try
> > +		 * and avoid the timer firing before we enter the drmIoctl.
> > +		 */
> > +		igt_assert(clock_gettime(CLOCK_MONOTONIC, &start) == 0);
> > +		igt_assert(timer_settime(__igt_sigiter.timer, 0, &its, NULL) == 0);
> > +		igt_assert(clock_gettime(CLOCK_MONOTONIC, &end) == 0);
> > +
> > +		__igt_sigiter.offset.tv_sec = end.tv_sec - start.tv_sec;
> > +		__igt_sigiter.offset.tv_nsec = end.tv_nsec - start.tv_nsec;
> > +		if (__igt_sigiter.offset.tv_nsec < 0) {
> > +			__igt_sigiter.offset.tv_nsec += NSEC_PER_SEC;
> > +			__igt_sigiter.offset.tv_sec -= 1;
> > +		}
> > +		if (__igt_sigiter.offset.tv_sec < 0) {
> > +			__igt_sigiter.offset.tv_nsec = 0;
> > +			__igt_sigiter.offset.tv_sec = 0;
> > +		}
> > +		igt_assert(__igt_sigiter.offset.tv_sec == 0);
> > +
> > +		igt_debug("Initial delay for interruption: %ld.%09lds\n",
> > +			  __igt_sigiter.offset.tv_sec,
> > +			  __igt_sigiter.offset.tv_nsec);
> > +	}
> > +
> > +	return true;
> > +}
> > +
> > +static bool igt_sigiter_stop(struct __igt_sigiter *iter, bool enable)
> > +{
> > +	if (enable) {
> > +		struct sigaction act;
> > +
> > +		SIG_ASSERT(igt_ioctl == sig_ioctl);
> > +		SIG_ASSERT(__igt_sigiter.tid == gettid());
> > +		igt_ioctl = drmIoctl;
> > +
> > +		timer_delete(__igt_sigiter.timer);
> > +
> > +		memset(&act, 0, sizeof(act));
> > +		act.sa_handler = SIG_IGN;
> > +		sigaction(SIGRTMIN, &act, NULL);
> > +
> > +		memset(&__igt_sigiter, 0, sizeof(__igt_sigiter));
> > +	}
> > +
> > +	memset(iter, 0, sizeof(*iter));
> > +	return false;
> > +}
> > +
> > +bool __igt_sigiter_continue(struct __igt_sigiter *iter, bool enable)
> > +{
> > +	if (iter->pass++ == 0)
> > +		return igt_sigiter_start(iter, enable);
> > +
> > +	/* If nothing reported SIGINT, nothing will on the next pass, so
> > +	 * give up! Also give up if everything is now executing faster
> > +	 * than current sigtimer.
> > +	 */
> > +	if (__igt_sigiter.stat.hit == 0 ||
> > +	    __igt_sigiter.stat.miss == __igt_sigiter.stat.ioctls)
> > +		return igt_sigiter_stop(iter, enable);
> > +
> > +	igt_debug("%s: pass %d, missed %ld/%ld\n",
> > +		  __func__, iter->pass - 1,
> > +		  __igt_sigiter.stat.miss,
> > +		  __igt_sigiter.stat.ioctls);
> > +
> > +	SIG_ASSERT(igt_ioctl == sig_ioctl);
> > +	SIG_ASSERT(__igt_sigiter.timer);
> > +
> > +	__igt_sigiter.offset.tv_sec *= 2;
> > +	__igt_sigiter.offset.tv_nsec *= 2;
> > +	while (__igt_sigiter.offset.tv_nsec >= NSEC_PER_SEC) {
> > +		__igt_sigiter.offset.tv_nsec -= NSEC_PER_SEC;
> > +		__igt_sigiter.offset.tv_sec += 1;
> > +	}
> > +	SIG_ASSERT(__igt_sigiter.offset.tv_nsec >= 0);
> > +	SIG_ASSERT(__igt_sigiter.offset.tv_sec >= 0);
> > +
> > +	memset(&__igt_sigiter.stat, 0, sizeof(__igt_sigiter.stat));
> > +	return true;
> > +}
> > +
> > +static struct igt_helper_process signal_helper;
> > +long long int sig_stat;
> > +__noreturn static void signal_helper_process(pid_t pid)
> > +{
> > +	/* Interrupt the parent process at 500Hz, just to be annoying */
> > +	while (1) {
> > +		usleep(1000 * 1000 / 500);
> > +		if (kill(pid, SIGCONT)) /* Parent has died, so must we. */
> > +			exit(0);
> > +	}
> > +}
> > +
> > +static void sig_handler(int i)
> > +{
> > +	sig_stat++;
> > +}
> > +
> > +/**
> > + * igt_fork_signal_helper:
> > + *
> > + * Fork a child process using #igt_fork_helper to interrupt the parent process
> > + * with a SIGCONT signal at regular quick intervals. The corresponding dummy
> > + * signal handler is installed in the parent process.
> > + *
> > + * This is useful to exercise ioctl error paths, at least where those can be
> > + * exercises by interrupting blocking waits, like stalling for the gpu. This
> > + * helper can also be used from children spawned with #igt_fork.
> > + *
> > + * In tests with subtests this function can be called outside of failure
> > + * catching code blocks like #igt_fixture or #igt_subtest.
> > + *
> > + * Note that this just spews signals at the current process unconditionally and
> > + * hence incurs quite a bit of overhead. For a more focused approach, with less
> > + * overhead, look at the #igt_while_interruptible code block macro.
> > + */
> > +void igt_fork_signal_helper(void)
> > +{
> > +	if (igt_only_list_subtests())
> > +		return;
> > +
> > +	/* We pick SIGCONT as it is a "safe" signal - if we send SIGCONT to
> > +	 * an unexpecting process it spuriously wakes up and does nothing.
> > +	 * Most other signals (e.g. SIGUSR1) cause the process to die if they
> > +	 * are not handled. This is an issue in case the sighandler is not
> > +	 * inherited correctly (or if there is a race in the inheritance
> > +	 * and we send the signal at exactly the wrong time).
> > +	 */
> > +	signal(SIGCONT, sig_handler);
> > +	setpgid(0, 0); /* define a new process group for the tests */
> > +
> > +	igt_fork_helper(&signal_helper) {
> > +		setpgid(0, 0); /* Escape from the test process group */
> > +
> > +		/* Pass along the test process group identifier,
> > +		 * negative pid => send signal to everyone in the group.
> > +		 */
> > +		signal_helper_process(-getppid());
> > +	}
> > +}
> > +
> > +/**
> > + * igt_stop_signal_helper:
> > + *
> > + * Stops the child process spawned with igt_fork_signal_helper() again.
> > + *
> > + * In tests with subtests this function can be called outside of failure
> > + * catching code blocks like #igt_fixture or #igt_subtest.
> > + */
> > +void igt_stop_signal_helper(void)
> > +{
> > +	if (igt_only_list_subtests())
> > +		return;
> > +
> > +	igt_stop_helper(&signal_helper);
> > +
> > +	sig_stat = 0;
> > +}
> > +
> > +/**
> > + * igt_suspend_signal_helper:
> > + *
> > + * Suspends the child process spawned with igt_fork_signal_helper(). This
> > + * should be called before a critical section of code that has difficulty to
> > + * make progress if interrupted frequently, like the clone() syscall called
> > + * from a largish executable. igt_resume_signal_helper() must be called after
> > + * the critical section to restart interruptions for the test.
> > + */
> > +void igt_suspend_signal_helper(void)
> > +{
> > +	int status;
> > +
> > +	if (!signal_helper.running)
> > +		return;
> > +
> > +	kill(signal_helper.pid, SIGSTOP);
> > +	while (waitpid(signal_helper.pid, &status, WUNTRACED) == -1 &&
> > +	       errno == EINTR)
> > +		;
> > +}
> > +
> > +/**
> > + * igt_resume_signal_helper:
> > + *
> > + * Resumes the child process spawned with igt_fork_signal_helper().
> > + *
> > + * This should be paired with igt_suspend_signal_helper() and called after the
> > + * problematic code sensitive to signals.
> > + */
> > +void igt_resume_signal_helper(void)
> > +{
> > +	if (!signal_helper.running)
> > +		return;
> > +
> > +	kill(signal_helper.pid, SIGCONT);
> > +}
> > +
> > +static struct igt_helper_process shrink_helper;
> > +__noreturn static void shrink_helper_process(int fd, pid_t pid)
> > +{
> > +	while (1) {
> > +		igt_drop_caches_set(fd, DROP_SHRINK_ALL);
> > +		usleep(1000 * 1000 / 50);
> > +		if (kill(pid, 0)) /* Parent has died, so must we. */
> > +			exit(0);
> > +	}
> > +}
> > +
> > +/**
> > + * igt_fork_shrink_helper:
> > + *
> > + * Fork a child process using #igt_fork_helper to force all available objects
> > + * to be paged out (via i915_gem_shrink()).
> > + *
> > + * This is useful to exercise swapping paths, without requiring us to hit swap.
> > + *
> > + * This should only be used from an igt_fixture.
> > + */
> > +void igt_fork_shrink_helper(int drm_fd)
> > +{
> > +	assert(!igt_only_list_subtests());
> > +	igt_require(igt_drop_caches_has(drm_fd, DROP_SHRINK_ALL));
> > +	igt_fork_helper(&shrink_helper)
> > +		shrink_helper_process(drm_fd, getppid());
> > +}
> > +
> > +/**
> > + * igt_stop_shrink_helper:
> > + *
> > + * Stops the child process spawned with igt_fork_shrink_helper().
> > + */
> > +void igt_stop_shrink_helper(void)
> > +{
> > +	igt_stop_helper(&shrink_helper);
> > +}
> > +
> > +static void show_kernel_stack(pid_t pid)
> > +{
> > +	char buf[80], *str;
> > +	int dir;
> > +
> > +	snprintf(buf, sizeof(buf), "/proc/%d", pid);
> > +	dir = open(buf, O_RDONLY);
> > +	if (dir < 0)
> > +		return;
> > +
> > +	str = igt_sysfs_get(dir, "stack");
> > +	if (str) {
> > +		igt_debug("Kernel stack for pid %d:\n%s\n", pid, str);
> > +		free(str);
> > +	}
> > +
> > +	close(dir);
> > +}
> > +
> > +static struct igt_helper_process hang_detector;
> > +__noreturn static void
> > +hang_detector_process(int fd, pid_t pid, dev_t rdev)
> > +{
> > +	struct udev_monitor *mon =
> > +		udev_monitor_new_from_netlink(udev_new(), "kernel");
> > +	struct pollfd pfd;
> > +	int ret;
> > +
> > +	udev_monitor_filter_add_match_subsystem_devtype(mon, "drm", NULL);
> > +	udev_monitor_enable_receiving(mon);
> > +
> > +	pfd.fd = udev_monitor_get_fd(mon);
> > +	pfd.events = POLLIN;
> > +
> > +	while ((ret = poll(&pfd, 1, 2000)) >= 0) {
> > +		struct udev_device *dev;
> > +		dev_t devnum;
> > +
> > +		if (kill(pid, 0)) { /* Parent has died, so must we. */
> > +			igt_warn("Parent died without killing its children (%s)\n",
> > +				 __func__);
> > +			break;
> > +		}
> > +
> > +		dev = NULL;
> > +		if (ret > 0)
> > +			dev = udev_monitor_receive_device(mon);
> > +		if (dev == NULL)
> > +			continue;
> > +
> > +		devnum = udev_device_get_devnum(dev);
> > +		if (memcmp(&rdev, &devnum, sizeof(dev_t)) == 0) {
> > +			const char *str;
> > +
> > +			str = udev_device_get_property_value(dev, "ERROR");
> > +			if (str && atoi(str) == 1) {
> > +				show_kernel_stack(pid);
> > +				kill(pid, SIGIO);
> > +			}
> > +		}
> > +
> > +		udev_device_unref(dev);
> > +	}
> > +
> > +	exit(0);
> > +}
> > +
> > +__noreturn static void sig_abort(int sig)
> > +{
> > +	errno = 0; /* inside a signal, last errno reporting is confusing */
> > +	igt_assert(!"GPU hung");
> > +}
> > +
> > +void igt_fork_hang_detector(int fd)
> > +{
> > +	struct stat st;
> > +
> > +	igt_assert(fstat(fd, &st) == 0);
> > +
> > +	/*
> > +	 * Disable per-engine reset to force an error uevent. We don't
> > +	 * expect to get any hangs whilst the detector is enabled (if we do
> > +	 * they are a test failure!) and so the loss of per-engine reset
> > +	 * functionality is not an issue.
> > +	 */
> > +	igt_assert(igt_params_set(fd, "reset", "%d", 1 /* only global reset */));
> > +
> > +	signal(SIGIO, sig_abort);
> > +	igt_fork_helper(&hang_detector)
> > +		hang_detector_process(fd, getppid(), st.st_rdev);
> > +}
> > +
> > +void igt_stop_hang_detector(void)
> > +{
> > +	/*
> > +	 * Give the uevent time to arrive. No sleep at all misses about 20% of
> > +	 * hangs (at least, in the i915_hangman/detector test). A sleep of 1ms
> > +	 * seems to miss about 2%, 10ms loses <1%, so 100ms should be safe.
> > +	 */
> > +	usleep(100 * 1000);
> > +
> > +	igt_stop_helper(&hang_detector);
> > +}
> > +
> > +/**
> > + * igt_check_boolean_env_var:
> > + * @env_var: environment variable name
> > + * @default_value: default value for the environment variable
> > + *
> > + * This function should be used to parse boolean environment variable options.
> > + *
> > + * Returns:
> > + * The boolean value of the environment variable @env_var as decoded by atoi()
> > + * if it is set and @default_value if the variable is not set.
> > + */
> > +bool igt_check_boolean_env_var(const char *env_var, bool default_value)
> > +{
> > +	char *val;
> > +
> > +	val = getenv(env_var);
> > +	if (!val)
> > +		return default_value;
> > +
> > +	return atoi(val) != 0;
> > +}
> > +
> > +/**
> > + * igt_aub_dump_enabled:
> > + *
> > + * Returns:
> > + * True if AUB dumping is enabled with IGT_DUMP_AUB=1 in the environment, false
> > + * otherwise.
> > + */
> > +bool igt_aub_dump_enabled(void)
> > +{
> > +	static int dump_aub = -1;
> > +
> > +	if (dump_aub == -1)
> > +		dump_aub = igt_check_boolean_env_var("IGT_DUMP_AUB", false);
> > +
> > +	return dump_aub;
> > +}
> > +
> > +/* other helpers */
> > +/**
> > + * igt_exchange_int:
> > + * @array: pointer to the array of integers
> > + * @i: first position
> > + * @j: second position
> > + *
> > + * Exchanges the two values at array indices @i and @j. Useful as an exchange
> > + * function for igt_permute_array().
> > + */
> > +void igt_exchange_int(void *array, unsigned i, unsigned j)
> > +{
> > +	int *int_arr, tmp;
> > +	int_arr = array;
> > +
> > +	tmp = int_arr[i];
> > +	int_arr[i] = int_arr[j];
> > +	int_arr[j] = tmp;
> > +}
> > +
> > +/**
> > + * igt_exchange_int64:
> > + * @array: pointer to the array of int64_t
> > + * @i: first position
> > + * @j: second position
> > + *
> > + * Exchanges the two values at array indices @i and @j. Useful as an exchange
> > + * function for igt_permute_array().
> > + */
> > +void igt_exchange_int64(void *array, unsigned i, unsigned j)
> > +{
> > +	int64_t *a = array;
> > +
> > +	igt_swap(a[i], a[j]);
> > +}
> > +
> > +/**
> > + * igt_permute_array:
> > + * @array: pointer to array
> > + * @size: size of the array
> > + * @exchange_func: function to exchange array elements
> > + *
> > + * This function randomly permutes the array using random() as the PRNG source.
> > + * The @exchange_func function is called to exchange two elements in the array
> > + * when needed.
> > + */
> > +void igt_permute_array(void *array, unsigned size,
> > +                       void (*exchange_func)(void *array,
> > +                                             unsigned i,
> > +                                             unsigned j))
> > +{
> > +	int i;
> > +
> > +	for (i = size - 1; i > 0; i--) {
> > +		/* yes, not perfectly uniform, who cares */
> > +		long l = hars_petruska_f54_1_random_unsafe() % (i +1);
> > +		if (i != l)
> > +			exchange_func(array, i, l);
> > +	}
> > +}
> > +
> > +__attribute__((format(printf, 1, 2)))
> > +static void igt_interactive_info(const char *format, ...)
> > +{
> > +	va_list args;
> > +
> > +	if (!isatty(STDERR_FILENO) || __igt_plain_output) {
> > +		errno = 0; /* otherwise would be either ENOTTY or EBADF */
> > +		return;
> > +	}
> > +
> > +	if (igt_log_level > IGT_LOG_INFO)
> > +		return;
> > +
> > +	va_start(args, format);
> > +	vfprintf(stderr, format, args);
> > +	va_end(args);
> > +}
> > +
> > +
> > +/**
> > + * igt_progress:
> > + * @header: header string to prepend to the progress indicator
> > + * @i: work processed thus far
> > + * @total: total amount of work
> > + *
> > + * This function draws a progress indicator, which is useful for running
> > + * long-winded tests manually on the console. To avoid spamming log files in
> > + * automated runs the progress indicator is suppressed when not running on a
> > + * terminal.
> > + */
> > +void igt_progress(const char *header, uint64_t i, uint64_t total)
> > +{
> > +	int divider = 200;
> > +
> > +	if (i+1 >= total) {
> > +		igt_interactive_info("\r%s100%%\n", header);
> > +		return;
> > +	}
> > +
> > +	if (total / 200 == 0)
> > +		divider = 1;
> > +
> > +	/* only bother updating about every 0.5% */
> > +	if (i % (total / divider) == 0)
> > +		igt_interactive_info("\r%s%3llu%%", header,
> > +				     (long long unsigned)i * 100 / total);
> > +}
> > +
> > +/**
> > + * igt_print_activity:
> > + *
> > + * Print a '.' to indicate activity. This is printed without a newline and
> > + * only if output is to a terminal.
> > + */
> > +void igt_print_activity(void)
> > +{
> > +	igt_interactive_info(".");
> > +}
> > +
> > +static int autoresume_delay;
> > +
> > +static const char *suspend_state_name[] = {
> > +	[SUSPEND_STATE_FREEZE] = "freeze",
> > +	[SUSPEND_STATE_STANDBY] = "standby",
> > +	[SUSPEND_STATE_S3] = "mem", /* Forces Suspend-to-Ram (S3) */
> > +	[SUSPEND_STATE_MEM] = "mem", /* Respects system default */
> > +	[SUSPEND_STATE_DISK] = "disk",
> > +};
> > +
> > +static const char *suspend_test_name[] = {
> > +	[SUSPEND_TEST_NONE] = "none",
> > +	[SUSPEND_TEST_FREEZER] = "freezer",
> > +	[SUSPEND_TEST_DEVICES] = "devices",
> > +	[SUSPEND_TEST_PLATFORM] = "platform",
> > +	[SUSPEND_TEST_PROCESSORS] = "processors",
> > +	[SUSPEND_TEST_CORE] = "core",
> > +};
> > +
> > +static const char *mem_sleep_name[] = {
> > +	[MEM_SLEEP_S2IDLE] = "s2idle",
> > +	[MEM_SLEEP_SHALLOW] = "shallow",
> > +	[MEM_SLEEP_DEEP] = "deep"
> > +};
> > +
> > +static enum igt_suspend_test get_suspend_test(int power_dir)
> > +{
> > +	char *test_line;
> > +	char *test_name;
> > +	enum igt_suspend_test test;
> > +
> > +	if (faccessat(power_dir, "pm_test", R_OK, 0))
> > +		return SUSPEND_TEST_NONE;
> > +
> > +	igt_assert((test_line = igt_sysfs_get(power_dir, "pm_test")));
> > +	for (test_name = strtok(test_line, " "); test_name;
> > +	     test_name = strtok(NULL, " "))
> > +		if (test_name[0] == '[') {
> > +			test_name[strlen(test_name) - 1] = '\0';
> > +			test_name++;
> > +			break;
> > +		}
> > +
> > +	if (!test_name) {
> > +	  	free(test_line);
> > +		return SUSPEND_TEST_NONE;
> > +	}
> > +
> > +	for (test = SUSPEND_TEST_NONE; test < SUSPEND_TEST_NUM; test++)
> > +		if (strcmp(suspend_test_name[test], test_name) == 0)
> > +			break;
> > +
> > +	igt_assert(test < SUSPEND_TEST_NUM);
> > +
> > +	free(test_line);
> > +	return test;
> > +}
> > +
> > +static void set_suspend_test(int power_dir, enum igt_suspend_test test)
> > +{
> > +	igt_assert(test < SUSPEND_TEST_NUM);
> > +
> > +	if (faccessat(power_dir, "pm_test", W_OK, 0)) {
> > +		igt_require(test == SUSPEND_TEST_NONE);
> > +		return;
> > +	}
> > +
> > +	igt_assert(igt_sysfs_set(power_dir, "pm_test", suspend_test_name[test]));
> > +}
> > +
> > +#define SQUELCH ">/dev/null 2>&1"
> > +
> > +static void suspend_via_rtcwake(enum igt_suspend_state state)
> > +{
> > +	char cmd[128];
> > +	int delay, ret;
> > +
> > +	igt_assert(state < SUSPEND_STATE_NUM);
> > +
> > +	delay = igt_get_autoresume_delay(state);
> > +
> > +	/*
> > +	 * Skip if rtcwake would fail for a reason not related to the kernel's
> > +	 * suspend functionality.
> > +	 */
> > +	snprintf(cmd, sizeof(cmd), "rtcwake -n -s %d -m %s " SQUELCH,
> > +		 delay, suspend_state_name[state]);
> > +	ret = igt_system(cmd);
> > +	igt_require_f(ret == 0, "rtcwake test failed with %i\n"
> > +		     "This failure could mean that something is wrong with "
> > +		     "the rtcwake tool or how your distro is set up.\n",
> > +		      ret);
> > +
> > +	snprintf(cmd, sizeof(cmd), "rtcwake -s %d -m %s ",
> > +		 delay, suspend_state_name[state]);
> > +	ret = igt_system(cmd);
> > +	if (ret) {
> > +		const char *path = "suspend_stats";
> > +		char *info;
> > +		int dir;
> > +
> > +		igt_warn("rtcwake failed with %i\n"
> > +			 "Check dmesg for further details.\n",
> > +			 ret);
> > +
> > +		dir = open(igt_debugfs_mount(), O_RDONLY);
> > +		info = igt_sysfs_get(dir, path);
> > +		close(dir);
> > +		if (info) {
> > +			igt_debug("%s:\n%s\n", path, info);
> > +			free(info);
> > +		}
> > +	}
> > +	igt_assert_eq(ret, 0);
> > +}
> > +
> > +static void suspend_via_sysfs(int power_dir, enum igt_suspend_state state)
> > +{
> > +	igt_assert(state < SUSPEND_STATE_NUM);
> > +	igt_assert(igt_sysfs_set(power_dir, "state",
> > +				 suspend_state_name[state]));
> > +}
> > +
> > +static bool is_state_supported(int power_dir, enum igt_suspend_state state)
> > +{
> > +	const char *str;
> > +	char *states;
> > +
> > +	igt_assert((states = igt_sysfs_get(power_dir, "state")));
> > +
> > +	str = strstr(states, suspend_state_name[state]);
> > +
> > +	if (!str)
> > +		igt_info("State %s not supported.\nSupported States: %s\n",
> > +			 suspend_state_name[state], states);
> > +
> > +	free(states);
> > +	return str;
> > +}
> > +
> > +static int get_mem_sleep(void)
> > +{
> > +	char *mem_sleep_states;
> > +	char *mem_sleep_state;
> > +	enum igt_mem_sleep mem_sleep;
> > +	int power_dir;
> > +
> > +	igt_require((power_dir = open("/sys/power", O_RDONLY)) >= 0);
> > +
> > +	if (faccessat(power_dir, "mem_sleep", R_OK, 0))
> > +		return MEM_SLEEP_NONE;
> > +
> > +	igt_assert((mem_sleep_states = igt_sysfs_get(power_dir, "mem_sleep")));
> > +	for (mem_sleep_state = strtok(mem_sleep_states, " "); mem_sleep_state;
> > +	     mem_sleep_state = strtok(NULL, " ")) {
> > +		if (mem_sleep_state[0] == '[') {
> > +			mem_sleep_state[strlen(mem_sleep_state) - 1] = '\0';
> > +			mem_sleep_state++;
> > +			break;
> > +		}
> > +	}
> > +
> > +	if (!mem_sleep_state) {
> > +		free(mem_sleep_states);
> > +		return MEM_SLEEP_NONE;
> > +	}
> > +
> > +	for (mem_sleep = MEM_SLEEP_S2IDLE; mem_sleep < MEM_SLEEP_NUM; mem_sleep++) {
> > +		if (strcmp(mem_sleep_name[mem_sleep], mem_sleep_state) == 0)
> > +			break;
> > +	}
> > +
> > +	igt_assert_f(mem_sleep < MEM_SLEEP_NUM, "Invalid mem_sleep state\n");
> > +
> > +	free(mem_sleep_states);
> > +	close(power_dir);
> > +	return mem_sleep;
> > +}
> > +
> > +static void set_mem_sleep(int power_dir, enum igt_mem_sleep sleep)
> > +{
> > +	igt_assert(sleep < MEM_SLEEP_NUM);
> > +
> > +	igt_assert_eq(faccessat(power_dir, "mem_sleep", W_OK, 0), 0);
> > +
> > +	igt_assert(igt_sysfs_set(power_dir, "mem_sleep",
> > +				 mem_sleep_name[sleep]));
> > +}
> > +
> > +static bool is_mem_sleep_state_supported(int power_dir, enum igt_mem_sleep state)
> > +{
> > +	const char *str;
> > +	char *mem_sleep_states;
> > +
> > +	igt_assert((mem_sleep_states = igt_sysfs_get(power_dir, "mem_sleep")));
> > +
> > +	str = strstr(mem_sleep_states, mem_sleep_name[state]);
> > +
> > +	if (!str)
> > +		igt_info("mem_sleep state %s not supported.\nSupported mem_sleep states: %s\n",
> > +			 mem_sleep_name[state], mem_sleep_states);
> > +
> > +	free(mem_sleep_states);
> > +	return str;
> > +}
> > +
> > +/**
> > + * igt_system_suspend_autoresume:
> > + * @state: an #igt_suspend_state, the target suspend state
> > + * @test: an #igt_suspend_test, test point at which to complete the suspend
> > + *	  cycle
> > + *
> > + * Execute a system suspend cycle targeting the given @state optionally
> > + * completing the cycle at the given @test point and automaically wake up
> > + * again. Waking up is either achieved using the RTC wake-up alarm for a full
> > + * suspend cycle or a kernel timer for a suspend test cycle. The kernel timer
> > + * delay for a test cycle can be configured by the suspend.pm_test_delay
> > + * kernel parameter (5 sec by default).
> > + *
> > + * #SUSPEND_TEST_NONE specifies a full suspend cycle.
> > + * The #SUSPEND_TEST_FREEZER..#SUSPEND_TEST_CORE test points can make it
> > + * possible to collect error logs in case a full suspend cycle would prevent
> > + * this by hanging the machine, or they can provide an idea of the faulty
> > + * component by comparing fail/no-fail results at different test points.
> > + *
> > + * This is very handy for implementing any kind of suspend/resume test.
> > + */
> > +void igt_system_suspend_autoresume(enum igt_suspend_state state,
> > +				   enum igt_suspend_test test)
> > +{
> > +	int power_dir;
> > +	enum igt_suspend_test orig_test;
> > +	enum igt_mem_sleep orig_mem_sleep = MEM_SLEEP_NONE;
> > +
> > +	igt_require((power_dir = open("/sys/power", O_RDONLY)) >= 0);
> > +	igt_require(is_state_supported(power_dir, state));
> > +	igt_require(test == SUSPEND_TEST_NONE ||
> > +		    faccessat(power_dir, "pm_test", R_OK | W_OK, 0) == 0);
> > +
> > +	igt_skip_on_f(state == SUSPEND_STATE_DISK &&
> > +		      !igt_get_total_swap_mb(),
> > +		      "Suspend to disk requires swap space.\n");
> > +
> > +	orig_test = get_suspend_test(power_dir);
> > +
> > +	if (state == SUSPEND_STATE_S3) {
> > +		orig_mem_sleep = get_mem_sleep();
> > +		igt_skip_on_f(!is_mem_sleep_state_supported(power_dir, MEM_SLEEP_DEEP),
> > +			      "S3 not supported in this system.\n");
> > +		set_mem_sleep(power_dir, MEM_SLEEP_DEEP);
> > +		igt_skip_on_f(get_mem_sleep() != MEM_SLEEP_DEEP,
> > +			      "S3 not possible in this system.\n");
> > +	}
> > +
> > +	set_suspend_test(power_dir, test);
> > +
> > +	if (test == SUSPEND_TEST_NONE)
> > +		suspend_via_rtcwake(state);
> > +	else
> > +		suspend_via_sysfs(power_dir, state);
> > +
> > +	if (orig_mem_sleep)
> > +		set_mem_sleep(power_dir, orig_mem_sleep);
> > +
> > +	set_suspend_test(power_dir, orig_test);
> > +	close(power_dir);
> > +}
> > +
> > +static int original_autoresume_delay;
> > +
> > +static void igt_restore_autoresume_delay(int sig)
> > +{
> > +	int delay_fd;
> > +	char delay_str[10];
> > +
> > +	igt_require((delay_fd = open("/sys/module/suspend/parameters/pm_test_delay",
> > +				    O_WRONLY)) >= 0);
> > +
> > +	snprintf(delay_str, sizeof(delay_str), "%d", original_autoresume_delay);
> > +	igt_require(write(delay_fd, delay_str, strlen(delay_str)));
> > +
> > +	close(delay_fd);
> > +}
> > +
> > +/**
> > + * igt_set_autoresume_delay:
> > + * @delay_secs: The delay in seconds before resuming the system
> > + *
> > + * Sets how long we wait to resume the system after suspending it, using the
> > + * suspend.pm_test_delay variable. On exit, the original delay value is
> > + * restored.
> > + */
> > +void igt_set_autoresume_delay(int delay_secs)
> > +{
> > +	int delay_fd;
> > +	char delay_str[10];
> > +
> > +	delay_fd = open("/sys/module/suspend/parameters/pm_test_delay", O_RDWR);
> > +
> > +	if (delay_fd >= 0) {
> > +		if (!original_autoresume_delay) {
> > +			igt_require(read(delay_fd, delay_str,
> > +					 sizeof(delay_str)));
> > +			original_autoresume_delay = atoi(delay_str);
> > +			igt_install_exit_handler(igt_restore_autoresume_delay);
> > +		}
> > +
> > +		snprintf(delay_str, sizeof(delay_str), "%d", delay_secs);
> > +		igt_require(write(delay_fd, delay_str, strlen(delay_str)));
> > +
> > +		close(delay_fd);
> > +	}
> > +
> > +	autoresume_delay = delay_secs;
> > +}
> > +
> > +/**
> > + * igt_get_autoresume_delay:
> > + * @state: an #igt_suspend_state, the target suspend state
> > + *
> > + * Retrieves how long we wait to resume the system after suspending it.
> > + * This can either be set through igt_set_autoresume_delay or be a default
> > + * value that depends on the suspend state.
> > + *
> > + * Returns: The autoresume delay, in seconds.
> > + */
> > +int igt_get_autoresume_delay(enum igt_suspend_state state)
> > +{
> > +	int delay;
> > +
> > +	if (autoresume_delay)
> > +		delay = autoresume_delay;
> > +	else
> > +		delay = state == SUSPEND_STATE_DISK ? 30 : 15;
> > +
> > +	return delay;
> > +}
> > +
> > +/**
> > + * igt_drop_root:
> > + *
> > + * Drop root privileges and make sure it actually worked. Useful for tests
> > + * which need to check security constraints. Note that this should only be
> > + * called from manually forked processes, since the lack of root privileges
> > + * will wreak havoc with the automatic cleanup handlers.
> > + */
> > +void igt_drop_root(void)
> > +{
> > +	igt_assert_eq(getuid(), 0);
> > +
> > +	igt_assert_eq(setgroups(0, NULL), 0);
> > +	igt_assert_eq(setgid(2), 0);
> > +	igt_assert_eq(setuid(2), 0);
> > +
> > +	igt_assert_eq(getgroups(0, NULL), 0);
> > +	igt_assert_eq(getgid(), 2);
> > +	igt_assert_eq(getuid(), 2);
> > +}
> > +
> > +/**
> > + * igt_debug_wait_for_keypress:
> > + * @var: var lookup to to enable this wait
> > + *
> > + * Waits for a key press when run interactively and when the corresponding debug
> > + * var is set in the --interactive-debug=$var variable. Multiple keys
> > + * can be specified as a comma-separated list or alternatively "all" if a wait
> > + * should happen for all cases. Calling this function with "all" will assert.
> > + *
> > + * When not connected to a terminal interactive_debug is ignored
> > + * and execution immediately continues.
> > + *
> > + * This is useful for display tests where under certain situation manual
> > + * inspection of the display is useful. Or when running a testcase in the
> > + * background.
> > + */
> > +void igt_debug_wait_for_keypress(const char *var)
> > +{
> > +	struct termios oldt, newt;
> > +
> > +	if (!isatty(STDIN_FILENO)) {
> > +		errno = 0; /* otherwise would be either ENOTTY or EBADF */
> > +		return;
> > +	}
> > +
> > +	if (!igt_interactive_debug)
> > +		return;
> > +
> > +	if (strstr(var, "all"))
> > +		igt_assert_f(false, "Bug in test: Do not call igt_debug_wait_for_keypress with \"all\"\n");
> > +
> > +	if (!strstr(igt_interactive_debug, var) &&
> > +	    !strstr(igt_interactive_debug, "all"))
> > +		return;
> > +
> > +	igt_info("Press any key to continue ...\n");
> > +
> > +	tcgetattr ( STDIN_FILENO, &oldt );
> > +	newt = oldt;
> > +	newt.c_lflag &= ~( ICANON | ECHO );
> > +	tcsetattr ( STDIN_FILENO, TCSANOW, &newt );
> > +	getchar();
> > +	tcsetattr ( STDIN_FILENO, TCSANOW, &oldt );
> > +}
> > +
> > +/**
> > + * igt_debug_interactive_mode_check:
> > + * @var: var lookup to to enable this wait
> > + * @expected: message to be printed as expected behaviour before wait for keys Y/n
> > + *
> > + * Waits for a key press when run interactively and when the corresponding debug
> > + * var is set in the --interactive-debug=$var variable. Multiple vars
> > + * can be specified as a comma-separated list or alternatively "all" if a wait
> > + * should happen for all cases.
> > + *
> > + * This is useful for display tests where under certain situation manual
> > + * inspection of the display is useful. Or when running a testcase in the
> > + * background.
> > + *
> > + * When not connected to a terminal interactive_debug is ignored
> > + * and execution immediately continues. For this reason by default this function
> > + * returns true. It returns false only when N/n is pressed indicating the
> > + * user isn't seeing what was expected.
> > + *
> > + * Force test fail when N/n is pressed.
> > + */
> > +void igt_debug_interactive_mode_check(const char *var, const char *expected)
> > +{
> > +	struct termios oldt, newt;
> > +	char key;
> > +
> > +	if (!isatty(STDIN_FILENO)) {
> > +		errno = 0; /* otherwise would be either ENOTTY or EBADF */
> > +		return;
> > +	}
> > +
> > +	if (!igt_interactive_debug)
> > +		return;
> > +
> > +	if (!strstr(igt_interactive_debug, var) &&
> > +	    !strstr(igt_interactive_debug, "all"))
> > +		return;
> > +
> > +	igt_info("Is %s [Y/n]", expected);
> > +
> > +	tcgetattr ( STDIN_FILENO, &oldt );
> > +	newt = oldt;
> > +	newt.c_lflag &= ~ICANON;
> > +	tcsetattr ( STDIN_FILENO, TCSANOW, &newt );
> > +	key = getchar();
> > +	tcsetattr ( STDIN_FILENO, TCSANOW, &oldt );
> > +
> > +	igt_info("\n");
> > +
> > +	igt_assert(key != 'n' && key != 'N');
> > +}
> > +
> > +/**
> > + * igt_lock_mem:
> > + * @size: the amount of memory to lock into RAM, in MB
> > + *
> > + * Allocate @size MB of memory and lock it into RAM. This releases any
> > + * previously locked memory.
> > + *
> > + * Use #igt_unlock_mem to release the currently locked memory.
> > + */
> > +static char *locked_mem;
> > +static size_t locked_size;
> > +
> > +void igt_lock_mem(size_t size)
> > +{
> > +	long pagesize = sysconf(_SC_PAGESIZE);
> > +	size_t i;
> > +	int ret;
> > +
> > +	if (size == 0) {
> > +		return;
> > +	}
> > +
> > +	if (locked_mem) {
> > +		igt_unlock_mem();
> > +		igt_warn("Unlocking previously locked memory.\n");
> > +	}
> > +
> > +	locked_size = size * 1024 * 1024;
> > +
> > +	locked_mem = malloc(locked_size);
> > +	igt_require_f(locked_mem,
> > +		      "Could not malloc %zdMiB for locking.\n", size);
> > +
> > +	/* write into each page to ensure it is allocated */
> > +	for (i = 0; i < locked_size; i += pagesize)
> > +		locked_mem[i] = i;
> > +
> > +	ret = mlock(locked_mem, locked_size);
> > +	igt_assert_f(ret == 0, "Could not mlock %zdMiB.\n", size);
> > +}
> > +
> > +/**
> > + * igt_unlock_mem:
> > + *
> > + * Release and free the RAM used by #igt_lock_mem.
> > + */
> > +void igt_unlock_mem(void)
> > +{
> > +	if (!locked_mem)
> > +		return;
> > +
> > +	munlock(locked_mem, locked_size);
> > +
> > +	free(locked_mem);
> > +	locked_mem = NULL;
> > +}
> > +
> > +/**
> > + * igt_is_process_running:
> > + * @comm: Name of process in the form found in /proc/pid/comm (limited to 15
> > + * chars)
> > + *
> > + * Returns: true in case the process has been found, false otherwise.
> > + *
> > + * This function checks in the process table for an entry with the name @comm.
> > + */
> > +int igt_is_process_running(const char *comm)
> > +{
> > +	PROCTAB *proc;
> > +	proc_t *proc_info;
> > +	bool found = false;
> > +
> > +	proc = openproc(PROC_FILLCOM | PROC_FILLSTAT);
> > +	igt_assert(proc != NULL);
> > +
> > +	while ((proc_info = readproc(proc, NULL))) {
> > +		if (!strncasecmp(proc_info->cmd, comm, sizeof(proc_info->cmd))) {
> > +			freeproc(proc_info);
> > +			found = true;
> > +			break;
> > +		}
> > +		freeproc(proc_info);
> > +	}
> > +
> > +	closeproc(proc);
> > +	return found;
> > +}
> > +
> > +/**
> > + * igt_terminate_process:
> > + * @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_terminate_process(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)
> > +{
> > +	struct pinfo p = { .pid = info->tid, .comm = info->cmd, .fn = fn };
> > +
> > +	if (!*state)
> > +		igt_show_stat_header();
> > +
> > +	__igt_show_stat(&p);
> > +	++*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);
> > +	}
> > +
> > +	closedir(dp);
> > +}
> > +
> > +/*
> > + * 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[30];
> > +	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_warn("%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 void pulseaudio_unload_module(proc_t *proc_info)
> > +{
> > +	struct igt_helper_process pa_proc = {};
> > +	char xdg_dir[PATH_MAX];
> > +	const char *homedir;
> > +	struct passwd *pw;
> > +
> > +	igt_fork_helper(&pa_proc) {
> > +		pw = getpwuid(proc_info->euid);
> > +		homedir = pw->pw_dir;
> > +		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", proc_info->euid);
> > +
> > +		igt_info("Request 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_wait_helper(&pa_proc);
> > +}
> > +
> > +static int pipewire_pulse_pid = 0;
> > +static int pipewire_pw_reserve_pid = 0;
> > +static struct igt_helper_process pw_reserve_proc = {};
> > +
> > +static void pipewire_reserve_wait(void)
> > +{
> > +	char xdg_dir[PATH_MAX];
> > +	const char *homedir;
> > +	struct passwd *pw;
> > +	proc_t *proc_info;
> > +	PROCTAB *proc;
> > +
> > +	igt_fork_helper(&pw_reserve_proc) {
> > +		igt_info("Preventing pipewire-pulse to use the audio drivers\n");
> > +
> > +		proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
> > +		igt_assert(proc != NULL);
> > +
> > +		while ((proc_info = readproc(proc, NULL))) {
> > +			if (pipewire_pulse_pid == proc_info->tid)
> > +				break;
> > +			freeproc(proc_info);
> > +		}
> > +		closeproc(proc);
> > +
> > +		/* Sanity check: if it can't find the process, it means it has gone */
> > +		if (pipewire_pulse_pid != proc_info->tid)
> > +			exit(0);
> > +
> > +		pw = getpwuid(proc_info->euid);
> > +		homedir = pw->pw_dir;
> > +		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", proc_info->euid);
> > +		setgid(proc_info->egid);
> > +		setuid(proc_info->euid);
> > +		clearenv();
> > +		setenv("HOME", homedir, 1);
> > +		setenv("XDG_RUNTIME_DIR",xdg_dir, 1);
> > +		freeproc(proc_info);
> > +
> > +		/*
> > +		 * pw-reserve will run in background. It will only exit when
> > +		 * igt_kill_children() is called later on. So, it shouldn't
> > +		 * call igt_waitchildren(). Instead, just exit with the return
> > +		 * code from pw-reserve.
> > +		 */
> > +		exit(system("pw-reserve -n Audio0 -r"));
> > +	}
> > +}
> > +
> > +/* Maximum time waiting for pw-reserve to start running */
> > +#define PIPEWIRE_RESERVE_MAX_TIME 1000 /* milisseconds */
> > +
> > +int pipewire_pulse_start_reserve(void)
> > +{
> > +	bool is_pw_reserve_running = false;
> > +	proc_t *proc_info;
> > +	int attempts = 0;
> > +	PROCTAB *proc;
> > +
> > +	if (!pipewire_pulse_pid)
> > +		return 0;
> > +
> > +	pipewire_reserve_wait();
> > +
> > +	/*
> > +	 * Note: using pw-reserve to stop using audio only works with
> > +	 * pipewire version 0.3.50 or upper.
> > +	 */
> > +	for (attempts = 0; attempts < PIPEWIRE_RESERVE_MAX_TIME; attempts++) {
> > +		usleep(1000);
> > +		proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
> > +		igt_assert(proc != NULL);
> > +
> > +		while ((proc_info = readproc(proc, NULL))) {
> > +			if (!strcmp(proc_info->cmd, "pw-reserve")) {
> > +				is_pw_reserve_running = true;
> > +				pipewire_pw_reserve_pid = proc_info->tid;
> > +				freeproc(proc_info);
> > +				break;
> > +			}
> > +			freeproc(proc_info);
> > +		}
> > +		closeproc(proc);
> > +		if (is_pw_reserve_running)
> > +			break;
> > +	}
> > +	if (!is_pw_reserve_running) {
> > +		igt_warn("Failed to remove audio drivers from pipewire\n");
> > +		return 1;
> > +	}
> > +	/* Let's grant some time for pw_reserve to notify pipewire via D-BUS */
> > +	usleep(50000);
> > +
> > +	/*
> > +	 * pw-reserve is running, and should have stopped using the audio
> > +	 * drivers. We can now remove the driver.
> > +	 */
> > +
> > +	return 0;
> > +}
> > +
> > +void pipewire_pulse_stop_reserve(void)
> > +{
> > +	if (!pipewire_pulse_pid)
> > +		return;
> > +
> > +	igt_stop_helper(&pw_reserve_proc);
> > +}
> > +
> > +/**
> > + * __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
> > + * @pipewire_pulse_pid: PID of pipewire-pulse process
> > + *
> > + * 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 process is pipewire-pulse, it can't be killed, as systemd will
> > + *	  respawn it. So, instead, the caller should call pw-reserve, remove
> > + *	  the kernel driver and then stop pw-reserve. On such case, this
> > + *	  function returns the PID of pipewire-pulse, but won't touch it.
> > + * 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;
> > +
> > +	/*
> > +	 * Terminating pipewire-pulse require an special procedure, which
> > +	 * is only available at version 0.3.50 and upper. 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.
> > +	 */
> > +	if (!strcmp(proc_info->cmd, "pipewire-pulse")) {
> > +		igt_info("process %d (%s) is using audio device. Should be requested to stop using them.\n",
> > +			 proc_info->tid, proc_info->cmd);
> > +		pipewire_pulse_pid = proc_info->tid;
> > +		return 0;
> > +	}
> > +	/*
> > +	 * pipewire-pulse itself doesn't hook into a /dev/snd device. Instead,
> > +	 * the actual binding happens at the Pipewire Session Manager, e.g.
> > +	 * either wireplumber or pipewire media-session.
> > +	 *
> > +	 * Just killing such processes won't produce any effect, as systemd
> > +	 * will respawn them. So, just ignore here, they'll honor pw-reserve,
> > +	 * when the time comes.
> > +	 */
> > +	if (!strcmp(proc_info->cmd, "pipewire-media-session"))
> > +		return 0;
> > +	if (!strcmp(proc_info->cmd, "wireplumber"))
> > +		return 0;
> > +
> > +	dp = opendir(proc_path);
> > +	if (!dp && errno == ENOENT)
> > +		return 0;
> > +	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;
> > +		}
> > +
> > +		/* 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);
> > +	pipewire_pulse_pid = 0;
> > +
> > +	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;
> > +	struct sigaction oldact;
> > +	struct igt_mean mean;
> > +
> > +	int sig;
> > +} igt_siglatency;
> > +
> > +static long delay(void)
> > +{
> > +	return hars_petruska_f54_1_random_unsafe() % (NSEC_PER_SEC / 1000);
> > +}
> > +
> > +static double elapsed(const struct timespec *now, const struct timespec *last)
> > +{
> > +	double nsecs;
> > +
> > +	nsecs = now->tv_nsec - last ->tv_nsec;
> > +	nsecs += 1e9*(now->tv_sec - last->tv_sec);
> > +
> > +	return nsecs;
> > +}
> > +
> > +static void siglatency(int sig, siginfo_t *info, void *arg)
> > +{
> > +	struct itimerspec its;
> > +
> > +	clock_gettime(CLOCK_MONOTONIC, &its.it_value);
> > +	if (info)
> > +		igt_mean_add(&igt_siglatency.mean,
> > +			     elapsed(&its.it_value, &igt_siglatency.target));
> > +	igt_siglatency.target = its.it_value;
> > +
> > +	its.it_value.tv_nsec += 100 * 1000;
> > +	its.it_value.tv_nsec += delay();
> > +	if (its.it_value.tv_nsec >= NSEC_PER_SEC) {
> > +		its.it_value.tv_nsec -= NSEC_PER_SEC;
> > +		its.it_value.tv_sec += 1;
> > +	}
> > +	its.it_interval.tv_sec = its.it_interval.tv_nsec = 0;
> > +	timer_settime(igt_siglatency.timer, TIMER_ABSTIME, &its, NULL);
> > +}
> > +
> > +void igt_start_siglatency(int sig)
> > +{
> > +	struct sigevent sev;
> > +	struct sigaction act;
> > +
> > +	if (sig <= 0)
> > +		sig = SIGRTMIN;
> > +
> > +	if (igt_siglatency.sig)
> > +		(void)igt_stop_siglatency(NULL);
> > +	igt_assert(igt_siglatency.sig == 0);
> > +	igt_siglatency.sig = sig;
> > +
> > +	memset(&sev, 0, sizeof(sev));
> > +	sev.sigev_notify = SIGEV_SIGNAL | SIGEV_THREAD_ID;
> > +	sev.sigev_notify_thread_id = gettid();
> > +	sev.sigev_signo = sig;
> > +	timer_create(CLOCK_MONOTONIC, &sev, &igt_siglatency.timer);
> > +
> > +	memset(&act, 0, sizeof(act));
> > +	act.sa_sigaction = siglatency;
> > +	sigaction(sig, &act, &igt_siglatency.oldact);
> > +
> > +	siglatency(sig, NULL, NULL);
> > +}
> > +
> > +double igt_stop_siglatency(struct igt_mean *result)
> > +{
> > +	double mean = igt_mean_get(&igt_siglatency.mean);
> > +
> > +	if (result)
> > +		*result = igt_siglatency.mean;
> > +
> > +	sigaction(igt_siglatency.sig, &igt_siglatency.oldact, NULL);
> > +	timer_delete(igt_siglatency.timer);
> > +	memset(&igt_siglatency, 0, sizeof(igt_siglatency));
> > +
> > +	return mean;
> > +}
> > +
> > +bool igt_allow_unlimited_files(void)
> > +{
> > +	struct rlimit rlim;
> > +	unsigned nofile_rlim = 1024*1024;
> > +
> > +	FILE *file = fopen("/proc/sys/fs/nr_open", "r");
> > +	if (file) {
> > +		igt_assert(fscanf(file, "%u", &nofile_rlim) == 1);
> > +		igt_info("System limit for open files is %u\n", nofile_rlim);
> > +		fclose(file);
> > +	}
> > +
> > +	if (getrlimit(RLIMIT_NOFILE, &rlim))
> > +		return false;
> > +
> > +	rlim.rlim_cur = nofile_rlim;
> > +	rlim.rlim_max = nofile_rlim;
> > +	return setrlimit(RLIMIT_NOFILE, &rlim) == 0;
> > +}
> > +
> > +/**
> > + * vfs_file_max: report maximum number of files
> > + *
> > + * Get the global system-wide maximum of open files the kernel allows,
> > + * by reading /proc/sys/fs/file-max. Fails the current subtest if
> > + * reading the file fails, and returns a suitable best guess if it
> > + * cannot be opened.
> > + *
> > + * Returns: System-wide maximum of open files, or a best effort guess.
> > + */
> > +uint64_t vfs_file_max(void)
> > +{
> > +	static long long unsigned max;
> > +	if (max == 0) {
> > +		FILE *file = fopen("/proc/sys/fs/file-max", "r");
> > +		max = 80000;
> > +		if (file) {
> > +			igt_assert(fscanf(file, "%llu", &max) == 1);
> > +			fclose(file);
> > +		}
> > +	}
> > +	return max;
> > +}
> > +
> > +void *igt_memdup(const void *ptr, size_t len)
> > +{
> > +	void *dup;
> > +
> > +	dup = malloc(len);
> > +	if (dup)
> > +		memcpy(dup, ptr, len);
> > +
> > +	return dup;
> > +}
> > diff --git a/lib/meson.build b/lib/meson.build
> > index cef2d0ff..cb83ee99 100644
> > --- a/lib/meson.build
> > +++ b/lib/meson.build
> > @@ -103,7 +103,6 @@ lib_deps = [
> >  	libdrm,
> >  	libdw,
> >  	libkmod,
> > -	libprocps,
> >  	libudev,
> >  	math,
> >  	pciaccess,
> > @@ -173,6 +172,12 @@ if chamelium.found()
> >  	lib_sources += 'monitor_edids/monitor_edids_helper.c'
> >  endif
> >  
> > +if libprocps.found()
> > +	lib_deps += libprocps
> > +else
> > +	lib_deps += libproc2
> > +endif
> > +
> 
> If we want to use newer version maybe this should be other way:
> if libprocps2.found()
> 	lib_deps += libproc2
> else
> 	lib_deps += libprocps
> endif
> 
> Regards,
> Kamil
> 
> >  if get_option('srcdir') != ''
> >      srcdir = join_paths(get_option('srcdir'), 'tests')
> >  else
> > diff --git a/lib/meson.build.orig b/lib/meson.build.orig
> > new file mode 100644
> > index 00000000..cef2d0ff
> > --- /dev/null
> > +++ b/lib/meson.build.orig
> > @@ -0,0 +1,358 @@
> > +lib_sources = [
> > +	'drmtest.c',
> > +	'huc_copy.c',
> > +	'i915/gem.c',
> > +	'i915/gem_context.c',
> > +	'i915/gem_create.c',
> > +	'i915/gem_engine_topology.c',
> > +	'i915/gem_scheduler.c',
> > +	'i915/gem_submission.c',
> > +	'i915/gem_ring.c',
> > +	'i915/gem_mman.c',
> > +	'i915/gem_vm.c',
> > +	'i915/intel_memory_region.c',
> > +	'i915/intel_mocs.c',
> > +	'i915/i915_blt.c',
> > +	'i915/i915_crc.c',
> > +	'igt_collection.c',
> > +	'igt_color_encoding.c',
> > +	'igt_crc.c',
> > +	'igt_debugfs.c',
> > +	'igt_device.c',
> > +	'igt_device_scan.c',
> > +	'igt_drm_fdinfo.c',
> > +	'igt_aux.c',
> > +	'igt_gt.c',
> > +	'igt_halffloat.c',
> > +	'igt_hwmon.c',
> > +	'igt_io.c',
> > +	'igt_matrix.c',
> > +	'igt_os.c',
> > +	'igt_params.c',
> > +	'igt_perf.c',
> > +	'igt_pipe_crc.c',
> > +	'igt_power.c',
> > +	'igt_primes.c',
> > +	'igt_rand.c',
> > +	'igt_stats.c',
> > +	'igt_syncobj.c',
> > +	'igt_sysfs.c',
> > +	'igt_sysrq.c',
> > +	'igt_taints.c',
> > +	'igt_thread.c',
> > +	'igt_types.c',
> > +	'igt_vec.c',
> > +	'igt_vgem.c',
> > +	'igt_x86.c',
> > +	'instdone.c',
> > +	'intel_allocator.c',
> > +	'intel_allocator_msgchannel.c',
> > +	'intel_allocator_random.c',
> > +	'intel_allocator_reloc.c',
> > +	'intel_allocator_simple.c',
> > +	'intel_batchbuffer.c',
> > +	'intel_bufops.c',
> > +	'intel_chipset.c',
> > +	'intel_ctx.c',
> > +	'intel_device_info.c',
> > +	'intel_mmio.c',
> > +	'ioctl_wrappers.c',
> > +	'media_spin.c',
> > +	'media_fill.c',
> > +	'gpgpu_fill.c',
> > +	'gpu_cmds.c',
> > +	'rendercopy_i915.c',
> > +	'rendercopy_i830.c',
> > +	'rendercopy_gen4.c',
> > +	'rendercopy_gen6.c',
> > +	'rendercopy_gen7.c',
> > +	'rendercopy_gen8.c',
> > +	'rendercopy_gen9.c',
> > +	'runnercomms.c',
> > +	'sw_sync.c',
> > +	'intel_aux_pgtable.c',
> > +	'intel_reg_map.c',
> > +	'intel_iosf.c',
> > +	'igt_kms.c',
> > +	'igt_fb.c',
> > +	'igt_core.c',
> > +	'igt_draw.c',
> > +	'igt_list.c',
> > +	'igt_map.c',
> > +	'igt_pm.c',
> > +	'igt_dummyload.c',
> > +	'igt_store.c',
> > +	'uwildmat/uwildmat.c',
> > +	'igt_kmod.c',
> > +	'igt_panfrost.c',
> > +	'igt_v3d.c',
> > +	'igt_vc4.c',
> > +	'igt_psr.c',
> > +	'igt_amd.c',
> > +	'igt_edid.c',
> > +	'igt_eld.c',
> > +	'igt_infoframe.c',
> > +	'veboxcopy_gen12.c',
> > +	'igt_msm.c',
> > +]
> > +
> > +lib_deps = [
> > +	cairo,
> > +	glib,
> > +	libatomic,
> > +	libdrm,
> > +	libdw,
> > +	libkmod,
> > +	libprocps,
> > +	libudev,
> > +	math,
> > +	pciaccess,
> > +	pixman,
> > +	pthreads,
> > +	realtime,
> > +	zlib
> > +]
> > +
> > +if libdrm_intel.found()
> > +	lib_deps += libdrm_intel
> > +else
> > +	lib_sources += 'stubs/drm/intel_bufmgr.c'
> > +	inc = [ inc, include_directories('stubs/drm') ]
> > +endif
> > +
> > +if libdrm_nouveau.found()
> > +	lib_deps += libdrm_nouveau
> > +	lib_sources += [
> > +		'igt_nouveau.c',
> > +		'nouveau/cea0b5.c'
> > +	]
> > +endif
> > +
> > +if libdrm_amdgpu.found()
> > +	lib_deps += libdrm_amdgpu
> > +	lib_sources += [
> > +		'amdgpu/amd_memory.c',
> > +		'amdgpu/amd_command_submission.c',
> > +		'amdgpu/amd_compute.c',
> > +		'amdgpu/amd_gfx.c',
> > +		'amdgpu/amd_ip_blocks.c',
> > +		'amdgpu/amd_shaders.c',
> > +		'amdgpu/amd_gfx_v8_0.c',
> > +		'amdgpu/amd_gfx_v9_0.c',
> > +		'amdgpu/amd_dispatch_helpers.c',
> > +		'amdgpu/amd_dispatch.c',
> > +		'amdgpu/amd_deadlock_helpers.c',
> > +		'amdgpu/amd_pci_unplug.c',
> > +		'amdgpu/xalloc.h'
> > +	]
> > +endif
> > +
> > +if libunwind.found()
> > +	lib_deps += libunwind
> > +else
> > +	inc = [ inc, include_directories('stubs/libunwind') ]
> > +endif
> > +
> > +if valgrind.found()
> > +	lib_deps += valgrind
> > +endif
> > +
> > +if gsl.found()
> > +	lib_deps += gsl
> > +	lib_sources += [ 'igt_frame.c', 'igt_audio.c' ]
> > +endif
> > +
> > +if alsa.found()
> > +	lib_deps += alsa
> > +	lib_sources += 'igt_alsa.c'
> > +endif
> > +
> > +if chamelium.found()
> > +	lib_deps += chamelium
> > +	lib_sources += [ 'igt_chamelium.c', 'igt_chamelium_stream.c' ]
> > +	lib_sources += 'monitor_edids/monitor_edids_helper.c'
> > +endif
> > +
> > +if get_option('srcdir') != ''
> > +    srcdir = join_paths(get_option('srcdir'), 'tests')
> > +else
> > +    srcdir = join_paths(meson.source_root(), 'tests')
> > +endif
> > +
> > +if get_option('version_hash') != ''
> > +    vcs_command = ['echo', get_option('version_hash')]
> > +else
> > +    vcs_command = [ 'git', 'log', '-n1', '--pretty=format:g%h' ]
> > +endif
> > +
> > +lib_version = vcs_tag(input : 'version.h.in', output : 'version.h',
> > +		      fallback : 'NO-GIT',
> > +		      command : vcs_command )
> > +
> > +lib_intermediates = []
> > +foreach f: lib_sources
> > +    name = f.underscorify()
> > +    lib = static_library('igt-' + name,
> > +	[ f, lib_version ],
> > +	include_directories: inc,
> > +	dependencies : lib_deps,
> > +	c_args : [
> > +	    '-DIGT_DATADIR="@0@"'.format(join_paths(prefix, datadir)),
> > +	    '-DIGT_SRCDIR="@0@"'.format(srcdir),
> > +	    '-DIGT_LOG_DOMAIN="@0@"'.format(f.split('.')[0]),
> > +	])
> > +
> > +    lib_intermediates += lib
> > +endforeach
> > +
> > +lib_igt_build = shared_library('igt',
> > +    ['dummy.c'],
> > +    link_whole: lib_intermediates,
> > +    dependencies: lib_deps,
> > +    install : true,
> > +    soversion : '0',
> > +)
> > +
> > +lib_igt = declare_dependency(link_with : lib_igt_build,
> > +			    include_directories : inc)
> > +
> > +igt_deps = [ lib_igt ] + lib_deps
> > +
> > +lin_igt_chipset_build = static_library('igt_chipset',
> > +                                       ['intel_chipset.c',
> > +                                        'intel_device_info.c'],
> > +                                       include_directories : inc)
> > +
> > +lib_igt_chipset = declare_dependency(link_with : lin_igt_chipset_build,
> > +                                     include_directories : inc)
> > +
> > +lib_igt_perf_build = static_library('igt_perf',
> > +	['igt_perf.c'],
> > +	include_directories : inc)
> > +
> > +lib_igt_perf = declare_dependency(link_with : lib_igt_perf_build,
> > +				  include_directories : inc)
> > +
> > +scan_dep = [
> > +	glib,
> > +	libudev,
> > +]
> > +
> > +lib_igt_device_scan_build = static_library('igt_device_scan',
> > +	['igt_device_scan.c',
> > +	'igt_list.c',
> > +	'igt_tools_stub.c',
> > +	'intel_device_info.c',
> > +	],
> > +	dependencies : scan_dep,
> > +	include_directories : inc)
> > +
> > +lib_igt_device_scan = declare_dependency(link_with : lib_igt_device_scan_build,
> > +				  include_directories : inc)
> > +
> > +lib_igt_drm_fdinfo_build = static_library('igt_drm_fdinfo',
> > +	['igt_drm_fdinfo.c'],
> > +	include_directories : inc)
> > +
> > +lib_igt_drm_fdinfo = declare_dependency(link_with : lib_igt_drm_fdinfo_build,
> > +				  include_directories : inc)
> > +i915_perf_files = [
> > +  'igt_list.c',
> > +  'i915/perf.c',
> > +  'i915/perf_data_reader.c',
> > +]
> > +
> > +i915_perf_hardware = [
> > +  'hsw',
> > +  'bdw', 'chv',
> > +  'sklgt2', 'sklgt3', 'sklgt4',
> > +  'kblgt2', 'kblgt3',
> > +  'cflgt2', 'cflgt3',
> > +  'bxt', 'glk',
> > +  'cnl',
> > +  'icl', 'ehl',
> > +  'tglgt1', 'tglgt2', 'rkl', 'dg1', 'adl',
> > +  'acmgt1', 'acmgt2', 'acmgt3',
> > +]
> > +
> > +i915_xml_files = []
> > +foreach hw : i915_perf_hardware
> > +  i915_xml_files += files('i915/perf-configs/oa-@0@.xml'.format(hw))
> > +endforeach
> > +
> > +i915_perf_files += custom_target(
> > +  'i915-perf-equations',
> > +  input : [ 'i915/perf-configs/perf-equations-codegen.py' ] + i915_xml_files,
> > +  output : [ 'i915_perf_equations.c', 'i915_perf_equations.h' ],
> > +  command : [
> > +    python3, '@INPUT0@',
> > +    '--code', '@OUTPUT0@',
> > +    '--header', '@OUTPUT1@',
> > +    i915_xml_files,
> > +  ])
> > +
> > +foreach hw : i915_perf_hardware
> > +  i915_perf_files += custom_target(
> > +    'i915-perf-registers-@0@'.format(hw),
> > +    input : [ 'i915/perf-configs/perf-registers-codegen.py',
> > +              'i915/perf-configs/oa-@0@.xml'.format(hw) ],
> > +    output : [ 'i915_perf_registers_@0@.c'.format(hw),
> > +               'i915_perf_registers_@0@.h'.format(hw), ],
> > +    command : [
> > +      python3, '@INPUT0@',
> > +      '--code', '@OUTPUT0@',
> > +      '--header', '@OUTPUT1@',
> > +      '--xml-file', '@INPUT1@'
> > +    ])
> > +  i915_perf_files += custom_target(
> > +    'i915-perf-metrics-@0@'.format(hw),
> > +    input : [ 'i915/perf-configs/perf-metricset-codegen.py',
> > +              'i915/perf-configs/oa-@0@.xml'.format(hw) ],
> > +    output : [ 'i915_perf_metrics_@0@.c'.format(hw),
> > +               'i915_perf_metrics_@0@.h'.format(hw), ],
> > +    command : [
> > +      python3, '@INPUT0@',
> > +      '--code', '@OUTPUT0@',
> > +      '--header', '@OUTPUT1@',
> > +      '--equations-include', 'i915_perf_equations.h',
> > +      '--registers-include', 'i915_perf_registers_@0@.h'.format(hw),
> > +      '--xml-file', '@INPUT1@',
> > +    ])
> > +endforeach
> > +
> > +lib_igt_i915_perf_build = shared_library(
> > +  'i915_perf',
> > +  i915_perf_files,
> > +  dependencies: lib_igt_chipset,
> > +  include_directories : inc,
> > +  install: true,
> > +  soversion: '1.5')
> > +
> > +lib_igt_i915_perf = declare_dependency(
> > +  link_with : lib_igt_i915_perf_build,
> > +  include_directories : inc)
> > +
> > +install_headers(
> > +  'igt_list.h',
> > +  'intel_chipset.h',
> > +  'i915/perf.h',
> > +  'i915/perf_data.h',
> > +  'i915/perf_data_reader.h',
> > +  subdir : 'i915-perf'
> > +)
> > +
> > +pkgconf = configuration_data()
> > +
> > +pkgconf.set('prefix', get_option('prefix'))
> > +pkgconf.set('exec_prefix', '${prefix}')
> > +pkgconf.set('libdir', '${prefix}/@0@'.format(get_option('libdir')))
> > +pkgconf.set('includedir', '${prefix}/@0@'.format(get_option('includedir')))
> > +pkgconf.set('i915_perf_version', '1.5.1')
> > +
> > +configure_file(
> > +  input : 'i915-perf.pc.in',
> > +  output : 'i915-perf.pc',
> > +  configuration : pkgconf,
> > +  install_dir : pkgconfigdir)
> > +
> > +subdir('tests')
> > diff --git a/meson.build b/meson.build
> > index 3e937f5a..475680ac 100644
> > --- a/meson.build
> > +++ b/meson.build
> > @@ -121,7 +121,15 @@ build_info += 'With libdrm: ' + ','.join(libdrm_info)
> >  
> >  pciaccess = dependency('pciaccess', version : '>=0.10')
> >  libkmod = dependency('libkmod')
> > -libprocps = dependency('libprocps', required : true)
> > +libprocps = dependency('libprocps', required : false)
> > +libproc2 = dependency('libproc2', required : false)
> > +if libprocps.found()
> > +  config.set('HAVE_LIBPROCPS', 1)
> > +elif libproc2.found()
> > +  config.set('HAVE_LIBPROC2', 1)
> > +else
> > +  error('Either libprocps or libproc2 is required')
> > +endif
> >  
> >  libunwind = dependency('libunwind', required : get_option('libunwind'))
> >  build_info += 'With libunwind: @0@'.format(libunwind.found())
> > -- 
> > 2.30.2
> > 

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [igt-dev] ✗ Fi.CI.IGT: failure for Use the new procps library libproc2
  2022-11-17  8:41 [igt-dev] [PATCH i-g-t] Use the new procps library libproc2 Petri Latvala
                   ` (2 preceding siblings ...)
  2022-11-17 11:17 ` [igt-dev] [PATCH i-g-t] " Kamil Konieczny
@ 2022-11-17 20:08 ` Patchwork
  3 siblings, 0 replies; 11+ messages in thread
From: Patchwork @ 2022-11-17 20:08 UTC (permalink / raw)
  To: Petri Latvala; +Cc: igt-dev

[-- Attachment #1: Type: text/plain, Size: 34826 bytes --]

== Series Details ==

Series: Use the new procps library libproc2
URL   : https://patchwork.freedesktop.org/series/111015/
State : failure

== Summary ==

CI Bug Log - changes from CI_DRM_12392_full -> IGTPW_8124_full
====================================================

Summary
-------

  **FAILURE**

  Serious unknown changes coming with IGTPW_8124_full absolutely need to be
  verified manually.
  
  If you think the reported changes have nothing to do with the changes
  introduced in IGTPW_8124_full, please notify your bug team to allow them
  to document this new failure mode, which will reduce false positives in CI.

  External URL: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/index.html

Participating hosts (9 -> 6)
------------------------------

  Missing    (3): pig-skl-6260u pig-kbl-iris pig-glk-j5005 

Possible new issues
-------------------

  Here are the unknown changes that may have been introduced in IGTPW_8124_full:

### IGT changes ###

#### Possible regressions ####

  * igt@kms_ccs@pipe-a-crc-sprite-planes-basic-yf_tiled_ccs:
    - shard-glk:          [PASS][1] -> [FAIL][2]
   [1]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-glk1/igt@kms_ccs@pipe-a-crc-sprite-planes-basic-yf_tiled_ccs.html
   [2]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-glk2/igt@kms_ccs@pipe-a-crc-sprite-planes-basic-yf_tiled_ccs.html

  
Known issues
------------

  Here are the changes found in IGTPW_8124_full that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@gem_eio@in-flight-contexts-10ms:
    - shard-tglb:         [PASS][3] -> [TIMEOUT][4] ([i915#3063])
   [3]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-tglb1/igt@gem_eio@in-flight-contexts-10ms.html
   [4]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb7/igt@gem_eio@in-flight-contexts-10ms.html

  * igt@gem_exec_balancer@parallel-out-fence:
    - shard-iclb:         NOTRUN -> [SKIP][5] ([i915#4525]) +1 similar issue
   [5]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb5/igt@gem_exec_balancer@parallel-out-fence.html

  * igt@gem_exec_fair@basic-none-solo@rcs0:
    - shard-apl:          [PASS][6] -> [FAIL][7] ([i915#2842]) +2 similar issues
   [6]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-apl7/igt@gem_exec_fair@basic-none-solo@rcs0.html
   [7]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-apl3/igt@gem_exec_fair@basic-none-solo@rcs0.html

  * igt@gem_exec_fair@basic-pace@vcs1:
    - shard-tglb:         NOTRUN -> [FAIL][8] ([i915#2842]) +4 similar issues
   [8]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb2/igt@gem_exec_fair@basic-pace@vcs1.html

  * igt@gem_exec_fair@basic-pace@vecs0:
    - shard-iclb:         NOTRUN -> [FAIL][9] ([i915#2842]) +2 similar issues
   [9]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb7/igt@gem_exec_fair@basic-pace@vecs0.html

  * igt@gem_lmem_swapping@parallel-random:
    - shard-iclb:         NOTRUN -> [SKIP][10] ([i915#4613]) +1 similar issue
   [10]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb2/igt@gem_lmem_swapping@parallel-random.html
    - shard-apl:          NOTRUN -> [SKIP][11] ([fdo#109271] / [i915#4613]) +2 similar issues
   [11]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-apl6/igt@gem_lmem_swapping@parallel-random.html
    - shard-glk:          NOTRUN -> [SKIP][12] ([fdo#109271] / [i915#4613]) +1 similar issue
   [12]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-glk7/igt@gem_lmem_swapping@parallel-random.html

  * igt@gem_lmem_swapping@verify-random:
    - shard-tglb:         NOTRUN -> [SKIP][13] ([i915#4613]) +2 similar issues
   [13]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb3/igt@gem_lmem_swapping@verify-random.html

  * igt@gem_pxp@reject-modify-context-protection-off-3:
    - shard-snb:          NOTRUN -> [SKIP][14] ([fdo#109271]) +82 similar issues
   [14]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-snb5/igt@gem_pxp@reject-modify-context-protection-off-3.html
    - shard-tglb:         NOTRUN -> [SKIP][15] ([i915#4270]) +1 similar issue
   [15]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb3/igt@gem_pxp@reject-modify-context-protection-off-3.html
    - shard-iclb:         NOTRUN -> [SKIP][16] ([i915#4270])
   [16]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb5/igt@gem_pxp@reject-modify-context-protection-off-3.html

  * igt@gem_userptr_blits@vma-merge:
    - shard-apl:          NOTRUN -> [FAIL][17] ([i915#3318])
   [17]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-apl1/igt@gem_userptr_blits@vma-merge.html
    - shard-snb:          NOTRUN -> [FAIL][18] ([i915#2724])
   [18]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-snb5/igt@gem_userptr_blits@vma-merge.html
    - shard-tglb:         NOTRUN -> [FAIL][19] ([i915#3318])
   [19]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb7/igt@gem_userptr_blits@vma-merge.html
    - shard-glk:          NOTRUN -> [FAIL][20] ([i915#3318])
   [20]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-glk8/igt@gem_userptr_blits@vma-merge.html
    - shard-iclb:         NOTRUN -> [FAIL][21] ([i915#3318])
   [21]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb3/igt@gem_userptr_blits@vma-merge.html

  * igt@gen9_exec_parse@bb-oversize:
    - shard-tglb:         NOTRUN -> [SKIP][22] ([i915#2527] / [i915#2856]) +1 similar issue
   [22]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb3/igt@gen9_exec_parse@bb-oversize.html
    - shard-iclb:         NOTRUN -> [SKIP][23] ([i915#2856])
   [23]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb5/igt@gen9_exec_parse@bb-oversize.html

  * igt@i915_pm_dc@dc6-psr:
    - shard-iclb:         [PASS][24] -> [FAIL][25] ([i915#3989] / [i915#454])
   [24]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-iclb6/igt@i915_pm_dc@dc6-psr.html
   [25]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb8/igt@i915_pm_dc@dc6-psr.html

  * igt@i915_pm_dc@dc9-dpms:
    - shard-iclb:         [PASS][26] -> [SKIP][27] ([i915#4281])
   [26]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-iclb6/igt@i915_pm_dc@dc9-dpms.html
   [27]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb3/igt@i915_pm_dc@dc9-dpms.html

  * igt@i915_pm_rc6_residency@rc6-idle@rcs0:
    - shard-tglb:         NOTRUN -> [WARN][28] ([i915#2681]) +3 similar issues
   [28]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb6/igt@i915_pm_rc6_residency@rc6-idle@rcs0.html

  * igt@i915_query@query-topology-unsupported:
    - shard-tglb:         NOTRUN -> [SKIP][29] ([fdo#109302])
   [29]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb6/igt@i915_query@query-topology-unsupported.html

  * igt@i915_suspend@fence-restore-tiled2untiled:
    - shard-apl:          [PASS][30] -> [DMESG-WARN][31] ([i915#180])
   [30]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-apl2/igt@i915_suspend@fence-restore-tiled2untiled.html
   [31]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-apl6/igt@i915_suspend@fence-restore-tiled2untiled.html

  * igt@kms_big_fb@4-tiled-16bpp-rotate-0:
    - shard-tglb:         NOTRUN -> [SKIP][32] ([i915#5286]) +1 similar issue
   [32]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb3/igt@kms_big_fb@4-tiled-16bpp-rotate-0.html
    - shard-iclb:         NOTRUN -> [SKIP][33] ([i915#5286])
   [33]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb5/igt@kms_big_fb@4-tiled-16bpp-rotate-0.html

  * igt@kms_big_fb@linear-64bpp-rotate-90:
    - shard-tglb:         NOTRUN -> [SKIP][34] ([fdo#111614])
   [34]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb8/igt@kms_big_fb@linear-64bpp-rotate-90.html
    - shard-iclb:         NOTRUN -> [SKIP][35] ([fdo#110725] / [fdo#111614])
   [35]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb6/igt@kms_big_fb@linear-64bpp-rotate-90.html

  * igt@kms_big_fb@linear-8bpp-rotate-180:
    - shard-glk:          [PASS][36] -> [FAIL][37] ([i915#5138])
   [36]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-glk3/igt@kms_big_fb@linear-8bpp-rotate-180.html
   [37]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-glk5/igt@kms_big_fb@linear-8bpp-rotate-180.html

  * igt@kms_big_fb@x-tiled-max-hw-stride-64bpp-rotate-0-async-flip:
    - shard-snb:          [PASS][38] -> [SKIP][39] ([fdo#109271]) +2 similar issues
   [38]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-snb7/igt@kms_big_fb@x-tiled-max-hw-stride-64bpp-rotate-0-async-flip.html
   [39]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-snb2/igt@kms_big_fb@x-tiled-max-hw-stride-64bpp-rotate-0-async-flip.html

  * igt@kms_big_fb@yf-tiled-32bpp-rotate-0:
    - shard-tglb:         NOTRUN -> [SKIP][40] ([fdo#111615]) +3 similar issues
   [40]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb2/igt@kms_big_fb@yf-tiled-32bpp-rotate-0.html

  * igt@kms_big_fb@yf-tiled-8bpp-rotate-180:
    - shard-iclb:         NOTRUN -> [SKIP][41] ([fdo#110723])
   [41]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb3/igt@kms_big_fb@yf-tiled-8bpp-rotate-180.html

  * igt@kms_ccs@pipe-a-ccs-on-another-bo-yf_tiled_ccs:
    - shard-tglb:         NOTRUN -> [SKIP][42] ([fdo#111615] / [i915#3689]) +1 similar issue
   [42]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb7/igt@kms_ccs@pipe-a-ccs-on-another-bo-yf_tiled_ccs.html

  * igt@kms_ccs@pipe-a-crc-primary-rotation-180-4_tiled_dg2_rc_ccs:
    - shard-apl:          NOTRUN -> [SKIP][43] ([fdo#109271]) +100 similar issues
   [43]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-apl2/igt@kms_ccs@pipe-a-crc-primary-rotation-180-4_tiled_dg2_rc_ccs.html

  * igt@kms_ccs@pipe-a-crc-primary-rotation-180-y_tiled_gen12_rc_ccs_cc:
    - shard-apl:          NOTRUN -> [SKIP][44] ([fdo#109271] / [i915#3886]) +7 similar issues
   [44]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-apl1/igt@kms_ccs@pipe-a-crc-primary-rotation-180-y_tiled_gen12_rc_ccs_cc.html

  * igt@kms_ccs@pipe-b-bad-aux-stride-y_tiled_gen12_rc_ccs_cc:
    - shard-iclb:         NOTRUN -> [SKIP][45] ([fdo#109278] / [i915#3886]) +1 similar issue
   [45]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb1/igt@kms_ccs@pipe-b-bad-aux-stride-y_tiled_gen12_rc_ccs_cc.html

  * igt@kms_ccs@pipe-b-bad-rotation-90-y_tiled_ccs:
    - shard-tglb:         NOTRUN -> [SKIP][46] ([i915#3689]) +6 similar issues
   [46]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb3/igt@kms_ccs@pipe-b-bad-rotation-90-y_tiled_ccs.html

  * igt@kms_ccs@pipe-b-missing-ccs-buffer-y_tiled_gen12_mc_ccs:
    - shard-tglb:         NOTRUN -> [SKIP][47] ([i915#3689] / [i915#3886])
   [47]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb2/igt@kms_ccs@pipe-b-missing-ccs-buffer-y_tiled_gen12_mc_ccs.html

  * igt@kms_ccs@pipe-c-bad-pixel-format-4_tiled_dg2_rc_ccs:
    - shard-tglb:         NOTRUN -> [SKIP][48] ([i915#6095]) +1 similar issue
   [48]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb5/igt@kms_ccs@pipe-c-bad-pixel-format-4_tiled_dg2_rc_ccs.html

  * igt@kms_ccs@pipe-c-ccs-on-another-bo-y_tiled_gen12_rc_ccs:
    - shard-iclb:         NOTRUN -> [SKIP][49] ([fdo#109278]) +9 similar issues
   [49]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb3/igt@kms_ccs@pipe-c-ccs-on-another-bo-y_tiled_gen12_rc_ccs.html

  * igt@kms_ccs@pipe-c-missing-ccs-buffer-y_tiled_gen12_rc_ccs_cc:
    - shard-glk:          NOTRUN -> [SKIP][50] ([fdo#109271] / [i915#3886]) +3 similar issues
   [50]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-glk5/igt@kms_ccs@pipe-c-missing-ccs-buffer-y_tiled_gen12_rc_ccs_cc.html

  * igt@kms_chamelium@dp-edid-read:
    - shard-iclb:         NOTRUN -> [SKIP][51] ([fdo#109284] / [fdo#111827]) +3 similar issues
   [51]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb2/igt@kms_chamelium@dp-edid-read.html

  * igt@kms_chamelium@dp-hpd:
    - shard-glk:          NOTRUN -> [SKIP][52] ([fdo#109271] / [fdo#111827]) +5 similar issues
   [52]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-glk2/igt@kms_chamelium@dp-hpd.html

  * igt@kms_chamelium@hdmi-edid-change-during-suspend:
    - shard-snb:          NOTRUN -> [SKIP][53] ([fdo#109271] / [fdo#111827]) +3 similar issues
   [53]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-snb5/igt@kms_chamelium@hdmi-edid-change-during-suspend.html
    - shard-apl:          NOTRUN -> [SKIP][54] ([fdo#109271] / [fdo#111827]) +8 similar issues
   [54]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-apl1/igt@kms_chamelium@hdmi-edid-change-during-suspend.html

  * igt@kms_chamelium@vga-hpd-enable-disable-mode:
    - shard-tglb:         NOTRUN -> [SKIP][55] ([fdo#109284] / [fdo#111827]) +6 similar issues
   [55]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb8/igt@kms_chamelium@vga-hpd-enable-disable-mode.html

  * igt@kms_content_protection@legacy:
    - shard-tglb:         NOTRUN -> [SKIP][56] ([i915#7118])
   [56]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb3/igt@kms_content_protection@legacy.html
    - shard-iclb:         NOTRUN -> [SKIP][57] ([i915#7118])
   [57]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb5/igt@kms_content_protection@legacy.html

  * igt@kms_content_protection@legacy@pipe-a-dp-1:
    - shard-apl:          NOTRUN -> [TIMEOUT][58] ([i915#7173])
   [58]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-apl8/igt@kms_content_protection@legacy@pipe-a-dp-1.html

  * igt@kms_cursor_crc@cursor-onscreen-max-size:
    - shard-iclb:         NOTRUN -> [SKIP][59] ([i915#3555]) +2 similar issues
   [59]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb8/igt@kms_cursor_crc@cursor-onscreen-max-size.html

  * igt@kms_cursor_legacy@cursor-vs-flip@varying-size:
    - shard-iclb:         [PASS][60] -> [FAIL][61] ([i915#5072]) +2 similar issues
   [60]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-iclb6/igt@kms_cursor_legacy@cursor-vs-flip@varying-size.html
   [61]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb7/igt@kms_cursor_legacy@cursor-vs-flip@varying-size.html

  * igt@kms_flip@2x-plain-flip-fb-recreate-interruptible:
    - shard-tglb:         NOTRUN -> [SKIP][62] ([fdo#109274] / [fdo#111825] / [i915#3637]) +2 similar issues
   [62]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb7/igt@kms_flip@2x-plain-flip-fb-recreate-interruptible.html

  * igt@kms_flip@2x-wf_vblank-ts-check:
    - shard-iclb:         NOTRUN -> [SKIP][63] ([fdo#109274]) +1 similar issue
   [63]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb1/igt@kms_flip@2x-wf_vblank-ts-check.html

  * igt@kms_flip_scaled_crc@flip-32bpp-4tile-to-32bpp-4tiledg2rcccs-upscaling@pipe-a-default-mode:
    - shard-iclb:         NOTRUN -> [SKIP][64] ([i915#2672]) +1 similar issue
   [64]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb3/igt@kms_flip_scaled_crc@flip-32bpp-4tile-to-32bpp-4tiledg2rcccs-upscaling@pipe-a-default-mode.html

  * igt@kms_flip_scaled_crc@flip-32bpp-4tile-to-32bpp-4tiledg2rcccs-upscaling@pipe-a-valid-mode:
    - shard-tglb:         NOTRUN -> [SKIP][65] ([i915#2587] / [i915#2672]) +1 similar issue
   [65]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb7/igt@kms_flip_scaled_crc@flip-32bpp-4tile-to-32bpp-4tiledg2rcccs-upscaling@pipe-a-valid-mode.html

  * igt@kms_flip_scaled_crc@flip-64bpp-yftile-to-16bpp-yftile-downscaling@pipe-a-valid-mode:
    - shard-iclb:         NOTRUN -> [SKIP][66] ([i915#2587] / [i915#2672]) +2 similar issues
   [66]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb1/igt@kms_flip_scaled_crc@flip-64bpp-yftile-to-16bpp-yftile-downscaling@pipe-a-valid-mode.html

  * igt@kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilercccs-downscaling@pipe-a-default-mode:
    - shard-iclb:         NOTRUN -> [SKIP][67] ([i915#2672] / [i915#3555])
   [67]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb2/igt@kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilercccs-downscaling@pipe-a-default-mode.html

  * igt@kms_frontbuffer_tracking@fbc-2p-scndscrn-spr-indfb-draw-blt:
    - shard-tglb:         NOTRUN -> [SKIP][68] ([fdo#109280] / [fdo#111825]) +17 similar issues
   [68]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb6/igt@kms_frontbuffer_tracking@fbc-2p-scndscrn-spr-indfb-draw-blt.html

  * igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-shrfb-pgflip-blt:
    - shard-tglb:         NOTRUN -> [SKIP][69] ([i915#6497]) +4 similar issues
   [69]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb6/igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-shrfb-pgflip-blt.html

  * igt@kms_frontbuffer_tracking@fbcpsr-2p-primscrn-indfb-msflip-blt:
    - shard-iclb:         NOTRUN -> [SKIP][70] ([fdo#109280]) +11 similar issues
   [70]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb7/igt@kms_frontbuffer_tracking@fbcpsr-2p-primscrn-indfb-msflip-blt.html

  * igt@kms_frontbuffer_tracking@psr-2p-primscrn-pri-shrfb-draw-mmap-wc:
    - shard-glk:          NOTRUN -> [SKIP][71] ([fdo#109271]) +58 similar issues
   [71]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-glk8/igt@kms_frontbuffer_tracking@psr-2p-primscrn-pri-shrfb-draw-mmap-wc.html

  * igt@kms_pipe_b_c_ivb@from-pipe-c-to-b-with-3-lanes:
    - shard-iclb:         NOTRUN -> [SKIP][72] ([fdo#109289])
   [72]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb8/igt@kms_pipe_b_c_ivb@from-pipe-c-to-b-with-3-lanes.html

  * igt@kms_plane_lowres@tiling-x@pipe-c-edp-1:
    - shard-tglb:         NOTRUN -> [SKIP][73] ([i915#3536]) +3 similar issues
   [73]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb8/igt@kms_plane_lowres@tiling-x@pipe-c-edp-1.html

  * igt@kms_plane_scaling@plane-scaler-with-rotation-unity-scaling@pipe-b-edp-1:
    - shard-tglb:         NOTRUN -> [SKIP][74] ([i915#5176]) +3 similar issues
   [74]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb3/igt@kms_plane_scaling@plane-scaler-with-rotation-unity-scaling@pipe-b-edp-1.html

  * igt@kms_plane_scaling@planes-downscale-factor-0-5@pipe-a-edp-1:
    - shard-iclb:         [PASS][75] -> [SKIP][76] ([i915#5235]) +2 similar issues
   [75]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-iclb5/igt@kms_plane_scaling@planes-downscale-factor-0-5@pipe-a-edp-1.html
   [76]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb2/igt@kms_plane_scaling@planes-downscale-factor-0-5@pipe-a-edp-1.html

  * igt@kms_psr2_sf@cursor-plane-move-continuous-sf:
    - shard-tglb:         NOTRUN -> [SKIP][77] ([i915#2920]) +1 similar issue
   [77]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb2/igt@kms_psr2_sf@cursor-plane-move-continuous-sf.html

  * igt@kms_psr2_su@frontbuffer-xrgb8888:
    - shard-iclb:         NOTRUN -> [SKIP][78] ([fdo#109642] / [fdo#111068] / [i915#658]) +1 similar issue
   [78]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb3/igt@kms_psr2_su@frontbuffer-xrgb8888.html

  * igt@kms_psr2_su@page_flip-p010@pipe-b-edp-1:
    - shard-iclb:         NOTRUN -> [FAIL][79] ([i915#5939]) +2 similar issues
   [79]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb2/igt@kms_psr2_su@page_flip-p010@pipe-b-edp-1.html

  * igt@kms_psr@psr2_primary_blt:
    - shard-tglb:         NOTRUN -> [FAIL][80] ([i915#132] / [i915#3467]) +1 similar issue
   [80]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb1/igt@kms_psr@psr2_primary_blt.html
    - shard-iclb:         NOTRUN -> [SKIP][81] ([fdo#109441])
   [81]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb8/igt@kms_psr@psr2_primary_blt.html

  * igt@kms_psr@psr2_primary_mmap_cpu:
    - shard-iclb:         [PASS][82] -> [SKIP][83] ([fdo#109441]) +1 similar issue
   [82]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-iclb2/igt@kms_psr@psr2_primary_mmap_cpu.html
   [83]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb3/igt@kms_psr@psr2_primary_mmap_cpu.html

  * igt@kms_psr_stress_test@invalidate-primary-flip-overlay:
    - shard-iclb:         [PASS][84] -> [SKIP][85] ([i915#5519])
   [84]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-iclb3/igt@kms_psr_stress_test@invalidate-primary-flip-overlay.html
   [85]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb7/igt@kms_psr_stress_test@invalidate-primary-flip-overlay.html

  * igt@kms_rotation_crc@primary-4-tiled-reflect-x-180:
    - shard-iclb:         NOTRUN -> [SKIP][86] ([i915#5289])
   [86]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb8/igt@kms_rotation_crc@primary-4-tiled-reflect-x-180.html
    - shard-tglb:         NOTRUN -> [SKIP][87] ([i915#5289])
   [87]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb7/igt@kms_rotation_crc@primary-4-tiled-reflect-x-180.html

  * igt@kms_setmode@clone-exclusive-crtc:
    - shard-tglb:         NOTRUN -> [SKIP][88] ([i915#3555]) +3 similar issues
   [88]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb7/igt@kms_setmode@clone-exclusive-crtc.html

  * igt@perf@unprivileged-single-ctx-counters:
    - shard-tglb:         NOTRUN -> [SKIP][89] ([fdo#109289]) +2 similar issues
   [89]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb3/igt@perf@unprivileged-single-ctx-counters.html

  * igt@prime_vgem@fence-write-hang:
    - shard-tglb:         NOTRUN -> [SKIP][90] ([fdo#109295])
   [90]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb8/igt@prime_vgem@fence-write-hang.html

  * igt@sysfs_clients@sema-25:
    - shard-apl:          NOTRUN -> [SKIP][91] ([fdo#109271] / [i915#2994])
   [91]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-apl7/igt@sysfs_clients@sema-25.html

  * igt@sysfs_clients@sema-50:
    - shard-tglb:         NOTRUN -> [SKIP][92] ([i915#2994])
   [92]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb7/igt@sysfs_clients@sema-50.html

  
#### Possible fixes ####

  * igt@gem_exec_balancer@parallel:
    - shard-iclb:         [SKIP][93] ([i915#4525]) -> [PASS][94] +1 similar issue
   [93]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-iclb8/igt@gem_exec_balancer@parallel.html
   [94]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb2/igt@gem_exec_balancer@parallel.html

  * igt@gem_exec_fair@basic-none@vcs0:
    - shard-glk:          [FAIL][95] ([i915#2842]) -> [PASS][96]
   [95]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-glk8/igt@gem_exec_fair@basic-none@vcs0.html
   [96]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-glk3/igt@gem_exec_fair@basic-none@vcs0.html

  * igt@gen9_exec_parse@allowed-single:
    - shard-apl:          [DMESG-WARN][97] ([i915#5566] / [i915#716]) -> [PASS][98]
   [97]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-apl3/igt@gen9_exec_parse@allowed-single.html
   [98]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-apl6/igt@gen9_exec_parse@allowed-single.html
    - shard-glk:          [DMESG-WARN][99] ([i915#5566] / [i915#716]) -> [PASS][100]
   [99]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-glk6/igt@gen9_exec_parse@allowed-single.html
   [100]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-glk7/igt@gen9_exec_parse@allowed-single.html

  * igt@kms_big_fb@linear-16bpp-rotate-180:
    - shard-glk:          [DMESG-FAIL][101] ([i915#118] / [i915#5138]) -> [PASS][102]
   [101]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-glk6/igt@kms_big_fb@linear-16bpp-rotate-180.html
   [102]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-glk3/igt@kms_big_fb@linear-16bpp-rotate-180.html

  * igt@kms_busy@basic@modeset:
    - shard-tglb:         [INCOMPLETE][103] -> [PASS][104]
   [103]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-tglb8/igt@kms_busy@basic@modeset.html
   [104]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb2/igt@kms_busy@basic@modeset.html

  * igt@kms_cursor_crc@cursor-suspend@pipe-b-dp-1:
    - shard-apl:          [DMESG-WARN][105] ([i915#180]) -> [PASS][106]
   [105]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-apl3/igt@kms_cursor_crc@cursor-suspend@pipe-b-dp-1.html
   [106]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-apl8/igt@kms_cursor_crc@cursor-suspend@pipe-b-dp-1.html

  * igt@kms_cursor_legacy@flip-vs-cursor@atomic-transitions:
    - shard-glk:          [FAIL][107] ([i915#2346]) -> [PASS][108]
   [107]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-glk8/igt@kms_cursor_legacy@flip-vs-cursor@atomic-transitions.html
   [108]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-glk6/igt@kms_cursor_legacy@flip-vs-cursor@atomic-transitions.html

  * igt@kms_cursor_legacy@flip-vs-cursor@atomic-transitions-varying-size:
    - shard-apl:          [FAIL][109] ([i915#2346]) -> [PASS][110]
   [109]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-apl8/igt@kms_cursor_legacy@flip-vs-cursor@atomic-transitions-varying-size.html
   [110]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-apl7/igt@kms_cursor_legacy@flip-vs-cursor@atomic-transitions-varying-size.html

  * igt@kms_flip@2x-flip-vs-absolute-wf_vblank@ab-hdmi-a1-hdmi-a2:
    - shard-glk:          [FAIL][111] ([i915#2122]) -> [PASS][112]
   [111]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-glk1/igt@kms_flip@2x-flip-vs-absolute-wf_vblank@ab-hdmi-a1-hdmi-a2.html
   [112]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-glk7/igt@kms_flip@2x-flip-vs-absolute-wf_vblank@ab-hdmi-a1-hdmi-a2.html

  * igt@kms_psr@psr2_sprite_plane_onoff:
    - shard-iclb:         [SKIP][113] ([fdo#109441]) -> [PASS][114] +4 similar issues
   [113]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-iclb1/igt@kms_psr@psr2_sprite_plane_onoff.html
   [114]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb2/igt@kms_psr@psr2_sprite_plane_onoff.html

  
#### Warnings ####

  * igt@gem_exec_balancer@parallel-ordering:
    - shard-iclb:         [SKIP][115] ([i915#4525]) -> [FAIL][116] ([i915#6117])
   [115]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-iclb6/igt@gem_exec_balancer@parallel-ordering.html
   [116]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb2/igt@gem_exec_balancer@parallel-ordering.html

  * igt@gem_pread@exhaustion:
    - shard-apl:          [INCOMPLETE][117] ([i915#7248]) -> [WARN][118] ([i915#2658])
   [117]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-apl6/igt@gem_pread@exhaustion.html
   [118]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-apl3/igt@gem_pread@exhaustion.html
    - shard-tglb:         [INCOMPLETE][119] ([i915#7248]) -> [WARN][120] ([i915#2658])
   [119]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-tglb7/igt@gem_pread@exhaustion.html
   [120]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb7/igt@gem_pread@exhaustion.html
    - shard-glk:          [INCOMPLETE][121] ([i915#7248]) -> [WARN][122] ([i915#2658])
   [121]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-glk3/igt@gem_pread@exhaustion.html
   [122]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-glk7/igt@gem_pread@exhaustion.html

  * igt@gem_pwrite@basic-exhaustion:
    - shard-tglb:         [WARN][123] ([i915#2658]) -> [INCOMPLETE][124] ([i915#7248])
   [123]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-tglb1/igt@gem_pwrite@basic-exhaustion.html
   [124]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-tglb3/igt@gem_pwrite@basic-exhaustion.html

  * igt@kms_plane_alpha_blend@alpha-basic@pipe-c-dp-1:
    - shard-apl:          [DMESG-FAIL][125] ([IGT#6]) -> [FAIL][126] ([i915#4573]) +1 similar issue
   [125]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-apl2/igt@kms_plane_alpha_blend@alpha-basic@pipe-c-dp-1.html
   [126]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-apl1/igt@kms_plane_alpha_blend@alpha-basic@pipe-c-dp-1.html

  * igt@kms_psr2_sf@primary-plane-update-sf-dmg-area:
    - shard-iclb:         [SKIP][127] ([fdo#111068] / [i915#658]) -> [SKIP][128] ([i915#2920])
   [127]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-iclb5/igt@kms_psr2_sf@primary-plane-update-sf-dmg-area.html
   [128]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-iclb2/igt@kms_psr2_sf@primary-plane-update-sf-dmg-area.html

  * igt@runner@aborted:
    - shard-apl:          ([FAIL][129], [FAIL][130], [FAIL][131], [FAIL][132]) ([fdo#109271] / [i915#3002] / [i915#4312]) -> ([FAIL][133], [FAIL][134], [FAIL][135]) ([i915#180] / [i915#3002] / [i915#4312])
   [129]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-apl8/igt@runner@aborted.html
   [130]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-apl3/igt@runner@aborted.html
   [131]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-apl3/igt@runner@aborted.html
   [132]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12392/shard-apl3/igt@runner@aborted.html
   [133]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-apl7/igt@runner@aborted.html
   [134]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-apl6/igt@runner@aborted.html
   [135]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/shard-apl1/igt@runner@aborted.html

  
  [IGT#6]: https://gitlab.freedesktop.org/drm/igt-gpu-tools/issues/6
  [fdo#109271]: https://bugs.freedesktop.org/show_bug.cgi?id=109271
  [fdo#109274]: https://bugs.freedesktop.org/show_bug.cgi?id=109274
  [fdo#109278]: https://bugs.freedesktop.org/show_bug.cgi?id=109278
  [fdo#109280]: https://bugs.freedesktop.org/show_bug.cgi?id=109280
  [fdo#109284]: https://bugs.freedesktop.org/show_bug.cgi?id=109284
  [fdo#109289]: https://bugs.freedesktop.org/show_bug.cgi?id=109289
  [fdo#109295]: https://bugs.freedesktop.org/show_bug.cgi?id=109295
  [fdo#109302]: https://bugs.freedesktop.org/show_bug.cgi?id=109302
  [fdo#109441]: https://bugs.freedesktop.org/show_bug.cgi?id=109441
  [fdo#109642]: https://bugs.freedesktop.org/show_bug.cgi?id=109642
  [fdo#110723]: https://bugs.freedesktop.org/show_bug.cgi?id=110723
  [fdo#110725]: https://bugs.freedesktop.org/show_bug.cgi?id=110725
  [fdo#111068]: https://bugs.freedesktop.org/show_bug.cgi?id=111068
  [fdo#111614]: https://bugs.freedesktop.org/show_bug.cgi?id=111614
  [fdo#111615]: https://bugs.freedesktop.org/show_bug.cgi?id=111615
  [fdo#111825]: https://bugs.freedesktop.org/show_bug.cgi?id=111825
  [fdo#111827]: https://bugs.freedesktop.org/show_bug.cgi?id=111827
  [i915#118]: https://gitlab.freedesktop.org/drm/intel/issues/118
  [i915#132]: https://gitlab.freedesktop.org/drm/intel/issues/132
  [i915#180]: https://gitlab.freedesktop.org/drm/intel/issues/180
  [i915#2122]: https://gitlab.freedesktop.org/drm/intel/issues/2122
  [i915#2346]: https://gitlab.freedesktop.org/drm/intel/issues/2346
  [i915#2527]: https://gitlab.freedesktop.org/drm/intel/issues/2527
  [i915#2587]: https://gitlab.freedesktop.org/drm/intel/issues/2587
  [i915#2658]: https://gitlab.freedesktop.org/drm/intel/issues/2658
  [i915#2672]: https://gitlab.freedesktop.org/drm/intel/issues/2672
  [i915#2681]: https://gitlab.freedesktop.org/drm/intel/issues/2681
  [i915#2724]: https://gitlab.freedesktop.org/drm/intel/issues/2724
  [i915#2842]: https://gitlab.freedesktop.org/drm/intel/issues/2842
  [i915#2856]: https://gitlab.freedesktop.org/drm/intel/issues/2856
  [i915#2920]: https://gitlab.freedesktop.org/drm/intel/issues/2920
  [i915#2994]: https://gitlab.freedesktop.org/drm/intel/issues/2994
  [i915#3002]: https://gitlab.freedesktop.org/drm/intel/issues/3002
  [i915#3063]: https://gitlab.freedesktop.org/drm/intel/issues/3063
  [i915#3318]: https://gitlab.freedesktop.org/drm/intel/issues/3318
  [i915#3467]: https://gitlab.freedesktop.org/drm/intel/issues/3467
  [i915#3536]: https://gitlab.freedesktop.org/drm/intel/issues/3536
  [i915#3555]: https://gitlab.freedesktop.org/drm/intel/issues/3555
  [i915#3637]: https://gitlab.freedesktop.org/drm/intel/issues/3637
  [i915#3689]: https://gitlab.freedesktop.org/drm/intel/issues/3689
  [i915#3886]: https://gitlab.freedesktop.org/drm/intel/issues/3886
  [i915#3989]: https://gitlab.freedesktop.org/drm/intel/issues/3989
  [i915#4270]: https://gitlab.freedesktop.org/drm/intel/issues/4270
  [i915#4281]: https://gitlab.freedesktop.org/drm/intel/issues/4281
  [i915#4312]: https://gitlab.freedesktop.org/drm/intel/issues/4312
  [i915#4525]: https://gitlab.freedesktop.org/drm/intel/issues/4525
  [i915#454]: https://gitlab.freedesktop.org/drm/intel/issues/454
  [i915#4573]: https://gitlab.freedesktop.org/drm/intel/issues/4573
  [i915#4613]: https://gitlab.freedesktop.org/drm/intel/issues/4613
  [i915#5072]: https://gitlab.freedesktop.org/drm/intel/issues/5072
  [i915#5138]: https://gitlab.freedesktop.org/drm/intel/issues/5138
  [i915#5176]: https://gitlab.freedesktop.org/drm/intel/issues/5176
  [i915#5235]: https://gitlab.freedesktop.org/drm/intel/issues/5235
  [i915#5286]: https://gitlab.freedesktop.org/drm/intel/issues/5286
  [i915#5289]: https://gitlab.freedesktop.org/drm/intel/issues/5289
  [i915#5519]: https://gitlab.freedesktop.org/drm/intel/issues/5519
  [i915#5566]: https://gitlab.freedesktop.org/drm/intel/issues/5566
  [i915#5939]: https://gitlab.freedesktop.org/drm/intel/issues/5939
  [i915#6095]: https://gitlab.freedesktop.org/drm/intel/issues/6095
  [i915#6117]: https://gitlab.freedesktop.org/drm/intel/issues/6117
  [i915#6497]: https://gitlab.freedesktop.org/drm/intel/issues/6497
  [i915#658]: https://gitlab.freedesktop.org/drm/intel/issues/658
  [i915#7118]: https://gitlab.freedesktop.org/drm/intel/issues/7118
  [i915#716]: https://gitlab.freedesktop.org/drm/intel/issues/716
  [i915#7173]: https://gitlab.freedesktop.org/drm/intel/issues/7173
  [i915#7248]: https://gitlab.freedesktop.org/drm/intel/issues/7248


Build changes
-------------

  * CI: CI-20190529 -> None
  * IGT: IGT_7064 -> IGTPW_8124
  * Piglit: piglit_4509 -> None

  CI-20190529: 20190529
  CI_DRM_12392: b837a6bcf4fb8432fa1c21d8b7fa707b128d6bfd @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_8124: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/index.html
  IGT_7064: f5cc3abb3d99e9ab5da2285b184e045da95c35a2 @ https://gitlab.freedesktop.org/drm/igt-gpu-tools.git
  piglit_4509: fdc5a4ca11124ab8413c7988896eec4c97336694 @ git://anongit.freedesktop.org/piglit

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8124/index.html

[-- Attachment #2: Type: text/html, Size: 43051 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [igt-dev] [PATCH i-g-t] Use the new procps library libproc2
  2022-11-17 11:17 ` [igt-dev] [PATCH i-g-t] " Kamil Konieczny
  2022-11-17 11:29   ` Petri Latvala
@ 2022-11-17 21:13   ` Craig Small
  1 sibling, 0 replies; 11+ messages in thread
From: Craig Small @ 2022-11-17 21:13 UTC (permalink / raw)
  To: Kamil Konieczny, igt-dev, Petri Latvala, Craig Small

[-- Attachment #1: Type: text/plain, Size: 4377 bytes --]

On Thu, 17 Nov 2022 at 22:17, Kamil Konieczny <
kamil.konieczny@linux.intel.com> wrote:

> Hi Craig,
>
> On 2022-11-17 at 10:41:29 +0200, Petri Latvala wrote:
> > +#ifdef HAVE_LIBPROCPS
> >  #include <proc/readproc.h>
> > +#endif
> > +#ifdef HAVE_LIBPROC2
> -- ^
> Maybe just #elif ?
>
That's probably better, you don't want both to happen.


> -- ^^^^^^^^^^^^^^^^^^
> > +#include <dirent.h>
> -- ^^^^^^^^^^^^^^^^^^
> What this has with libproc2 ?
> Note also that FreeBSD have its own limits.h
>
I'm not sure why that would be there, the library handles the directory
enumeration.


>
> > +         if (!strncasecmp(pid_comm, comm, strlen(pid_comm))) {
>
> Processes AA and aa are different, why do you use strncasecmp ?
>
I agree, but the original uses strncasecmp. They both should use strncmp
but that's beyond "making it work with libproc2".
Otherwise linking with libprocps is case-insenstive and linking with
libproc2 is case sensitive.


>
> > @@ -1524,10 +1616,13 @@ static void pipewire_reserve_wait(void)
> >       char xdg_dir[PATH_MAX];
> >       const char *homedir;
> >       struct passwd *pw;
> > -     proc_t *proc_info;
> > -     PROCTAB *proc;
> > +     int tid=0, euid, egid;
> >
> > +#ifdef HAVE_LIBPROCPS
> >       igt_fork_helper(&pw_reserve_proc) {
> > +             proc_t *proc_info;
> > +             PROCTAB *proc;
> > +
> >               igt_info("Preventing pipewire-pulse to use the audio
> drivers\n");
> >
> >               proc = openproc(PROC_FILLCOM | PROC_FILLSTAT |
> PROC_FILLARG);
> > @@ -1540,19 +1635,44 @@ static void pipewire_reserve_wait(void)
> >               }
> >               closeproc(proc);
> >
> > +             tid = proc_info->tid;
> > +             euid = proc_info->euid;
> > +             egid = proc_info->egid;
> > +             freeproc(proc_info);
> > +#endif
> > +#ifdef HAVE_LIBPROC2
> > +     igt_fork(child, 1) {
>
> This should be fork_helper like above.
>
Did something change here recently? My version of pipewire_reserve_wait()
has igt_fork()
However, whatever the current version is doing they should be the same.


>
> > +             enum pids_item Items[] = { PIDS_ID_PID, PIDS_ID_EUID,
> PIDS_ID_EGID };
> > +             enum rel_items { EU_PID, EU_EUID, EU_EGID };
> > +             struct pids_info *info = NULL;
> > +             struct pids_stack *stack;
> > +
> > +             igt_info("Preventing pipewire-pulse to use the audio
> drivers\n");
> > +
> > +             if (procps_pids_new(&info, Items, 3) < 0)
> > +                 return;
> ------------------- ^
> Use exit(0) here plus maybe some ing_info why it failed ?
>
Ah yes, because at this point you're running as a child process.
The error message we (procps) use for this is:
"Unable to create pid info structure"
So this would be
{
  igt_info("Unable to create pid info structure");
  exit(0);
}

> @@ -1584,6 +1702,10 @@ int pipewire_pulse_start_reserve(void)
> >        * pipewire version 0.3.50 or upper.
> >        */
> >       for (attempts = 0; attempts < PIPEWIRE_RESERVE_MAX_TIME;
> attempts++) {
> > +#ifdef HAVE_LIBPROCPS
> > +             PROCTAB *proc;
> > +             proc_t *proc_info;
> > +
> >               usleep(1000);
> >               proc = openproc(PROC_FILLCOM | PROC_FILLSTAT |
> PROC_FILLARG);
> >               igt_assert(proc != NULL);
> > @@ -1598,6 +1720,25 @@ int pipewire_pulse_start_reserve(void)
> >                       freeproc(proc_info);
> >               }
> >               closeproc(proc);
> > +#endif
> > +#ifdef HAVE_LIBPROC2
> > +             enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD };
> > +             struct pids_info *info = NULL;
> > +             struct pids_stack *stack;
> > +
> > +             usleep(1000);
> > +
> > +             if (procps_pids_new(&info, Items, 2) < 0)
> > +                     return 1;
> ----------------------- ^
> break or igt_assert_f(errno == 0, "Getting procps failed\n"); here.
>
If procps_pids_new() returns a negative number then getting the info
structure has failed.
This is very similar to igt_assert(proc != NULL) above in the same function.
I'm not sure of your coding style, should you assert that procps_pids_new
returns >= 0? or use the branch?

Thankyou for your review on this patch. The sole goal of this patch is so
igt does the same thing with libprocps and libproc2 so I've tried to keep
the same behavior.

  - Craig

[-- Attachment #2: Type: text/html, Size: 6528 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [igt-dev] PATCH i-g-t Use the new procps library libproc2
  2022-11-17  8:23 ` Petri Latvala
@ 2022-11-17  8:54   ` Craig Small
  0 siblings, 0 replies; 11+ messages in thread
From: Craig Small @ 2022-11-17  8:54 UTC (permalink / raw)
  To: Petri Latvala; +Cc: Development mailing list for IGT GPU Tools

[-- Attachment #1: Type: text/plain, Size: 849 bytes --]

On Thu, 17 Nov 2022 at 19:23, Petri Latvala <petri.latvala@intel.com> wrote:

> What is the current distro availability status of libproc2? I can't
> find it at all in Ubuntu, it seems to be in experimental repo only in
> Debian, and I have no idea about the others.
>
Not sure, its a bit wait and see for other distributions. I think one of
the SuSE based ones had it.

The program will have to test for both libprocps and libproc2. I have
purposely put libproc2 into Debian experimental because otherwise the bug
reports for all its dependent projects would be raised higher and any
updates would not pass.
I'm giving those projects time to get the patches into either the Debian
package or, preferably upstream.

After that time then libproc2 will be in Sid and then no more updates of
dependent projects should happen until its fixed.

 - Craig

[-- Attachment #2: Type: text/html, Size: 1280 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [igt-dev] PATCH i-g-t Use the new procps library libproc2
  2022-11-17  6:03 [igt-dev] PATCH i-g-t " Craig Small
@ 2022-11-17  8:23 ` Petri Latvala
  2022-11-17  8:54   ` Craig Small
  0 siblings, 1 reply; 11+ messages in thread
From: Petri Latvala @ 2022-11-17  8:23 UTC (permalink / raw)
  To: Craig Small; +Cc: Development mailing list for IGT GPU Tools

On Thu, Nov 17, 2022 at 05:03:57PM +1100, Craig Small wrote:
> Hi,
>   Attached is a patch for i-g-t so that it can use the old libprocps or new
> libproc2 library. I have had two people test it at [1] and said it was ok,
> besides myself compiling it.

Hi, thanks for the patch! I'll send that again later to the mailing
list in a form that patchwork recognizes.

What is the current distro availability status of libproc2? I can't
find it at all in Ubuntu, it seems to be in experimental repo only in
Debian, and I have no idea about the others.


-- 
Petri Latvala

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [igt-dev] PATCH i-g-t Use the new procps library libproc2
@ 2022-11-17  6:03 Craig Small
  2022-11-17  8:23 ` Petri Latvala
  0 siblings, 1 reply; 11+ messages in thread
From: Craig Small @ 2022-11-17  6:03 UTC (permalink / raw)
  To: Development mailing list for IGT GPU Tools


[-- Attachment #1.1: Type: text/plain, Size: 510 bytes --]

Hi,
  Attached is a patch for i-g-t so that it can use the old libprocps or new
libproc2 library. I have had two people test it at [1] and said it was ok,
besides myself compiling it.

 - Craig

1: https://gitlab.com/procps-ng/procps/-/issues/239#note_1146187466

-- 

Craig Small             https://dropbear.xyz/  csmall at : dropbear.xyz
Debian GNU/Linux        https://www.debian.org/
<http://www.debian.org/>  csmall at : debian.org
GPG fingerprint:     5D2F B320 B825 D939 04D2  0519 3938 F96B DF50 FEA5

[-- Attachment #1.2: Type: text/html, Size: 987 bytes --]

[-- Attachment #2: libproc2_library --]
[-- Type: application/octet-stream, Size: 13081 bytes --]

--- a/lib/igt_aux.c
+++ b/lib/igt_aux.c
@@ -52,8 +52,16 @@
 #include <assert.h>
 #include <grp.h>
 
+#ifdef HAVE_LIBPROCPS
 #include <proc/readproc.h>
+#endif
+#ifdef HAVE_LIBPROC2
+#include <libproc2/pids.h>
+#endif
+
 #include <libudev.h>
+#include <linux/limits.h>
+#include <dirent.h>
 
 #include "drmtest.h"
 #include "i915_drm.h"
@@ -1190,6 +1198,7 @@
  */
 int igt_is_process_running(const char *comm)
 {
+#if HAVE_LIBPROCPS
 	PROCTAB *proc;
 	proc_t *proc_info;
 	bool found = false;
@@ -1208,6 +1217,26 @@
 
 	closeproc(proc);
 	return found;
+#endif
+#if HAVE_LIBPROC2
+	enum pids_item Item[] = { PIDS_CMD };
+	struct pids_info *info = NULL;
+	struct pids_stack *stack;
+	char *pid_comm;
+	bool found = false;
+
+	if (procps_pids_new(&info, Item, 1) < 0)
+	    return false;
+	while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
+	    pid_comm = PIDS_VAL(0, str, stack, info);
+	    if (!strncasecmp(pid_comm, comm, strlen(pid_comm))) {
+		found = true;
+		break;
+	    }
+	}
+	procps_pids_unref(&info);
+	return found;
+#endif
 }
 
 /**
@@ -1224,6 +1253,7 @@
  */
 int igt_terminate_process(int sig, const char *comm)
 {
+#if HAVE_LIBPROCPS
 	PROCTAB *proc;
 	proc_t *proc_info;
 	int err = 0;
@@ -1245,6 +1275,29 @@
 
 	closeproc(proc);
 	return err;
+#endif
+#if HAVE_LIBPROC2
+	enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD };
+	struct pids_info *info = NULL;
+	struct pids_stack *stack;
+	char *pid_comm;
+	int pid;
+	int err = 0;
+
+	if (procps_pids_new(&info, Items, 2) < 0)
+		return -errno;
+	while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
+		pid = PIDS_VAL(0, s_int, stack, info);
+		pid_comm = PIDS_VAL(1, str, stack, info);
+		if (!strncasecmp(pid_comm, comm, strlen(pid_comm))) {
+			if (kill(pid, sig) < 0)
+				err = -errno;
+			break;
+		}
+	}
+	procps_pids_unref(&info);
+	return err;
+#endif
 }
 
 struct pinfo {
@@ -1314,9 +1367,9 @@
 }
 
 static void
-igt_show_stat(proc_t *info, int *state, const char *fn)
+igt_show_stat(const pid_t tid, const char *cmd, int *state, const char *fn)
 {
-	struct pinfo p = { .pid = info->tid, .comm = info->cmd, .fn = fn };
+	struct pinfo p = { .pid = tid, .comm = cmd, .fn = fn };
 
 	if (!*state)
 		igt_show_stat_header();
@@ -1326,7 +1379,7 @@
 }
 
 static void
-__igt_lsof_fds(proc_t *proc_info, int *state, char *proc_path, const char *dir)
+__igt_lsof_fds(const pid_t tid, const char *cmd, int *state, char *proc_path, const char *dir)
 {
 	struct dirent *d;
 	struct stat st;
@@ -1373,7 +1426,7 @@
 		dirn = dirname(copy_fd_lnk);
 
 		if (!strncmp(dir, dirn, strlen(dir)))
-			igt_show_stat(proc_info, state, fd_lnk);
+			igt_show_stat(tid, cmd, state, fd_lnk);
 
 		free(copy_fd_lnk);
 		free(fd_lnk);
@@ -1389,13 +1442,14 @@
 static void
 __igt_lsof(const char *dir)
 {
-	PROCTAB *proc;
-	proc_t *proc_info;
-
 	char path[30];
 	char *name_lnk;
 	struct stat st;
 	int state = 0;
+#ifdef HAVE_LIBPROCPS
+	PROCTAB *proc;
+	proc_t *proc_info;
+
 
 	proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
 	igt_assert(proc != NULL);
@@ -1416,19 +1470,57 @@
 		name_lnk[read] = '\0';
 
 		if (!strncmp(dir, name_lnk, strlen(dir)))
-			igt_show_stat(proc_info, &state, name_lnk);
+			igt_show_stat(proc_info->tid, proc_info->cmd, &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);
+		__igt_lsof_fds(proc_info->tid, proc_info->cmd, &state, path, dir);
 
 		free(name_lnk);
 		freeproc(proc_info);
 	}
 
 	closeproc(proc);
+#endif
+#ifdef HAVE_LIBPROC2
+	enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD };
+	struct pids_info *info = NULL;
+	struct pids_stack *stack;
+
+	if (procps_pids_new(&info, Items, 2) < 0)
+		return;
+	while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
+		ssize_t read;
+		int tid = PIDS_VAL(0, s_int, stack, info);
+		char *pid_comm = PIDS_VAL(1, str, stack, info);
+
+		/* check current working directory */
+		memset(path, 0, sizeof(path));
+		snprintf(path, sizeof(path), "/proc/%d/cwd", 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(tid, pid_comm, &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", tid);
+
+		__igt_lsof_fds(tid, pid_comm, &state, path, dir);
+
+		free(name_lnk);
+	}
+	procps_pids_unref(&info);
+#endif
 }
 
 /**
@@ -1463,21 +1555,21 @@
 	free(sanitized);
 }
 
-static void pulseaudio_unload_module(proc_t *proc_info)
+static void pulseaudio_unload_module(const uid_t euid, const gid_t egid)
 {
 	char xdg_dir[PATH_MAX];
 	const char *homedir;
 	struct passwd *pw;
 
 	igt_fork(child, 1) {
-		pw = getpwuid(proc_info->euid);
+		pw = getpwuid(euid);
 		homedir = pw->pw_dir;
-		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", proc_info->euid);
+		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", euid);
 
 		igt_info("Request pulseaudio to stop using audio device\n");
 
-		setgid(proc_info->egid);
-		setuid(proc_info->euid);
+		setgid(egid);
+		setuid(euid);
 		clearenv();
 		setenv("HOME", homedir, 1);
 		setenv("XDG_RUNTIME_DIR",xdg_dir, 1);
@@ -1495,10 +1587,13 @@
 	char xdg_dir[PATH_MAX];
 	const char *homedir;
 	struct passwd *pw;
-	proc_t *proc_info;
-	PROCTAB *proc;
+	int tid=0, euid, egid;
 
+#ifdef HAVE_LIBPROCPS
 	igt_fork(child, 1) {
+		proc_t *proc_info;
+		PROCTAB *proc;
+
 		igt_info("Preventing pipewire-pulse to use the audio drivers\n");
 
 		proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
@@ -1511,19 +1606,44 @@
 		}
 		closeproc(proc);
 
+		tid = proc_info->tid;
+		euid = proc_info->euid;
+		egid = proc_info->egid;
+		freeproc(proc_info);
+#endif
+#ifdef HAVE_LIBPROC2
+	igt_fork(child, 1) {
+		enum pids_item Items[] = { PIDS_ID_PID, PIDS_ID_EUID, PIDS_ID_EGID };
+		enum rel_items { EU_PID, EU_EUID, EU_EGID };
+		struct pids_info *info = NULL;
+		struct pids_stack *stack;
+
+		igt_info("Preventing pipewire-pulse to use the audio drivers\n");
+
+		if (procps_pids_new(&info, Items, 3) < 0)
+		    return;
+		while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
+			tid = PIDS_VAL(EU_PID, s_int, stack, info);
+			if (pipewire_pulse_pid == tid)
+				break;
+		}
+		euid = PIDS_VAL(EU_EUID, s_int, stack, info);
+		egid = PIDS_VAL(EU_EGID, s_int, stack, info);
+		procps_pids_unref(&info);
+#endif
+
 		/* Sanity check: if it can't find the process, it means it has gone */
-		if (pipewire_pulse_pid != proc_info->tid)
+		if (pipewire_pulse_pid != tid)
 			exit(0);
 
-		pw = getpwuid(proc_info->euid);
+		pw = getpwuid(euid);
 		homedir = pw->pw_dir;
-		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", proc_info->euid);
-		setgid(proc_info->egid);
-		setuid(proc_info->euid);
+		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", euid);
+		setgid(egid);
+		setuid(euid);
 		clearenv();
 		setenv("HOME", homedir, 1);
 		setenv("XDG_RUNTIME_DIR",xdg_dir, 1);
-		freeproc(proc_info);
 
 		/*
 		 * pw-reserve will run in background. It will only exit when
@@ -1541,9 +1661,7 @@
 int pipewire_pulse_start_reserve(void)
 {
 	bool is_pw_reserve_running = false;
-	proc_t *proc_info;
 	int attempts = 0;
-	PROCTAB *proc;
 
 	if (!pipewire_pulse_pid)
 		return 0;
@@ -1555,8 +1673,11 @@
 	 * pipewire version 0.3.50 or upper.
 	 */
 	for (attempts = 0; attempts < PIPEWIRE_RESERVE_MAX_TIME; attempts++) {
+#ifdef HAVE_LIBPROCPS
+		proc_t *proc_info;
+
 		usleep(1000);
-		proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
+		PROCTAB *proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
 		igt_assert(proc != NULL);
 
 		while ((proc_info = readproc(proc, NULL))) {
@@ -1569,6 +1690,25 @@
 			freeproc(proc_info);
 		}
 		closeproc(proc);
+#endif
+#ifdef HAVE_LIBPROC2
+		enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD };
+		struct pids_info *info = NULL;
+		struct pids_stack *stack;
+
+		usleep(1000);
+
+		if (procps_pids_new(&info, Items, 2) < 0)
+			return 1;
+		while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
+			if (!strcmp(PIDS_VAL(1, str, stack, info), "pw-reserve")) {
+				is_pw_reserve_running = true;
+				pipewire_pw_reserve_pid = PIDS_VAL(0, s_int, stack, info);
+				break;
+			}
+		}
+		procps_pids_unref(&info);
+#endif
 		if (is_pw_reserve_running)
 			break;
 	}
@@ -1616,7 +1756,7 @@
  * 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)
+__igt_lsof_audio_and_kill_proc(const pid_t tid, const char *cmd, const uid_t euid, const gid_t egid, char *proc_path)
 {
 	const char *audio_dev = "/dev/snd/";
 	char path[PATH_MAX * 2];
@@ -1641,10 +1781,10 @@
 	 * 2) unload/unbind the the audio driver(s);
 	 * 3) stop the pw-reserve thread.
 	 */
-	if (!strcmp(proc_info->cmd, "pipewire-pulse")) {
+	if (!strcmp(cmd, "pipewire-pulse")) {
 		igt_info("process %d (%s) is using audio device. Should be requested to stop using them.\n",
-			 proc_info->tid, proc_info->cmd);
-		pipewire_pulse_pid = proc_info->tid;
+			 tid, cmd);
+		pipewire_pulse_pid = tid;
 		return 0;
 	}
 	/*
@@ -1656,9 +1796,9 @@
 	 * will respawn them. So, just ignore here, they'll honor pw-reserve,
 	 * when the time comes.
 	 */
-	if (!strcmp(proc_info->cmd, "pipewire-media-session"))
+	if (!strcmp(cmd, "pipewire-media-session"))
 		return 0;
-	if (!strcmp(proc_info->cmd, "wireplumber"))
+	if (!strcmp(cmd, "wireplumber"))
 		return 0;
 
 	dp = opendir(proc_path);
@@ -1692,22 +1832,22 @@
 		 * 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);
+		if (!strcmp(cmd, "pulseaudio")) {
+			pulseaudio_unload_module(euid, egid);
 			break;
 		}
 
 		/* 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);
+				tid, cmd);
 
-		if (kill(proc_info->tid, SIGTERM) < 0) {
+		if (kill(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) {
+				cmd, tid);
+			if (kill(tid, SIGABRT) < 0) {
 				fail++;
 				igt_info("Fail to terminate %s (pid: %d) with SIGABRT\n",
-					proc_info->cmd, proc_info->tid);
+					cmd, tid);
 			}
 		}
 
@@ -1729,23 +1869,48 @@
 igt_lsof_kill_audio_processes(void)
 {
 	char path[PATH_MAX];
+	int fail = 0;
+
+#ifdef HAVE_LIBPROCPS
 	proc_t *proc_info;
 	PROCTAB *proc;
-	int fail = 0;
 
 	proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
 	igt_assert(proc != NULL);
 	pipewire_pulse_pid = 0;
-
 	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);
+			fail += __igt_lsof_audio_and_kill_proc(proc_info->tid, proc_info->cmd, proc_info->euid, proc_info->egid, path);
 
 		freeproc(proc_info);
 	}
 	closeproc(proc);
+#endif
+#ifdef HAVE_LIBPROC2
+	enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD, PIDS_ID_EUID, PIDS_ID_EGID };
+	enum rel_items { EU_PID, EU_CMD, EU_EUID, EU_EGID };
+	struct pids_info *info = NULL;
+	struct pids_stack *stack;
+	pid_t tid;
+
+	if (procps_pids_new(&info, Items, 4) < 0)
+		return 1;
+	while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
+		tid = PIDS_VAL(EU_PID, s_int, stack, info);
+
+		if (snprintf(path, sizeof(path), "/proc/%d/fd", tid) < 1)
+			fail++;
+		else
+			fail += __igt_lsof_audio_and_kill_proc(tid,
+				PIDS_VAL(EU_CMD, str, stack, info),
+				PIDS_VAL(EU_EUID, s_int, stack, info),
+				PIDS_VAL(EU_EGID, s_int, stack, info),
+				path);
+	}
+	procps_pids_unref(&info);
+#endif
 
 	return fail;
 }
--- a/meson.build
+++ b/meson.build
@@ -121,7 +121,15 @@
 
 pciaccess = dependency('pciaccess', version : '>=0.10')
 libkmod = dependency('libkmod')
-libprocps = dependency('libprocps', required : true)
+libprocps = dependency('libprocps', required : false)
+libproc2 = dependency('libproc2', required : false)
+if libprocps.found()
+  config.set('HAVE_LIBPROCPS', 1)
+elif libproc2.found()
+  config.set('HAVE_LIBPROC2', 1)
+else
+  error('Either libprocps or libproc2 is required')
+endif
 
 libunwind = dependency('libunwind', required : get_option('libunwind'))
 build_info += 'With libunwind: @0@'.format(libunwind.found())
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -97,7 +97,6 @@
 	libdrm,
 	libdw,
 	libkmod,
-	libprocps,
 	libudev,
 	math,
 	pciaccess,
@@ -148,6 +147,12 @@
 	lib_sources += 'igt_chamelium_stream.c'
 endif
 
+if libprocps.found()
+	lib_deps += libprocps
+else
+	lib_deps += libproc2
+endif
+
 if get_option('srcdir') != ''
     srcdir = join_paths(get_option('srcdir'), 'tests')
 else

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2022-11-17 21:13 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-17  8:41 [igt-dev] [PATCH i-g-t] Use the new procps library libproc2 Petri Latvala
2022-11-17  8:46 ` Petri Latvala
2022-11-17  9:05   ` Craig Small
2022-11-17 10:06 ` [igt-dev] ✓ Fi.CI.BAT: success for " Patchwork
2022-11-17 11:17 ` [igt-dev] [PATCH i-g-t] " Kamil Konieczny
2022-11-17 11:29   ` Petri Latvala
2022-11-17 21:13   ` Craig Small
2022-11-17 20:08 ` [igt-dev] ✗ Fi.CI.IGT: failure for " Patchwork
  -- strict thread matches above, loose matches on Subject: below --
2022-11-17  6:03 [igt-dev] PATCH i-g-t " Craig Small
2022-11-17  8:23 ` Petri Latvala
2022-11-17  8:54   ` Craig Small

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.