Skip to content

drm/apple-g5p: Bound the kexec handover and fix two lost-GPU races - #125

Open
brkosebay wants to merge 79 commits into
OrchardLinux:mainfrom
brkosebay:j71t/gpu-handover
Open

brkosebay wants to merge 79 commits into
OrchardLinux:mainfrom
brkosebay:j71t/gpu-handover

Conversation

@brkosebay

@brkosebay brkosebay commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Part 11 of 11 of the iPad 5 (J71t) series in this repository; merge after #120.

Problem

A warm restart could jump into the next kernel while the GPU still had DMA in flight, or hang in an unbounded wait for the firmware to stop. Two races could also lose the GPU: with two jobs in flight, the previous job's 3D completion could make a new job's 3D phase look retired, so its doorbell was never rung and the job was lost after a silent second; and when the firmware's ALIVE overtook the GPU cores' power-on request, the busy request was counted as a failed power-on and the boot fell back to software rendering.

Change

Bound and serialise the GPU handover, accept an already completed handover, keep halt and poweroff working after a quiesce failure, and flush the consoles before an emergency cold reset. Ring a render phase's doorbell unless the phase's own stamp shows it retired, as Apple's TA and 3D submit paths ring after every ring entry. When ALIVE overtakes a power-on request that is still in flight, let the first-ALIVE handler wait for the request's busy bit to clear within the power-on bound, as Apple's power-state code does, before any GPU register write.

Testing

  • Hardware: iPad 5 Wi-Fi (J71t). Hands-on testing on 2026-11-11 with an integrated build of this series, made before the final failure-path fixes, covered warm restart with the GPU in use.
  • Not covered on hardware: the cold-reset escape when the handover fails. The power-on race is rare (twice in a day of boots before the fix), so the fix is not yet shown on hardware.
  • 2026-11-12, doorbell fix: glmark2 terrain ran 20 of 20 and then 5 of 5 times without a lost job (before the fix, 2 of about 11 runs lost a 3D job). 20+ warm restarts with the GPU in use, several right after a glmark2 run, completed without a freeze.
  • Host: the Linux test suite passes at this branch's head, and the kernel and m1n1 patch series apply on apple-16k and apple-4k. At the top of the series the applied kernel tree differs from the built release image only where main took earlier PRs of this series in reworked form, by the closed perf: apple: A9 memory port byte counters #123, and by the new G5P sleep refusal (drivers/drm 0045, not yet run on the device).
  • Other boards that share these drivers or compatibles have not been tested.

Dependencies

Depends on #120.

This branch is stacked on the PRs it depends on and carries their commits; the change of this PR is the commit "drm/apple-g5p: Bound the kexec handover and fix two lost-GPU races", and the merge commits after it bring the branch onto current main.

This update keeps the branch's history: it merges current main and the updated branch below it into this branch. Nothing is forced.

🤖 Generated with Claude Code

Add the A9 (S8003) G5P render driver used by the merged PowerVR/G5P Mesa
winsys as kernel patches, enable it on J71t in the device tree, set
CONFIG_DRM_APPLE_G5P=y for apple-16k, and add an install-time recipe that
extracts the original META LDR (apple/agx-g5p-c0.ldr) from the device's own
iOS kernelcache into /lib/firmware. No firmware is committed.

The driver provides TYPED_V3 rendering, GEM buffers, VM/arena mappings,
PRIME import/export, syncobj, bounded retirement and recovery. It does not
expose the old compute or fixed-fixture ABI.

Hardware: the exact kernel this derives from (brkosebay/linux a9/gpu-pr1
407b7bf2cbbe) booted on a J71t with the merged Mesa, rendered labwc on the
GPU with KMS scanout at 60 Hz, and passed the G5P device rungs with zero GPU
events. This Orchard adaptation itself has not yet been built by orchard or
run on hardware.

Assisted-by: LLM
kernel/configs/apple-16k/config:
CONFIG_DRM_APPLE_G5P=y
These options provide g5p and its Kconfig dependencies on both page sizes.

Assisted-by: LLM
kernel/configs/apple-16k/config:
# CONFIG_NVME_APPLE_ANS_EARLY_USB is not set
These options provide warm-storage and its Kconfig dependencies on both page sizes.

