Skip to content

The two unattributed halves of the s2idle cycle are one PM notifier each -- Easel's PCIe revival and the firmware cache

scope: device:google-taimen · severity: finding · confidence: proven · subsystem: pm

The question — ~480 ms of a 958 ms s2idle cycle sat outside device PM, in two blocks nobody had attributed: one inside suspend_enter before freeze_processes, one inside suspend_finish after Restarting tasks: Done.

You do not need a function tracer. This kernel has none – available_tracers is nop, CONFIG_FUNCTION_TRACER is unset – so the function_graph recipe cannot run at all. It is also not needed: events/notifier/notifier_run is a plain tracepoint, present whenever CONFIG_TRACING=y, and its TP_printk is "%ps" on the callback. Enable it with power/suspend_resume for the phase markers and every PM notifier names itself, in order, with a timestamp.

Each window is one notifier.

window A 116.5 ms fw_pm_notify 110.9 ms pm_prepare_console 0.004 ms
window B 353.9 ms easel_pm_notify 328.5 ms pci_notify 23.4 ms

fw_pm_notify -> device_cache_fw_images(), and this device sets CONFIG_FW_LOADER_COMPRESS_ZSTD=y, so every suspend zstd-decompresses the firmware of every device into RAM – to be dropped again 10 s after resume. The cache exists so a firmware request during resume can be served without the filesystem. Nothing on this device makes one: rootfs is UFS and is back before driver resume. CONFIG_FW_CACHE=n removes the notifier outright.

easel_pm_notify on PM_POST_SUSPEND powers Easel on, cycles PERST#, retrains the PCIe link, polls config space in msleep(20) steps and rescans the bus. The PM_POST_SUSPEND chain runs inside suspend_finish(), before pm_suspend() returns – so all 328 ms are in front of the user. Moving it to a work item costs nothing: while it runs Easel is absent, which is exactly the state a failed revival already leaves behind, so no caller learns a new case. PM_SUSPEND_PREPARE cancel_work_sync()s first.

Together: 958 ms -> 440 ms, measured end to end from dmesg.

The 309 ms dpm_suspend was real, not a capture artefact. It is wiphy_suspend, and it reads 246 ms or 113 ms depending on one thing: whether WoWLAN was armed. The system-sleep hook only runs on the logind path, so any cycle driven by rtcwake or a raw /sys/power/state write measures the unarmed number – unless an earlier logind suspend armed it, because the hook deliberately leaves it armed. Two runs on the same kernel differ by 240 ms for that reason alone. Always record iw phy0 wowlan show in the same capture.