From 8c6eac5bb8cb44d5e4c3544ce60910d5a6aada49 Mon Sep 17 00:00:00 2001 From: Adam Ford Date: Tue, 5 May 2026 10:46:52 -0500 Subject: [PATCH] rutabaga_gfx/cross_domain: handle CMD_WRITE on Eventfd items MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cross_domain `write` handler only matched `CrossDomainItem::WaylandWritePipe`, falling into the catch-all for every other item type after the unconditional `remove()` at the top of the function had already dropped the entry from the table. PipeWire (and other clients that share host-created eventfds via SCM_RIGHTS for per-period wakeups) sends CMD_WRITE on those eventfd identifiers — the first such write returns InvalidCrossDomainItemType *after* removing the item, and every subsequent write on the same identifier returns InvalidCrossDomainItemId, masquerading on the guest as the opaque VIRTIO_GPU_RESP_ERR_UNSPEC (0x1200). Reproduced inside a libkrun guest on x86_64 by routing PipeWire's ALSA-shim audio through a host PipeWire daemon. With a paired BT speaker as the sink, `speaker-test -D pipewire -c2 -t wav -l 1` produces, per stream, ~10 entries of: [ 0.682819] [drm:virtio_gpu_dequeue_ctrl_func] *ERROR* response 0x1200 (command 0x207) [ 0.723762] [drm:virtio_gpu_dequeue_ctrl_func] *ERROR* response 0x1200 (command 0x207) [ 0.767615] [drm:virtio_gpu_dequeue_ctrl_func] *ERROR* response 0x1200 (command 0x207) [ 0.807779] [drm:virtio_gpu_dequeue_ctrl_func] *ERROR* response 0x1200 (command 0x207) [ 0.852469] [drm:virtio_gpu_dequeue_ctrl_func] *ERROR* response 0x1200 (command 0x207) [ 0.896552] [drm:virtio_gpu_dequeue_ctrl_func] *ERROR* response 0x1200 (command 0x207) [ 0.936504] [drm:virtio_gpu_dequeue_ctrl_func] *ERROR* response 0x1200 (command 0x207) [ 0.980567] [drm:virtio_gpu_dequeue_ctrl_func] *ERROR* response 0x1200 (command 0x207) [ 1.024476] [drm:virtio_gpu_dequeue_ctrl_func] *ERROR* response 0x1200 (command 0x207) [ 1.064636] [drm:virtio_gpu_dequeue_ctrl_func] *ERROR* response 0x1200 (command 0x207) with audio still playing — PipeWire has socket-based fallback timing that doesn't depend on eventfd ack, so the failures are cosmetic for playback. They are not cosmetic for clients that strictly require the eventfd handshake (PipeWire's ALSA shim under heavier loads, and the buffer-pool wakeup path used by V4L2 capture streams). Add an Eventfd arm that mirrors the WaylandWritePipe semantics: `write_volatile` performs the 8-byte counter increment, and the item is re-inserted into the table unless the guest signaled `hang_up`. Verified post-fix: zero CMD_WRITE failures, zero `0x1200` entries in guest dmesg, audio playback unchanged. Camera capture (gst-launch pipewiresrc → MJPEG) also exercises this path for buffer-pool wakeups and runs cleanly with valid 98%-non-zero JPEG frames. Signed-off-by: Adam Ford --- src/rutabaga_gfx/src/cross_domain/mod.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/rutabaga_gfx/src/cross_domain/mod.rs b/src/rutabaga_gfx/src/cross_domain/mod.rs index 5a36f081e..81d7b71c7 100644 --- a/src/rutabaga_gfx/src/cross_domain/mod.rs +++ b/src/rutabaga_gfx/src/cross_domain/mod.rs @@ -991,6 +991,28 @@ impl CrossDomainContext { Ok(()) } + // Handle writes to Eventfd items. PipeWire (and other clients + // that pass eventfds via SCM_RIGHTS) uses these for per-period + // wakeups: an 8-byte write to the eventfd's counter signals + // the host. Without this arm, the first such write hits the + // catch-all below, returning InvalidCrossDomainItemType after + // the unconditional remove() above has already dropped the + // item — leaving every subsequent write to the same id + // failing with InvalidCrossDomainItemId. Mirrors the + // WaylandWritePipe re-insert semantics on hang_up == 0. + CrossDomainItem::Eventfd(file) => { + if len != 0 { + write_volatile(&file, opaque_data)?; + } + + if cmd_write.hang_up == 0 { + items + .table + .insert(cmd_write.identifier, CrossDomainItem::Eventfd(file)); + } + + Ok(()) + } _ => Err(RutabagaError::InvalidCrossDomainItemType), } }