Igt-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Rodrigo Vivi <rodrigo.vivi@intel.com>
To: Karthik Poosa <karthik.poosa@intel.com>
Cc: <igt-dev@lists.freedesktop.org>, <anshuman.gupta@intel.com>,
	<badal.nilawar@intel.com>, <raag.jadav@intel.com>,
	<riana.tauro@intel.com>, <sk.anirban@intel.com>,
	<mallesh.koujalagi@intel.com>, <soham.purkait@intel.com>,
	<ravi.kishore.koppuravuri@intel.com>
Subject: Re: [PATCH i-g-t] tests/intel/xe_pm: Validate root-port D3cold entry on system suspend
Date: Wed, 11 Nov 2026 13:45:27 -0500	[thread overview]
Message-ID: <avS3gcuObbGGHPbT@intel.com> (raw)
In-Reply-To: <20261102095802.3419285-2-karthik.poosa@intel.com>

On Mon, Nov 02, 2026 at 03:28:02PM +0530, Karthik Poosa wrote:
> When D3cold is supported and allowed, check the root port's ACPI
> transition log across system suspend and resume for a D0 -> D3cold
> transition. Skip the transition assertion if the root port is already
> in D3cold at suspend entry.
> 
> Signed-off-by: Karthik Poosa <karthik.poosa@intel.com>
> Assisted-by: LLM
> ---
>  tests/intel/xe_pm.c | 234 +++++++++++++++++++++++++++++++++++++++++---
>  1 file changed, 221 insertions(+), 13 deletions(-)
> 
> diff --git a/tests/intel/xe_pm.c b/tests/intel/xe_pm.c
> index 543f46f5b..2eb70a383 100644
> --- a/tests/intel/xe_pm.c
> +++ b/tests/intel/xe_pm.c
> @@ -12,8 +12,10 @@
>   */
>  
>  #include <dirent.h>
> +#include <errno.h>
>  #include <limits.h>
>  #include <fcntl.h>
> +#include <stdio.h>
>  #include <string.h>
>  #include <sys/ioctl.h>
>  
> @@ -63,6 +65,9 @@ typedef struct {
>  
>  uint64_t orig_threshold;
>  int fw_handle = -1;
> +static int fd_kmsg = -1;
> +static char d3cold_kmsg_entry[512];
> +static bool acpi_d3cold_debug_enabled_by_test;
>  
>  static pthread_mutex_t suspend_lock = PTHREAD_MUTEX_INITIALIZER;
>  static pthread_cond_t suspend_cond = PTHREAD_COND_INITIALIZER;
> @@ -192,6 +197,120 @@ static void cleanup_d3(device_t device)
>  	dpms_on_off(device, DRM_MODE_DPMS_ON);
>  }
>  
> +static int count_kmsg_entries(int kmsg, const char *entry)
> +{
> +	char buf[4096];
> +	char *kmsg_entry, *p;
> +	const unsigned char *src;
> +	size_t entry_len = strlen(entry);
> +	int count = 0;
> +	ssize_t len;
> +
> +	igt_assert(entry_len <= (SIZE_MAX - 1) / 4);
> +	kmsg_entry = malloc(4 * entry_len + 1);
> +	igt_assert(kmsg_entry);
> +
> +	/* Match /dev/kmsg's escaping of backslashes and non-printable bytes. */
> +	for (src = (const unsigned char *)entry, p = kmsg_entry; *src; src++) {
> +		if (*src < ' ' || *src >= 127 || *src == '\\')
> +			p += sprintf(p, "\\x%02x", (unsigned int)*src);
> +		else
> +			*p++ = *src;
> +	}
> +	*p = '\0';
> +
> +	errno = 0;
> +	while ((len = read(kmsg, buf, sizeof(buf) - 1)) > 0) {
> +		buf[len] = '\0';
> +		if (strstr(buf, kmsg_entry)) {
> +			igt_debug("Found dmesg entry: %s\n", buf);
> +			count++;
> +		}
> +	}
> +
> +	free(kmsg_entry);
> +
> +	if (len == 0)
> +		return count;
> +
> +	igt_assert_f(len == -1 && (errno == EAGAIN || errno == EWOULDBLOCK),
> +		     "Unexpected /dev/kmsg read result len=%zd errno=%d\n", len, errno);
> +
> +	return count;
> +}
> +
> +static void build_rp_d3cold_kmsg_str(struct pci_device *pci_root, char *buf, size_t len)
> +{
> +	char path[PATH_MAX], fw_path[256];
> +	ssize_t n;
> +	int fd;
> +
> +	snprintf(path, sizeof(path),
> +		 "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/firmware_node/path",
> +		 pci_root->domain, pci_root->bus, pci_root->dev, pci_root->func);
> +
> +	fd = open(path, O_RDONLY);
> +	igt_require_fd(fd);
> +	n = read(fd, fw_path, sizeof(fw_path) - 1);
> +	igt_assert_lte(0, (int)n);
> +	fw_path[n] = '\0';
> +	if (n > 0 && fw_path[n - 1] == '\n')
> +		fw_path[n - 1] = '\0';
> +	close(fd);
> +
> +	snprintf(buf, len, "%s: PM: Power state change: D0 -> D3cold", fw_path);

I'm a bit confused by this message and the order, but I will trust
more your tests...

> +}
> +
> +#define ACPI_DYNDDBG_CONTROL "/sys/kernel/debug/dynamic_debug/control"
> +#define ACPI_D3COLD_DYNDDBG_QUERY \
> +	"file drivers/acpi/device_pm.c format \"Power state change: %s -> %s\""
> +
> +static bool acpi_d3cold_debug_is_enabled(void)
> +{
> +	char line[1024];
> +	FILE *fp;
> +	bool enabled = false;
> +
> +	fp = fopen(ACPI_DYNDDBG_CONTROL, "r");
> +	if (!fp)
> +		return false;
> +
> +	while (fgets(line, sizeof(line), fp)) {
> +		char *message = strstr(line, "Power state change: %s -> %s");
> +		char *flags = strchr(line, '=');
> +
> +		if (message && flags && flags < message && memchr(flags, 'p', message - flags)) {
> +			enabled = true;
> +			break;
> +		}
> +	}
> +
> +	fclose(fp);
> +	return enabled;
> +}
> +
> +static bool set_acpi_d3cold_debug(bool enable)
> +{
> +	FILE *fp;
> +	int ret;
> +
> +	fp = fopen(ACPI_DYNDDBG_CONTROL, "w");
> +	if (!fp)
> +		return false;
> +
> +	ret = fprintf(fp, "%s %sp\n", ACPI_D3COLD_DYNDDBG_QUERY, enable ? "+" : "-");
> +	if (fclose(fp))
> +		return false;
> +
> +	return ret > 0;
> +}
> +
> +static void restore_acpi_d3cold_debug(int sig)
> +{
> +	if (acpi_d3cold_debug_enabled_by_test)
> +		set_acpi_d3cold_debug(false);
> +}
> +
>  static bool in_d3(device_t device, enum igt_acpi_d_state state)
>  {
>  	uint16_t val;
> @@ -228,6 +347,85 @@ static void close_fw_handle(int sig)
>  	close(fw_handle);
>  }
>  
> +static void close_d3cold_kmsg_handle(int sig)
> +{
> +	if (fd_kmsg < 0)
> +		return;
> +
> +	close(fd_kmsg);
> +	fd_kmsg = -1;
> +}
> +
> +static void start_d3cold_kmsg_logging(void)
> +{
> +	igt_require_fd(fd_kmsg);
> +	igt_assert_neq(lseek(fd_kmsg, 0, SEEK_END), -1);
> +}
> +
> +static void check_d3cold_kmsg_count(void)
> +{
> +	int count;
> +
> +	igt_require_fd(fd_kmsg);
> +	count = count_kmsg_entries(fd_kmsg, d3cold_kmsg_entry);
> +	igt_info("Root port D3cold entry count=%d\n", count);
> +	igt_assert_f(count > 0,
> +		     "No root port D3cold entry detected during system suspend\n");
> +}
> +
> +static bool d3cold_validation_enabled(device_t device)
> +{
> +	uint32_t d3cold_allowed;
> +
> +	if (!igt_pm_acpi_d3cold_supported(device.pci_root))
> +		return false;
> +
> +	igt_pm_get_d3cold_allowed(device.pci_slot_name, &d3cold_allowed);
> +
> +	return d3cold_allowed;
> +}
> +
> +static void prepare_d3cold_kmsg(device_t device)
> +{
> +	igt_require_f(d3cold_validation_enabled(device),
> +		      "D3Cold validation requires a D3Cold-capable root port and d3cold_allowed=1\n");
> +
> +	if (fd_kmsg < 0) {
> +		build_rp_d3cold_kmsg_str(device.pci_root, d3cold_kmsg_entry,
> +					 sizeof(d3cold_kmsg_entry));
> +		fd_kmsg = open("/dev/kmsg", O_RDONLY | O_NONBLOCK);
> +		igt_require_fd(fd_kmsg);
> +	}
> +
> +	if (!acpi_d3cold_debug_is_enabled()) {
> +		igt_require_f(set_acpi_d3cold_debug(true),
> +			      "ACPI D3Cold dynamic debug is unavailable\n");
> +		acpi_d3cold_debug_enabled_by_test = true;
> +		igt_require_f(acpi_d3cold_debug_is_enabled(),
> +			      "Failed to enable ACPI D3Cold dynamic debug\n");
> +	}
> +}
> +
> +static void suspend_check_d3cold(device_t device, enum igt_suspend_state s_state,
> +				 enum igt_suspend_test test)
> +{
> +	igt_require_f(d3cold_validation_enabled(device),
> +		      "D3Cold validation requires a D3Cold-capable root port and d3cold_allowed=1\n");

This makes all the generic suspend subtests skip on machines without d3cold.
Please ensure this is only called or checked in d3cold related tests

> +
> +	if (igt_pm_get_acpi_real_d_state(device.pci_root) == IGT_ACPI_D3Cold) {
> +		igt_info("Root port already in D3cold at suspend entry; skip transition check\n");
> +		igt_system_suspend_autoresume(s_state, test);
> +		return;
> +	}
> +
> +	prepare_d3cold_kmsg(device);
> +
> +	/* Seek to end before suspend so only new messages are scanned */
> +	start_d3cold_kmsg_logging();
> +	igt_system_suspend_autoresume(s_state, test);
> +	check_d3cold_kmsg_count();
> +}
> +
>  #define MAX_VMAS 2
>  
>  static void*
> @@ -578,7 +776,7 @@ child_exec(void *arguments)
>  static void
>  test_exec(device_t device, int n_exec_queues, int n_execs,
>  		  enum igt_suspend_state s_state, enum igt_acpi_d_state d_state,
> -		  unsigned int flags)
> +		  unsigned int flags, bool validate_d3cold)
>  {
>  	enum igt_suspend_test test = s_state == SUSPEND_STATE_DISK ?
>  				     SUSPEND_TEST_DEVICES : SUSPEND_TEST_NONE;
> @@ -587,6 +785,10 @@ test_exec(device_t device, int n_exec_queues, int n_execs,
>  	pthread_t threads[65]; /* MAX_ENGINES + 1 */
>  	child_exec_args args;
>  
> +	if (validate_d3cold && n_execs > 1 && s_state != NO_SUSPEND) {
> +		prepare_d3cold_kmsg(device);
> +	}
> +
>  	xe_for_each_engine(device.fd_xe, eci) {
>  		args.device = device;
>  		args.eci = eci;
> @@ -607,7 +809,10 @@ test_exec(device_t device, int n_exec_queues, int n_execs,
>  	}
>  
>  	if (n_execs > 1 && s_state != NO_SUSPEND) {
> -		igt_system_suspend_autoresume(s_state, test);
> +		if (validate_d3cold)
> +			suspend_check_d3cold(device, s_state, test);
> +		else
> +			igt_system_suspend_autoresume(s_state, test);
>  
>  		pthread_mutex_lock(&suspend_lock);
>  		pthread_cond_broadcast(&suspend_cond);
> @@ -845,7 +1050,7 @@ static void test_mocs_suspend_resume(device_t device, enum igt_suspend_state s_s
>  				enum igt_suspend_test test = s_state == SUSPEND_STATE_DISK ?
>  					SUSPEND_TEST_DEVICES : SUSPEND_TEST_NONE;
>  
> -				igt_system_suspend_autoresume(s_state, test);
> +				suspend_check_d3cold(device, s_state, test);
>  			}
>  		}
>  		igt_assert(igt_debugfs_exists(device.fd_xe, path, O_RDONLY));
> @@ -981,7 +1186,7 @@ int igt_main()
>  		igt_device_get_pci_slot_name(device.fd_xe, device.pci_slot_name);
>  
>  		/* Always perform initial once-basic exec checking for health */
> -		test_exec(device, 1, 1, NO_SUSPEND, NO_RPM, 0);
> +		test_exec(device, 1, 1, NO_SUSPEND, NO_RPM, 0, false);
>  
>  		igt_pm_get_d3cold_allowed(device.pci_slot_name, &d3cold_allowed);
>  		has_runtime_pm = igt_setup_runtime_pm(device.fd_xe);
> @@ -989,6 +1194,8 @@ int igt_main()
>  		device.res = drmModeGetResources(device.fd_xe);
>  
>  		igt_install_exit_handler(igt_drm_debug_mask_reset_exit_handler);
> +		igt_install_exit_handler(close_d3cold_kmsg_handle);
> +		igt_install_exit_handler(restore_acpi_d3cold_debug);
>  		update_debug_mask_if_ci(DRM_UT_KMS);
>  		errno = 0;
>  	}
> @@ -997,28 +1204,28 @@ int igt_main()
>  		igt_subtest_f("%s-basic", s->name) {
>  			enum igt_suspend_test test = s->state == SUSPEND_STATE_DISK ?
>  				SUSPEND_TEST_DEVICES : SUSPEND_TEST_NONE;
> -			igt_system_suspend_autoresume(s->state, test);
> +			suspend_check_d3cold(device, s->state, test);

s4 shouldn't check for d3cold... I believe not even s3... so, let's limit
the scope of this test

>  		}
>  
>  		igt_subtest_f("%s-basic-exec", s->name) {
> -			test_exec(device, 1, 2, s->state, NO_RPM, 0);
> +			test_exec(device, 1, 2, s->state, NO_RPM, 0, true);
>  		}
>  
>  		igt_subtest_f("%s-exec-after", s->name) {
>  			enum igt_suspend_test test = s->state == SUSPEND_STATE_DISK ?
>  				SUSPEND_TEST_DEVICES : SUSPEND_TEST_NONE;
>  
> -			igt_system_suspend_autoresume(s->state, test);
> -			test_exec(device, 1, 2, NO_SUSPEND, NO_RPM, 0);
> +			suspend_check_d3cold(device, s->state, test);
> +			test_exec(device, 1, 2, NO_SUSPEND, NO_RPM, 0, false);
>  		}
>  
>  		igt_subtest_f("%s-multiple-execs", s->name) {
> -			test_exec(device, 16, 32, s->state, NO_RPM, 0);
> +			test_exec(device, 16, 32, s->state, NO_RPM, 0, true);
>  		}
>  
>  		for (const struct vm_op *op = vm_op; op->name; op++) {
>  			igt_subtest_f("%s-vm-bind-%s", s->name, op->name) {
> -				test_exec(device, 16, 32, s->state, NO_RPM, op->flags);
> +				test_exec(device, 16, 32, s->state, NO_RPM, op->flags, true);
>  			}
>  		}
>  
> @@ -1026,7 +1233,8 @@ int igt_main()
>  			igt_subtest_f("%s-%s-basic-exec", s->name, d->name) {
>  				igt_require_f(has_runtime_pm, "Runtime PM not available\n");
>  				igt_assert(setup_d3(device, d->state));
> -				test_exec(device, 1, 2, s->state, NO_RPM, 0);
> +				test_exec(device, 1, 2, s->state, NO_RPM, 0,
> +					  d->state == IGT_ACPI_D3Cold);
>  				cleanup_d3(device);
>  			}
>  		}
> @@ -1050,7 +1258,7 @@ int igt_main()
>  		igt_subtest_f("%s-basic-exec", d->name) {
>  			igt_require_f(has_runtime_pm, "Runtime PM not available\n");
>  			igt_assert(setup_d3(device, d->state));
> -			test_exec(device, 1, 1, NO_SUSPEND, d->state, 0);
> +			test_exec(device, 1, 1, NO_SUSPEND, d->state, 0, false);
>  			cleanup_d3(device);
>  		}
>  
> @@ -1065,7 +1273,7 @@ int igt_main()
>  		igt_subtest_f("%s-multiple-execs", d->name) {
>  			igt_require_f(has_runtime_pm, "Runtime PM not available\n");
>  			igt_assert(setup_d3(device, d->state));
> -			test_exec(device, 16, 32, NO_SUSPEND, d->state, 0);
> +			test_exec(device, 16, 32, NO_SUSPEND, d->state, 0, false);
>  			cleanup_d3(device);
>  		}
>  
> -- 
> 2.25.1
> 

  reply	other threads:[~2026-11-11 18:48 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-02  9:58 [PATCH i-g-t 0/1] tests/intel/xe_pm: Check root-port D3cold entry Karthik Poosa
2026-11-02  9:58 ` [PATCH i-g-t] tests/intel/xe_pm: Validate root-port D3cold entry on system suspend Karthik Poosa
2026-11-11 18:45   ` Rodrigo Vivi [this message]
2026-11-02 19:45 ` ✓ Xe.CI.BAT: success for " Patchwork
2026-11-02 19:46 ` ✓ i915.CI.BAT: " Patchwork
2026-11-02 20:35 ` ✓ Xe.CI.FULL: " Patchwork
2026-11-02 21:31 ` ✓ i915.CI.Full: " Patchwork

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=avS3gcuObbGGHPbT@intel.com \
    --to=rodrigo.vivi@intel.com \
    --cc=anshuman.gupta@intel.com \
    --cc=badal.nilawar@intel.com \
    --cc=igt-dev@lists.freedesktop.org \
    --cc=karthik.poosa@intel.com \
    --cc=mallesh.koujalagi@intel.com \
    --cc=raag.jadav@intel.com \
    --cc=ravi.kishore.koppuravuri@intel.com \
    --cc=riana.tauro@intel.com \
    --cc=sk.anirban@intel.com \
    --cc=soham.purkait@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox