Skip to content

Experiment: withdraw pmp-dispext0's PMP request in sleep (module, under the driver's lock) - #3

Merged
CoreyH merged 4 commits into
mainfrom
exp/pmp-dispext-release
Oct 29, 2026
Merged

CoreyH merged 4 commits into
mainfrom
exp/pmp-dispext-release

Conversation

@CoreyH

@CoreyH CoreyH commented Oct 29, 2026 •

Copy link
Copy Markdown
Owner

Why

omacom/linux-aurora #274 cut M3 Pro s2idle from ~2.6 W to 1.35 W, mostly by withdrawing the external display processors' PMP report requests during sleep (patch 6, for entries marked apple,release-for-sleep). This M1 Pro has the same PMP report block, and its device tree marks pmp-disp0 and pmp-dispext0 apple,always-on. A read-only check (target = actual = 0x7000) shows both asserted and acknowledged with nothing attached, so they stay up all night.

This is a pre-screen before building a whole kernel: does withdrawing pmp-dispext0's request during sleep save real power here?

What

  • pmp-sleep-release/: an out-of-tree module that does what patch 6 does.
    • Timing: it withdraws each configured request the PMP has acknowledged in suspend_noirq, after every device's suspend callback, and asserts it again in resume_noirq, before any device resumes.
    • Serialization: both writes use the driver's register sequence under the driver's own rep->lock.
    • Reaching the driver: pmp-report is built in, so the module gets to it through the report device's drvdata. The leading struct members come from the running kernel's BTF.
    • Load checks: load refuses unless rep->dev, the T600x offsets and every id (an enabled entry in the live device tree) check out. vermagic pins it to this kernel.
    • Reporting: read-only parameters record what each sleep withdrew and any unacknowledged writes.
  • sleep-drain-pmp release 0xd / keep loads or unloads it. Nothing loads it at boot, so a reboot always ends the experiment. install.sh builds it for the running kernel (non-fatal if the headers are missing).
  • sleep-drain-log writes nothing new. It logs the module's ids before the sleep and pmp_released / pmp_failures after.
  • Snapshots add cc= (the SMC coulomb counter, which counts mA·s here, not µAh) and uv=. The post line and sleep-drain-report gain avg_mw_cc, which is accurate for 10-minute sleeps where energy_now isn't.
  • pmp-request.c: a read-only view of the PMP report masks (no write path).
  • CLAUDE.md, RESEARCH.md (new "Lead" section), install/uninstall.

An earlier revision wrote the mask from userspace in the sleep hook. Review showed a concurrent driver update could be silently overwritten, which userspace can't prevent, so the write moved into the kernel.

Tested so far

  • Module builds against linux-aurora-headers 7.2.11-9 with clang (LLVM=1). The kernel itself was built with clang 18; this is clang 23, the usual warning.
  • pmp-request read-only as root: status=0x1 target=0x7000 actual=0x7000. The binary contains no MMIO store.
  • avg_mw_cc on a real 61 s awake sample: 12,292 mW vs energy_now's 12,110. Lines without cc print -.
  • Report rows (including no-post) align. bash -n on every script.

Not run yet: loading the module, or a sleep with it. That happens after review and merge.

After merging

pkexec bash install.sh, then sleep-drain-pmp release 0xd for the "on" runs and sleep-drain-pmp keep for the "off" runs.

🤖 Generated with Claude Code

CoreyH and others added 2 commits October 29, 2026 13:54
…leep

aurora #274 cut M3 Pro s2idle from ~2.6 W to 1.35 W mostly by withdrawing
external display PMP requests in suspend_noirq. On T600x, pmp-disp0 and
pmp-dispext0 are apple,always-on, and a read of the PMP report (target =
actual = 0x7000) shows both asserted with nothing attached.

- pmp-request.c: read, withdraw or assert one request on T600x through
  /dev/mem, with the pmp-report driver's register sequence (aligned 64-bit
  ldr/str only) and a refusal unless the live device tree is this block.
- sleep-drain-log: with /etc/sleep-drain/pmp-release (sleep-drain-pmp), the
  listed requests are withdrawn as the last pre step and asserted again as
  the first post step; only ones this hook withdrew go back. Logged as
  pmp_release= and pmp_back=.
- Snapshots log charge_counter (mA·s on this machine, not µAh) and voltage;
  the post line and report gain avg_mw_cc, accurate for short sleeps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A request left withdrawn after resume would starve that display processor,
so the post step retries for up to a second. "Already asserted" on the
first try means something re-asserted it during the sleep (logged
<id>:early); after an unacknowledged write it is that write landing (ok).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

