[igt-dev] [PATCH i-g-t] Use the new procps library libproc2

Petri Latvala petri.latvala at intel.com
Thu Nov 17 08:46:06 UTC 2022


On Thu, Nov 17, 2022 at 10:41:29AM +0200, Petri Latvala wrote:
> From: Craig Small <csmall at 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 at anholt.net>
> + *    Daniel Vetter <daniel.vetter at 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- at 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- at 0@'.format(hw),
> +    input : [ 'i915/perf-configs/perf-registers-codegen.py',
> +              'i915/perf-configs/oa- at 0@.xml'.format(hw) ],
> +    output : [ 'i915_perf_registers_ at 0@.c'.format(hw),
> +               'i915_perf_registers_ at 0@.h'.format(hw), ],
> +    command : [
> +      python3, '@INPUT0@',
> +      '--code', '@OUTPUT0@',
> +      '--header', '@OUTPUT1@',
> +      '--xml-file', '@INPUT1@'
> +    ])
> +  i915_perf_files += custom_target(
> +    'i915-perf-metrics- at 0@'.format(hw),
> +    input : [ 'i915/perf-configs/perf-metricset-codegen.py',
> +              'i915/perf-configs/oa- at 0@.xml'.format(hw) ],
> +    output : [ 'i915_perf_metrics_ at 0@.c'.format(hw),
> +               'i915_perf_metrics_ at 0@.h'.format(hw), ],
> +    command : [
> +      python3, '@INPUT0@',
> +      '--code', '@OUTPUT0@',
> +      '--header', '@OUTPUT1@',
> +      '--equations-include', 'i915_perf_equations.h',
> +      '--registers-include', 'i915_perf_registers_ at 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
> 


More information about the igt-dev mailing list