Skip to content

drm: apple: av: let the AV endpoint's AFK slots be reused - #3

Closed
nekolyanich wants to merge 4 commits into
aurora-wipfrom
pr/dcp-av-afk-slots-v6
Closed

nekolyanich wants to merge 4 commits into
aurora-wipfrom
pr/dcp-av-afk-slots-v6

Conversation

@nekolyanich

Copy link
Copy Markdown
Owner

DCP announces the AV endpoint's two services (DCPAVSimpleVideoInterface and DCPAVAudioInterface) again on every display connect. Their ops are not reusable, so each connect takes two of the endpoint's 16 slots for good, and after eight connects in one boot the next display gets AFK[ep:29]: no reusable service slots and no audio service until a reboot. Before aurora-silicon#282 DCP also replugged the sink itself after refusing an audio start, which got there within minutes (seen 2026-10-11: from the 9th connect); with aurora-silicon#282 it takes eight real replugs, which a dock or a KVM switch still reaches. 5837706 kept unconverted owners like these non-reusable on purpose; this converts them, the same way, and builds on its afk_service_get/put. aurora-silicon#293's 7357e51 defers re-announced DPAV services on AFK v2; it does not reach the AFK v1 AV endpoint, and this series doesn't touch its path.

Commits:

  1. drm: apple: afk: complete a command whose reply is too short as failed: a reply shorter than the command header was dropped, so its caller waited the command out, and a command that had already timed out kept its buffers and the slot. It now completes as failed: with the reply's IOReturn if it is nonzero (the 4-byte replies look like a bare IOReturn; an assumption), else AFK_RETCODE_SHORT_REPLY. On our side (13.5 firmware, downstream trees) these were always 4 bytes: 28 times on the DCPDPDevice endpoint in 7 boots and once on the AV endpoint. This path is AFK v1 only: AFK v2 replies reach afk_recv_handle_reply() as a full synthetic header after afk_v2_receive() checked them, so they never take it. Not seen on aurora-wip, so it is untested here. b12111f (on satchlj/j613-25g83-gpu-dt-handoff, not in aurora-wip) handles the 14.7 short results on the std-service path.
  2. drm: apple: av: pin the AV services and let their AFK slots be reused: the audio service keeps an owner pin while asrv->srv points to it, dropped on teardown or, after afk_shutdown() on unbind, in the new av_service_detach(). An announce without a teardown (not seen) warns and drops the older pin. The open and close in av_service_connect()/av_service_disconnect(), made without srv_rwsem, take their own pin; they nest with the get/put that afk_send_command_timeout_len() and the v2 handlers now take. The video service keeps no pointer. Then both are marked reusable. The AV ops have no prepare, so afk_service_reinit() enables them as before and the owner pin in init succeeds.
  3. drm: apple: afk: log which slot a service gets: slot index and "(reused)" on the existing new service line. A slot freed by afk_service_discard() logs as not reused. Easy to drop.
  4. drm: apple: av: open each generation of the audio service: is_open becomes the generation that was opened, so a teardown during or before the disconnect no longer leaves the next generation unopened, and open_lock serialises the open and the close, which the hotplug, power and start paths call concurrently (two disconnects 0.7 ms apart, once). Found by reading; separate so it can be judged or dropped on its own.

Testing: MacBook Pro 16" M2 Pro (j416s), DCP firmware 13.5 (AFK v1), under the m1n1 hypervisor, with the bench-only DT change from aurora-silicon#276 (atc0 out of the DCP routes). A TV on a USB-C to HDMI adapter on dcpext0, a Dell over Thunderbolt on dcpext1. The kernel is aurora-wip (bb41967) with this series, aurora-silicon#276, aurora-silicon#277 and aurora-silicon#296; the series has since been rebased onto aurora-wip after aurora-silicon#293 (two textual conflicts in afk.c). An initramfs prompts me to replug the TV's adapter (16 times, 30 s after each plug) and starts 2 s of silence (2 ch, 24 bit) on the TV's card about every 7 s, 120 starts in all. Counts are for the TV's DCP (289c00000).

aurora-wip (2026-10-11, before aurora-silicon#282) with this PR
TV connects 10 17 (boot + 16 replugs, 16 of 16 reached the display, 0 lost)
AV service generations 8 each, then none 17 audio
slots used all 16 DCPAVSimpleVideoInterface slot 0 and DCPAVAudioInterface slot 1 (and AppleDCPDP2HDMI slots 0 and 1), each 1 new + 16 reused
no reusable service slots from the 9th connect 0
starts played in full – 95 of 120

All 95 starts that began and ended with the display connected played in full; the other 25 fell around the replugs, while the display was unplugged or not yet back (counted per window, not start by start; 5 of them are prepares that timed out). DCP logged "Capabilities changed" 4 times, each 0.5-1 s after an unplug (DCP's log shows a DPTX link fault before it restarts its AV interfaces), none at an audio start after a plug. No "announced again without a teardown", no short replies, no WARNING, no runtime PM underflow.

On the new base (aurora-wip after aurora-silicon#293, with this series as rebased, aurora-silicon#276, aurora-silicon#277 and aurora-silicon#296), two cold boots under the hypervisor with the TV and the Dell attached: both displays connected, 8 of 8 starts played in full on the TV, no "Capabilities changed", no WARNING, no no reusable service slots. No replugs there.

Not tested: natively on aurora-wip, other Macs, AFK v2 (H17P/J700), the 12.3 firmware command table, unbind with an audio service up, the short-reply path (commit 1). No KUnit cases yet; the short-reply completion and the owner pin could go next to afk-service-test.c if you want them.

Follow-up, not in this PR: dcp_audiosrv_* hold srv_rwsem for write across their AFK calls (up to 1 s each), so a teardown in the rx worker waits behind them, and the calls queued after it time out. Pinning under the read lock, as connect/disconnect do here, would fix that. And start_av_service_wq is not cancelled on unbind, so the start work can still open the service after av_service_disconnect() there.

nekolyanich added 4 commits October 14, 2026 07:44
afk_recv_handle_reply() dropped a reply shorter than the command header.
Its caller then waited the command out; if the command had already timed
out (free_on_ack), its DMA buffers and its cmd_map bit were never freed,
and the service's slot could not be reclaimed. Complete it instead, as
failed. The short replies seen are 4 bytes, which looks like a bare
IOReturn (an assumption): a nonzero one is passed on, anything else
completes with AFK_RETCODE_SHORT_REPLY (~0), so that no caller reads its
output buffer as a reply. AFK v2 replies reach this function with a
full header and are not affected.

Seen with 13.5 firmware on an M2 Pro (downstream trees), always 4 bytes:
28 times on the DCPDPDevice endpoint across 7 boots, the first in each
boot for a command still pending (whose caller then timed out), the rest
for commands that had already timed out; and once on the AV endpoint,
for an audio getElements stuck behind a blocked rx worker.

Assisted-by: Claude:claude-opus-5-5 checkpatch
Signed-off-by: nekolyanich <me@nekolyanich.com>
DCP announces the AV endpoint's services (DCPAVSimpleVideoInterface and
DCPAVAudioInterface) again on every display connect. They are not
reusable, so each connect takes two of the endpoint's 16 slots for good:
after eight connects in one boot the next display got "AFK[ep:29]: no
reusable service slots" and no audio service until a reboot (M2 Pro, a
TV on a USB-C to HDMI adapter, under the m1n1 hypervisor).

Convert their owners the way commit 5837706 ("drm/apple: pin DP
service owners before recycling retired AFK slots") did for the DP
service, on which this builds. The audio service keeps an owner pin
while asrv->srv points to it, released on its teardown or, after
afk_shutdown() on unbind, in av_service_detach(). An announce without a
teardown, not seen, warns and drops the older pin, and a late teardown
of the older one then leaves the live one alone. The open and close in
av_service_connect() and av_service_disconnect(), made without
srv_rwsem, pin it for the call. The close no longer disables a torn
down service itself: the teardown handler already clears enabled. The
video service keeps no pointer to it. Then mark both reusable.

Fixes: 02e1582 ("gpu: drm: apple: Set up client of AV endpoint")
Assisted-by: Claude:claude-opus-5-5 checkpatch
Signed-off-by: nekolyanich <me@nekolyanich.com>
Add the slot index to the "new service" line, and whether the slot was
used before, so slot reuse can be told from a table that is merely not
full yet.

Assisted-by: Claude:claude-opus-5-5 checkpatch
Signed-off-by: nekolyanich <me@nekolyanich.com>
is_open said that the audio service had been opened, but not which
announce of it: a teardown left it set, and if the display's disconnect
came after the teardown, av_service_disconnect() found no service and
returned without clearing it, so the next generation was never opened.
Keep the generation that was opened instead, counted at each announce,
and set it only if no teardown came during the open; a generation torn
down meanwhile is not connected to the audio device either. The open and
the close checked and changed the state without a lock, and the hotplug,
power and start paths call them concurrently (two disconnects 0.7 ms
apart seen once): serialise them with open_lock, which the AFK rx worker
never takes.

Found by reading, not seen to matter in our runs.

Assisted-by: Claude:claude-opus-5-5 checkpatch
Signed-off-by: nekolyanich <me@nekolyanich.com>
@nekolyanich

Copy link
Copy Markdown
Owner Author

Published as aurora-silicon#299

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