kernel/configs/apple-4k/config:
# CONFIG_NVME_APPLE_ANS_EARLY_USB is not set
These options provide warm-storage and its Kconfig dependencies on both page sizes.

Assisted-by: LLM
kernel/configs/apple-16k/config:
CONFIG_APPLE_AOP_GYRO=m
CONFIG_APPLE_AOP_PRESSURE=m
These options provide aop and its Kconfig dependencies on both page sizes.

Assisted-by: LLM
kernel/configs/apple-16k/config:
# CONFIG_NVME_APPLE_ANS_EARLY_USB is not set
These options provide warm-storage and its Kconfig dependencies on both page sizes.

kernel/configs/apple-4k/config:
# CONFIG_NVME_APPLE_ANS_EARLY_USB is not set
These options provide warm-storage and its Kconfig dependencies on both page sizes.

Assisted-by: LLM
kernel/configs/apple-16k/config:
CONFIG_APPLE_AOP_GYRO=m
CONFIG_APPLE_AOP_PRESSURE=m
These options provide aop and its Kconfig dependencies on both page sizes.

Assisted-by: LLM
kernel/configs/apple-16k/config:
# CONFIG_NVME_APPLE_ANS_EARLY_USB is not set
These options provide warm-storage and its Kconfig dependencies on both page sizes.

kernel/configs/apple-4k/config:
# CONFIG_NVME_APPLE_ANS_EARLY_USB is not set
These options provide warm-storage and its Kconfig dependencies on both page sizes.

Assisted-by: LLM
The VXD decoder only exposed H.264, so HEVC video was decoded on the
CPU.

Prepare the driver for a second coded format, add the HEVC
command-stream builder and the stateless HEVC decoder, enable it on the
S8000 and T8010 variants, and require a proven firmware stop before
kexec. Adds the video_decode_hevc feature (working on J71t, untested on
the other boards that have the driver, missing elsewhere).

Assisted-by: LLM
Bring this branch onto current main and the series it depends on. The
merge keeps the other side's tree; the next commit carries the reworked
version of this branch's change.
S8000/S8003 left the SoC and memory performance state at the boot
default, which bounds GPU throughput by memory bandwidth.

Describe the SoC performance state in the binding and DT, run the SoC
and memory at the high state while active, and restore the low state for
sleep.

Assisted-by: LLM
A Mac grants an iPad more than the standard USB current, but the charger
took the gadget's reported budget unchecked and the composite gadget
could draw the extra current while the bus was suspended.

Cap and log the USB host budget in the TriStar charger, draw no extra
Apple current while suspended, and let the initramfs and orchard USB
helpers opt J71t into the Mac current profile. Documented in
docs/bring-up/charging/j71t-mac-current.md, with tests.

Assisted-by: LLM
There was no way to measure memory bandwidth per port on A9, which is
needed to tell GPU work from memory stalls.

Add a binding, DT node and perf driver for the A9 AMC port monitor,
exposing CPU and GFX read/write byte counters, and enable it for
apple-16k.

Assisted-by: LLM
Bring this branch onto current main and the series it depends on. The
merge keeps the other side's tree; the next commit carries the reworked
version of this branch's change.
Bring this branch onto current main and the series it depends on. The
merge keeps the other side's tree; the next commit carries the reworked
version of this branch's change.
Each render job paid a fixed host cost (serial submission, polling after
events, uninterruptible VM binds), which capped frame rates well below
the GPU's capacity.

Account per-job GPU and host time, reduce serial job overhead, make VM
binds killable and tune post-event polling, record joint latency,
pipeline two render jobs deep, honour fragment-only waits, start the
next job early during the 3D wait, gate TA PM records on dirty state,
and default to the measured pipeline configuration.

Assisted-by: LLM
The GPU blocked every system sleep, and an idle GPU kept its power on
because the firmware was never allowed to park itself.

Let the firmware self-park after bring-up and ring a parked firmware
without the GPC settle wait. Implement a bounded retained system sleep:
register the reservation as a suspend admission, hold clients across it,
answer the readiness query, and refuse sleep with a named reason while
work, a kexec power hold or pmgr's gfx hold is active. pmdomain ends the
warm boot hold at sync_state, the gfx gate is cycled with auto-PM like
pmgr-pwrstate, and the other admission users name their refusals too.

Assisted-by: LLM
A warm restart could jump into the next kernel while the GPU still had
DMA in flight, or hang in an unbounded wait for the firmware to stop.
Two races could also lose the GPU: with two jobs in flight, the previous
job's 3D completion could make a new job's 3D phase look retired, so its
doorbell was never rung and the job was lost after a silent second; and
when the firmware's ALIVE overtook the GPU cores' power-on request, the
busy request was counted as a failed power-on and the boot fell back to
software rendering.

Bound and serialise the GPU handover, accept an already completed
handover, keep halt and poweroff working after a quiesce failure, and
flush the consoles before an emergency cold reset. Ring a render phase's
doorbell unless the phase's own stamp shows it retired, as Apple's TA
and 3D submit paths ring after every ring entry. When ALIVE overtakes a
power-on request that is still in flight, let the first-ALIVE handler
wait for the request's busy bit to clear within the power-on bound, as
Apple's power-state code does, before any GPU register write.

Assisted-by: LLM
Bring this branch onto current main. Main has since merged OrchardLinux#124, OrchardLinux#122,
OrchardLinux#118, OrchardLinux#121, OrchardLinux#110, OrchardLinux#127, OrchardLinux#101, OrchardLinux#126, OrchardLinux#108 and OrchardLinux#111, several in part or
reworked, and OrchardLinux#123 and OrchardLinux#128 were closed. Where main carries a topic,
main's version is kept; the left-out parts that warm restart and
suspend need come back in OrchardLinux#107 and OrchardLinux#117.
Bring this branch onto current main. Main has since merged OrchardLinux#124, OrchardLinux#122,
OrchardLinux#118, OrchardLinux#121, OrchardLinux#110, OrchardLinux#127, OrchardLinux#101, OrchardLinux#126, OrchardLinux#108 and OrchardLinux#111, several in part or
reworked, and OrchardLinux#123 and OrchardLinux#128 were closed. Where main carries a topic,
main's version is kept; the left-out parts that warm restart and
suspend need come back in OrchardLinux#107 and OrchardLinux#117.

This branch's own patches in drivers/usb are renumbered to follow
main's.
Bring this branch onto current main. Main has since merged OrchardLinux#124, OrchardLinux#122,
OrchardLinux#118, OrchardLinux#121, OrchardLinux#110, OrchardLinux#127, OrchardLinux#101, OrchardLinux#126, OrchardLinux#108 and OrchardLinux#111, several in part or
reworked, and OrchardLinux#123 and OrchardLinux#128 were closed. Where main carries a topic,
main's version is kept; the left-out parts that warm restart and
suspend need come back in OrchardLinux#107 and OrchardLinux#117.

This branch's own patches in devices/s8000, drivers/base and drivers/usb
are renumbered to follow main's.
Bring this branch onto current main. Main has since merged OrchardLinux#124, OrchardLinux#122,
OrchardLinux#118, OrchardLinux#121, OrchardLinux#110, OrchardLinux#127, OrchardLinux#101, OrchardLinux#126, OrchardLinux#108 and OrchardLinux#111, several in part or
reworked, and OrchardLinux#123 and OrchardLinux#128 were closed. Where main carries a topic,
main's version is kept; the left-out parts that warm restart and
suspend need come back in OrchardLinux#107 and OrchardLinux#117.

This branch's own patches in devices/s8000, drivers/base and drivers/usb
are renumbered to follow main's.
Bring this branch onto current main. Main has since merged OrchardLinux#124, OrchardLinux#122,
OrchardLinux#118, OrchardLinux#121, OrchardLinux#110, OrchardLinux#127, OrchardLinux#101, OrchardLinux#126, OrchardLinux#108 and OrchardLinux#111, several in part or
reworked, and OrchardLinux#123 and OrchardLinux#128 were closed. Where main carries a topic,
main's version is kept; the left-out parts that warm restart and
suspend need come back in OrchardLinux#107 and OrchardLinux#117.

This branch's own patches in devices/s8000, drivers/base,
drivers/pmdomain and drivers/usb are renumbered to follow main's.
The A9 AOP driver on main reads apple,aop-target, apple,chip-id and
apple,chip-revision, and the motion sensor drivers read their factory
calibration, from properties the bootloader fills in. Nothing on main
fills them in; going by the driver's probe, the A9 AOP cannot start
without them (read from the code, not run). This is the m1n1 patch that
fills them in, from OrchardLinux#101, unchanged.