The userspace PMP mask update is not serialized with the kernel driver's updates; resolve whether the writes can overlap before enabling this experiment.

Reviewed changes This review covers the PMP request helper and sleep-hook release/restore flow, coulomb-counter power measurement and reporting, plus installer and research-documentation updates.

  • PMP request experiment Adds a device-tree-guarded /dev/mem helper and optional pre-suspend withdrawal/post-resume restoration of configured request bits.
  • Power measurement Adds coulomb-counter and voltage snapshots, avg_mw_cc calculations, and the corresponding report column.
  • Installation and documentation Adds the PMP toggle command and updates install, uninstall, and research instructions.

Important

This run used your free trial credits ($9.78 of $10 left) on GPT Luna, a fast, lightweight model. Connect a provider → to run the model you choose.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using gpt-6-luna (free trial credits) | 𝕏

Comment thread pmp-request.c Resolved
Comment thread sleep-drain-report Resolved
…W window

Review on #3: the kernel driver updates the same target mask under a
spinlock userspace can't take, so an overlapping update could be lost.
- Refuse to write while target != actual (exit 4): the driver may be
  mid-update. The hook logs <id>:busy and records nothing; the restore
  retries.
- Read actual, read target and write the new mask back to back, at
  SCHED_FIFO priority, so no normal task preempts the window.
- After the acknowledgement, re-read the mask; if it changed elsewhere,
  exit 5 (logged <id>:raced) so a collision is visible.
The residual window is a few instructions; the real fix is patch 6 in the
driver, under its lock. Also: the report's no-post and pending rows gain
the mW(cc) placeholder so they stay aligned.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

The updated PMP collision check can still silently overwrite a concurrent kernel request and let its power-domain transition time out.

Reviewed changes This review covers d5399ef, which added settling-state checks, a higher-priority read-modify-write window, post-acknowledgement mask verification, and aligned the pending/no-post report rows.

  • Narrowed the PMP write window Refused writes while target and actual masks differ, raised the helper to SCHED_FIFO around the read-modify-write, and added a post-write mask check.
  • Aligned exceptional report rows Added the missing mW(cc) placeholder to pending and no-post rows.

Important

This run used your free trial credits ($9.73 of $10 left) on GPT Luna, a fast, lightweight model. Connect a provider → to run the model you choose.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using gpt-6-luna (free trial credits) | 𝕏

Comment thread pmp-request.c Resolved
Review on #3: a userspace read-modify-write of the PMP target mask can
overwrite a concurrent pmp-report update undetectably (the driver's write can
land between our read and our write), and userspace can't take the driver's
spinlock. So the write moves into the kernel:

- pmp-sleep-release/: an out-of-tree module that withdraws the requests in
  its ids in suspend_noirq (only if acknowledged) and asserts them again in
  resume_noirq, with the driver's register sequence under rep->lock, which
  is what aurora #274's patch 6 does. pmp-report is built in, so the module
  reaches the report through its device's drvdata. The leading struct members
  come from the running kernel's BTF, and load refuses unless rep->dev, the
  T600x offsets and every id (an enabled entry in the live DT) check out.
  Read-only parameters report what each sleep withdrew and any unacknowledged
  writes.
- sleep-drain-pmp loads and unloads the module (nothing loads it at boot);
  install.sh builds it for the running kernel, non-fatally.
- The hook no longer writes: it logs the module's ids before the sleep and
  pmp_released/pmp_failures after.
- pmp-request is read-only: no write path or MMIO store left.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@CoreyH CoreyH changed the title Experiment: withdraw pmp-dispext0's PMP request for the sleep Experiment: withdraw pmp-dispext0's PMP request in sleep (module, under the driver's lock) Oct 29, 2026

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes This review covers 14fc957, which replaces userspace PMP writes with a kernel module that withdraws configured requests under the report driver's lock during system sleep and restores them on resume.

  • Moved PMP writes into noirq callbacks Added pmp_sleep_release, validated the live report device, register offsets, and enabled DT request IDs, and reduced pmp-request to read-only access.
  • Updated experiment control and logging Added explicit module load/unload controls, logged the configured IDs and module outcome through the sleep hook, and updated installation, uninstall, and research documentation. The PR description reports that the module has not yet been loaded or tested during sleep.

Important

This run used your free trial credits ($9.71 of $10 left) on GPT Luna, a fast, lightweight model. Connect a provider → to run the model you choose.

Pullfrog  | View workflow run | Using gpt-6-luna (free trial credits) | 𝕏

@CoreyH
CoreyH merged commit 1a9c48e into main Oct 29, 2026
1 check passed
@CoreyH
CoreyH deleted the exp/pmp-dispext-release branch October 29, 2026 13:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant