pci_device_shutdown() resumes devices on poweroff: ~19 W in S5

Anxo <[email protected]>
Newsgroups org.kernel.vger.linux-pm,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pci
Message-ID <CAMkZroTGOXnLFGx=j+mkiKnvrivsNAvUt5=kc5dJQON0JHhzBg@mail.gmail.com>
Hi,

On an HP OMEN 16-ap0xxx (AMD iGPU + NVIDIA discrete, Fedora 44, kernel
7.1.8-200.fc44.x86_64), a normal `poweroff` leaves the machine drawing
19-20 W in S5. A full battery is empty by morning. Windows on the same
hardware does not do this, and halting straight from GRUB - so that no
Linux kernel ever runs the shutdown path - measures 1.05 W. The
platform's S5 is clean; what poisons it is what Linux leaves behind.

The cause looks like pci_device_shutdown():

static void pci_device_shutdown(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct pci_driver *drv = pci_dev->driver;

pm_runtime_resume(dev);

if (drv && drv->shutdown)
drv->shutdown(pci_dev);
...

That pm_runtime_resume() is unconditional and runs after every userspace
shutdown hook. The discrete GPU, which userspace has already let fall to
D3cold, is brought back to D0 on the way into S5 and stays powered. No
userspace mitigation is possible from outside the kernel: you can put the
device to sleep a millisecond before poweroff and it is woken again
afterwards.

The resume was added in 2012 by commit 3ff2de9ba1a2 ("PCI/PM: Resume
device before shutdown"), and its rationale is kexec:

  "Without this patch, a device may not be enumerated after a kexec
   because the corresponding bridge is not in D0, so that configuration
   space of the device is not accessible."

On a real poweroff there is no kexec, and pci_device_shutdown() already
tests kexec_in_progress a few lines below, to decide whether to clear Bus
Master. The information needed to tell the two cases apart is already in
scope at that point; the resume simply does not consult it.

Measurements. All are battery energy_now before and after a real
shutdown, over windows of the stated length. Raw watts are not comparable
across windows of different length, so the nights and the 20-minute
windows should be read separately:

  normal shutdown, unpatched (n=2)      20.05 / 19.73 W
  dGPU pinned in D0, poweroff via
    PM1a_CNT so device_shutdown()
    never runs (single-variable control)      19.85 W
  GRUB `halt`, no Linux kernel ran           1.05 W
  dGPU kept in D3cold through S5,
    four whole nights           0.46 / 0.46 / 0.47 / 0.45 W

The 19.85 W row is the one that closes it: same shutdown path as its own
twin, one variable changed (the D-state the dGPU arrives in).

Ruled out, each with its own measurement rather than by argument: the
DSDT and all AML (measured with a live 56-byte DSDT), all 23 static
SSDTs, the SCI and the GPEs, the FADT and ACPICA's hardware layer, the
IOMMU, amd_pstate and the CPPC MSR, every armed wakeup source, all of
userspace and systemd (measured with a custom PID 1, no udev, no
journald), and the shutdown method itself (EFI ResetSystem vs ACPI). The
full log, including the dead ends and the measurements I lost to my own
methodology errors, is here:

  https://github.com/AnxoCalvo/s5-poweroff-fix
  https://github.com/AnxoCalvo/s5-poweroff-fix/blob/main/docs/EVIDENCE.md

I am deliberately not sending a patch. The 2012 commit message notes that
some shutdown work - MSI shutdown was mentioned - needs the device in D0,
so making the resume conditional is not simply a matter of testing
kexec_in_progress, and I have no basis for judging which drivers rely on
being in D0 inside their .shutdown(). Proposing a change to shared code
from a sample of one laptop did not seem right.

What I do have is a rig that measures this reliably. The discriminator is
battery energy over a fixed window; wakeup behaviour and thermal
signature both looked convincing and were both misleading. If a patch
would be useful to test, I am glad to run it and report numbers.

Caveats, stated plainly: one machine, one firmware, one vendor's GPU. The
mechanism appears generic - there are reports blaming Thunderbolt/USB4
docks and xHCI controllers rather than GPUs - but I have measured only
this one, and the numbers above are not evidence about anyone else's
hardware.

Thanks,
Anxo Calvo
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.