Assisted-by: LLM
From OrchardLinux#101, the part that main did not take with the sensors: the
retained AOP reload across kexec (checksummed shutdown log, power-state
observer, pr_flush export, warm_state attribute). The sensor parts that
main carries are left out of these patches; what remains applies on
top of main's AOP driver and its fresh-start order.

Assisted-by: LLM
From OrchardLinux#110, the part main did not take with the stable USB serial: the
platform layer embeds each supported board's pristine DTB and a
compatible-to-file map in the initrd. Before switching root, the initrd
copies the running board's tree to /run/orchard-kexec/pristine.dtb, or,
after a warm restart, the retained tree the previous kernel named with
orchard_warm.dtb=, and records the boot ID next to it. Warm restart
boots the next kernel with that tree, never with one rebuilt from an
updated /boot.

No feature table is added: whether warm restart is offered is decided
from the mechanisms it uses, not from a feature state.

Assisted-by: LLM
Bring this branch onto current main. Main has since merged OrchardLinux#124, OrchardLinux#122,
OrchardLinux#118, OrchardLinux#121, OrchardLinux#110, OrchardLinux#127, OrchardLinux#101, OrchardLinux#126, OrchardLinux#108 and OrchardLinux#111, several in part or
reworked, and OrchardLinux#123 and OrchardLinux#128 were closed. Where main carries a topic,
main's version is kept; the left-out parts that warm restart and
suspend need come back in OrchardLinux#107 and OrchardLinux#117.

This branch's own patches in devices/s8000, drivers/base,
drivers/pmdomain and drivers/usb are renumbered to follow main's.
Bring this branch onto current main. Main has since merged OrchardLinux#124, OrchardLinux#122,
OrchardLinux#118, OrchardLinux#121, OrchardLinux#110, OrchardLinux#127, OrchardLinux#101, OrchardLinux#126, OrchardLinux#108 and OrchardLinux#111, several in part or
reworked, and OrchardLinux#123 and OrchardLinux#128 were closed. Where main carries a topic,
main's version is kept; the left-out parts that warm restart and
suspend need come back in OrchardLinux#107 and OrchardLinux#117.

This branch's own patches in devices/s8000, devices/t8010, drivers/base,
drivers/media, drivers/pmdomain, drivers/power and drivers/usb are
renumbered to follow main's.

Main's versions apply first now, so 0011 in drivers/media keep their
change with a diff regenerated for the new context.
From OrchardLinux#101, the suspend precheck that main did not take with the
sensors. It belongs with suspend: it makes the AOP refuse a pre-sync
system sleep while its firmware is starting or stopping.

Assisted-by: LLM
From OrchardLinux#121, the patch main did not take with HEVC. It uses the VXD
sleep stop and sleep gate this branch adds, so it sits here rather
than with the earlier warm restart branch: a failed firmware stop keeps
the firmware, command buffers and power, and kexec takes the cold-reset
path before those pages become the next kernel's RAM.

Assisted-by: LLM
Bring this branch onto current main. Main has since merged OrchardLinux#124, OrchardLinux#122,
OrchardLinux#118, OrchardLinux#121, OrchardLinux#110, OrchardLinux#127, OrchardLinux#101, OrchardLinux#126, OrchardLinux#108 and OrchardLinux#111, several in part or
reworked, and OrchardLinux#123 and OrchardLinux#128 were closed. Where main carries a topic,
main's version is kept; the left-out parts that warm restart and
suspend need come back in OrchardLinux#107 and OrchardLinux#117.

This branch's own patches in devices/s8000, devices/s8003,
devices/t8010, drivers/base, drivers/media, drivers/pmdomain,
drivers/power and drivers/usb are renumbered to follow main's.

Main's versions apply first now, so 0011 in drivers/media keep their
change with a diff regenerated for the new context.
Bring this branch onto current main. Main has since merged OrchardLinux#124, OrchardLinux#122,
OrchardLinux#118, OrchardLinux#121, OrchardLinux#110, OrchardLinux#127, OrchardLinux#101, OrchardLinux#126, OrchardLinux#108 and OrchardLinux#111, several in part or
reworked, and OrchardLinux#123 and OrchardLinux#128 were closed. Where main carries a topic,
main's version is kept; the left-out parts that warm restart and
suspend need come back in OrchardLinux#107 and OrchardLinux#117.

