Skip to content

drm/apple: don't treat a slow poweroff clear swap as a DCP crash - #635

Open
makhov wants to merge 2 commits into
AsahiLinux:fairydustfrom
makhov:drm-apple-poweroff-timeout
Open

makhov wants to merge 2 commits into
AsahiLinux:fairydustfrom
makhov:drm-apple-poweroff-timeout

Conversation

@makhov

@makhov makhov commented Sep 21, 2026

Copy link
Copy Markdown

Problem

On a MacBookPro18,3 (M1 Pro) running fairydust, hot-plugging a USB-C DP monitor sometimes leaves it black until reboot. DP-1 is reported as connected, but every commit on the CRTC fails with -EINVAL, and the compositor can't even disable the output. Details in #634.

Root cause

iomfb_poweroff() submits a clear swap and waits 50 ms for it. On timeout it sets dcp->crashed = true and returns without logging anything. From then on dcp_crtc_atomic_check() rejects every commit on that CRTC.

On DP unplug the firmware first does its own teardown (display HPD removed, power_down_M3, set_device_enabled_gated: 1 -> 0) and only then swallows the clear swap. On a failing unplug that took ~53 ms. The DCP keeps working normally afterwards (later hotplug callbacks and syslog messages all arrive), so this isn't a real crash. I saw the pattern in 3 separate boots.

Fix

  1. Don't treat a slow clear swap as a crash. Wait up to 1000 ms, the same as the setPowerState(0) wait that follows. On timeout, warn and continue powering off, the same way the setPowerState(0) timeout is already handled. dcp->crashed is then only set by dcp_rtk_crashed(). A longer wait alone isn't enough: a clear swap queued behind a slow operation (an unplug during a modeset, or right after resume, where powering on took about 1 s) could still miss the deadline. A late reply is safe because the swap cookie is refcounted.
  2. Stop early if DCP crashed during the wait. If the RTKit crash callback fires while we wait, return right after the timeout instead of queueing abort_swaps (RTKit refuses messages to a crashed co-processor) and waiting another second. This is the same exit the function took on any timeout before, so callers already handle it.

How a real crash is handled is unchanged: dcp_rtk_crashed() sets dcp->crashed and disconnects the connector, and dcp_crtc_atomic_check() rejects every commit.

History: the 50 ms wait with crashed = true is in the original "drm/apple: Add DCP display driver" commit (f2bc92ff49a9) with no stated reason, and it most likely predates external DP hotplug.

Trade-off: if the firmware hangs without RTKit reporting a crash, each poweroff now stalls for up to about 2 s instead of the pipe being rejected right away. The screen is dark either way, but this version recovers if the firmware responds again.

Not addressed here: the DRM hotplug event from the Type-C disconnect is sent before DCP has processed the unplug, so the compositor's disable always queues behind the firmware teardown.

Testing

On 7.1.13-fd-1-ARCH (fairydust ce9f2eba72c0), 4 boots, with and without a second USB-C dock attached:

  • Clear swap completed after 20–53 ms over 8 unplugs, with no timeouts. One took 53 ms, which the old 50 ms wait would have turned into a dead pipe.
  • Every monitor replug got a modeset (9 of 9).
  • Builds without new warnings, and checkpatch is clean.

#634 also describes a second failure with the same symptom (the port's cd321x stops entering DP altmode). That one is upstream of appledrm and is not fixed here.

On DP unplug the DCP firmware first tears down the link on its own
(HPD removal, M3 power down, set_device_enabled 1 -> 0) and only then
processes the clear swap queued by iomfb_poweroff(). That teardown was
measured at ~53 ms, just above the 50 ms the driver waited. The timeout
silently set dcp->crashed, after which dcp_crtc_atomic_check() rejects
every commit on the CRTC with -EINVAL until reboot: the monitor is
detected on the next plug but never gets a modeset (black screen), and
the compositor cannot even disable the output.

Wait up to 1000 ms, matching the setPowerState(0) wait below. Even
then, a clear swap queued behind a slow operation (unplug during a
modeset, or right after resume where powering on took about a second)
can miss the deadline, so don't treat a timeout as a crash at all: warn
and continue powering off, like the setPowerState(0) wait does. Real
firmware crashes are reported via dcp_rtk_crashed(). A late reply is
safe since the swap cookie is refcounted.

Link: AsahiLinux#634
Signed-off-by: Alexey Makhov <makhov.alex@gmail.com>
If the RTKit crashed callback fires while iomfb_poweroff() waits for the
clear swap, the wait runs into its timeout and the function then queues
abort_swaps, which RTKit refuses for a crashed co-processor, and waits
another second for a reply that cannot arrive.

Return right after the timeout when dcp->crashed is set. This is the
same exit the function took on any clear swap timeout before, so callers
already handle it. The unlocked read of dcp->crashed matches
dcp_crtc_atomic_check(); a stale value only means taking the previous,
slower but safe path.

Signed-off-by: Alexey Makhov <makhov.alex@gmail.com>
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