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
>
next prev parent 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.