This branch's own patches in devices/s8000, devices/s8003,
devices/t8010, drivers/base, drivers/media, drivers/pmdomain,
drivers/power and drivers/usb are renumbered to follow main's.

Main's versions apply first now, so 0011 in drivers/media keep their
change with a diff regenerated for the new context.
Bring this branch onto current main. Main has since merged OrchardLinux#124, OrchardLinux#122,
OrchardLinux#118, OrchardLinux#121, OrchardLinux#110, OrchardLinux#127, OrchardLinux#101, OrchardLinux#126, OrchardLinux#108 and OrchardLinux#111, several in part or
reworked, and OrchardLinux#123 and OrchardLinux#128 were closed. Where main carries a topic,
main's version is kept; the left-out parts that warm restart and
suspend need come back in OrchardLinux#107 and OrchardLinux#117.

This branch's own patches in devices/s8000, devices/s8003,
devices/t8010, drivers/base, drivers/media, drivers/pmdomain,
drivers/power, drivers/soc-apple and drivers/usb are renumbered to
follow main's.

Main's versions apply first now, so 0011 in drivers/media keep their
change with a diff regenerated for the new context.
Bring this branch onto current main. Main has since merged OrchardLinux#124, OrchardLinux#122,
OrchardLinux#118, OrchardLinux#121, OrchardLinux#110, OrchardLinux#127, OrchardLinux#101, OrchardLinux#126, OrchardLinux#108 and OrchardLinux#111, several in part or
reworked, and OrchardLinux#123 and OrchardLinux#128 were closed. Where main carries a topic,
main's version is kept; the left-out parts that warm restart and
suspend need come back in OrchardLinux#107 and OrchardLinux#117.

This branch's own patches in devices/s8000, devices/s8003,
devices/t8010, drivers/base, drivers/media, drivers/pmdomain,
drivers/power, drivers/soc-apple and drivers/usb are renumbered to
follow main's.

Main's versions apply first now, so 0011 in drivers/media keep their
change with a diff regenerated for the new context.
On J71t a completed suspend/resume cycle does not bring the GPU back,
yet the G5P sleep admission admits an idle GPU, so
/sys/power/suspend_ready reads "ready" and userspace that follows it
suspends into a GPU that does not come back.

drivers/drm 0045 adds a read-only "suspend" parameter to the G5P
driver, off by default. While it is off, the GPU's sleep admission
refuses before anything is shut, and suspend_ready reads
"not-ready <gpu>:resume-unsupported -95". Booting with
a9_gpu_drm.suspend=1 admits sleep exactly as before. Boards without
the G5P driver are not affected.

The patch takes the next free number in drivers/drm, after the
gpu-handover branch's 0042-0044, so every existing patch keeps its
number.

Assisted-by: LLM
Carry the G5P sleep refusal (drivers/drm 0045) from j71t/g5p-suspend.

Assisted-by: LLM
Bringing this branch onto main took main's devices/j71t/firmware.json,
which main rewrote with its AOP firmware, and lost the output the GPU
needs: lib/firmware/apple/agx-g5p-c0.ldr, extracted from the
kernelcache by agx-g5p-ldr-from-kernelcache@1 (src/orchard/recipe.py
still has the step). Without it the G5P driver finds no loader and the
GPU never starts ("autoboot stopped at validate (-2)").

Put the entry back after main's outputs, unchanged; the file is again
main's plus this one output.

Assisted-by: LLM
Carry the restored G5P firmware loader entry in
devices/j71t/firmware.json.
Carry the restored G5P firmware loader entry in
devices/j71t/firmware.json.
Carry the restored G5P firmware loader entry in
devices/j71t/firmware.json.
J71t's agx-g5p-c0.ldr output was lost when main's firmware.json was
merged in, while its recipe step stayed, and no test noticed: the GPU
simply never started. Assert that J71t ships the loader, and that every
step in orchard.recipe.STEPS is used by some board's firmware output,
which holds on main as well.

Assisted-by: LLM
Carry the firmware recipe test for the G5P loader.
Carry the firmware recipe test for the G5P loader.
Carry the firmware recipe test for the G5P loader.
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