diff --git a/local/patches/mesa/26-cs-submit-bidirectional-seqno.patch b/local/patches/mesa/26-cs-submit-bidirectional-seqno.patch new file mode 100644 index 0000000000..4ed5a696b4 --- /dev/null +++ b/local/patches/mesa/26-cs-submit-bidirectional-seqno.patch @@ -0,0 +1,135 @@ +--- a/src/gallium/winsys/redox/drm/redox_drm_cs.c ++++ b/src/gallium/winsys/redox/drm/redox_drm_cs.c +@@ -5,14 +5,17 @@ + * local/recipes/gpu/redox-drm/source/src/scheme.rs + * local/recipes/gpu/redox-drm/source/src/driver.rs + * +- * Two ioctls are used: ++ * Two ioctls are used, both following the standard DRM bidirectional-ioctl ++ * pattern: the caller fills the input fields, passes the struct to drmIoctl, ++ * and the kernel writes the response fields back into the SAME struct. ++ * + * - REDOX_PRIVATE_CS_SUBMIT (DRM_IOCTL_BASE + 31) +- * in: src_handle (gem), dst_handle (gem, ignored for now), +- * src_offset (offset into src buffer), byte_count (DWORDs to submit) ++ * in: src_handle (gem), dst_handle (gem, ignored for now), ++ * src_offset, dst_offset, byte_count + * out: seqno (u64) — the kernel-assigned submission id + * - REDOX_PRIVATE_CS_WAIT (DRM_IOCTL_BASE + 32) +- * in: seqno, timeout_ns +- * out: completed (bool), completed_seqno (u64) ++ * in: seqno, timeout_ns ++ * out: completed (u8), completed_seqno (u64) + * + * The kernel reads `byte_count` u32 dwords from the mapped GEM at + * `src_handle + src_offset` and writes them into the GPU render ring. +@@ -20,6 +23,12 @@ + * We invoke these ioctls via `drmIoctl` on the winsys fd. libdrm's + * redox patch redirects the ioctls to /scheme:drm/card0. + * ++ * The seqno returned by SUBMIT is the kernel's global per-device counter. ++ * It must NOT be faked with a local counter: under multi-process GPU use ++ * (compositor + multiple clients sharing /scheme/drm/card0) a local counter ++ * diverges from the kernel's namespace and fence waits keyed on it never ++ * complete. ++ * + * SPDX-License-Identifier: MIT + */ + +@@ -51,7 +60,12 @@ + #include "util/u_debug.h" + + /* Wire format of the kernel ABI structs. MUST match the kernel definitions +- * (kernel's GemHandle = u32; the structs are #[repr(C)] in Rust). ++ * in local/recipes/gpu/redox-drm/source/src/driver.rs (#[repr(C)]). ++ * ++ * Both structs are bidirectional: the caller fills the input fields, and the ++ * kernel writes the response fields back into the same struct on return. ++ * There is no separate result struct on the wire — the kernel serializes the ++ * same struct (with response fields populated) back to userspace. + */ + struct RedoxCsSubmitWire { + uint32_t src_handle; +@@ -59,20 +73,15 @@ + uint64_t src_offset; + uint64_t dst_offset; + uint64_t byte_count; +-}; +- +-struct RedoxCsSubmitResultWire { +- uint64_t seqno; ++ uint64_t seqno; /* response: kernel-assigned submission id */ + }; + + struct RedoxCsWaitWire { + uint64_t seqno; + uint64_t timeout_ns; +-}; +- +-struct RedoxCsWaitResultWire { +- uint8_t completed; +- uint64_t completed_seqno; ++ uint8_t completed; /* response: non-zero if seqno completed */ ++ uint8_t _pad[7]; /* align completed_seqno to 8 bytes */ ++ uint64_t completed_seqno; /* response: highest completed seqno */ + }; + + uint64_t +@@ -81,7 +90,6 @@ + uint32_t batch_dword_count) + { + struct RedoxCsSubmitWire submit = {0}; +- struct RedoxCsSubmitResultWire result = {0}; + size_t byte_count; + void *mapped; + int err; +@@ -143,30 +151,23 @@ + return 0; + } + +- /* The ioctl is the kernel-side drmIoctl wrapper; the actual submit +- * returns the seqno through a separate read (DRM protocol on Redox +- * uses a write-then-read pattern). For now, this uses a placeholder +- * ioctl that returns the result synchronously. +- * +- * TODO: integrate with the actual scheme:drm request/response +- * protocol by calling drmCommandWriteRead or by using a dedicated +- * 'ioctl with response' helper once the recipe-side IOCTL +- * interface supports that mode. ++ /* The kernel writes the assigned seqno back into submit.seqno ++ * (bidirectional ioctl — no separate response struct on the wire). ++ * This is the kernel's global per-device counter, correct under ++ * multi-process GPU use where a local counter would diverge. + */ +- result.seqno = rws->cs ? rws->cs->last_seqno + 1 : 1; + + /* Drop the batch BO; the kernel has copied from it into the ring. */ + redox_drm_bo_unmap(rws, batch_pr); + redox_drm_bo_destroy(rws, batch_pr); + +- return result.seqno; ++ return submit.seqno; + } + + uint64_t + redox_drm_cs_query_last_seqno(struct redox_drm_winsys *rws) + { + struct RedoxCsWaitWire wait = {0}; +- struct RedoxCsWaitResultWire result = {0}; + int err; + + if (!rws) +@@ -184,10 +185,10 @@ + return rws->cs ? rws->cs->last_seqno : 0; + } + +- /* Same write-then-read caveat as submit; for now the placeholder +- * ioctl fills in result synchronously. ++ /* The kernel writes completed_seqno back into the wait struct ++ * (bidirectional ioctl — same struct for request and response). + */ +- return result.completed_seqno; ++ return wait.completed_seqno; + } + + struct redox_drm_cs * diff --git a/local/recipes/gpu/redox-drm/source/src/driver.rs b/local/recipes/gpu/redox-drm/source/src/driver.rs index e4f87e40b1..b43d3767c0 100644 --- a/local/recipes/gpu/redox-drm/source/src/driver.rs +++ b/local/recipes/gpu/redox-drm/source/src/driver.rs @@ -11,27 +11,47 @@ pub enum DriverEvent { Hotplug { connector_id: u32 }, } +/// Bidirectional CS submit wire struct, mirroring the standard DRM +/// bidirectional-ioctl pattern (see `DrmAmdgpuCsWire` in scheme.rs). +/// The caller fills the input fields (`src_handle` … `byte_count`); +/// the kernel writes the assigned sequence number back into `seqno` +/// in the same struct — there is no separate response struct on the wire. #[repr(C)] -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Default)] pub struct RedoxPrivateCsSubmit { pub src_handle: GemHandle, pub dst_handle: GemHandle, pub src_offset: u64, pub dst_offset: u64, pub byte_count: u64, + /// Response — kernel-assigned submission sequence number. + /// Written by the kernel; callers read it after the ioctl returns. + pub seqno: u64, } +/// Result type returned by the `GpuDriver::redox_private_cs_submit` +/// trait method. Retained as the trait return type; the wire response +/// is carried in the bidirectional `RedoxPrivateCsSubmit.seqno` field. #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct RedoxPrivateCsSubmitResult { pub seqno: u64, } +/// Bidirectional CS wait wire struct. The caller fills `seqno` and +/// `timeout_ns`; the kernel writes the completion status back into +/// `completed` and `completed_seqno` in the same struct. #[repr(C)] -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Default)] pub struct RedoxPrivateCsWait { pub seqno: u64, pub timeout_ns: u64, + /// Response — non-zero if the waited-for seqno has completed. + pub completed: u8, + /// Padding so `completed_seqno` is naturally aligned to 8 bytes. + pub _pad: [u8; 7], + /// Response — highest completed seqno at the time of the call. + pub completed_seqno: u64, } #[repr(C)] @@ -624,9 +644,9 @@ mod tests { #[test] fn redox_private_cs_submit_size() { - // src_handle(u32) + dst_handle(u32) + src_offset(u64) + dst_offset(u64) + byte_count(u64) - // = 4 + 4 + 8 + 8 + 8 = 32 bytes - assert_eq!(size_of::(), 32); + // src_handle(u32) + dst_handle(u32) + src_offset(u64) + dst_offset(u64) + // + byte_count(u64) + seqno(u64) = 4 + 4 + 8 + 8 + 8 + 8 = 40 bytes + assert_eq!(size_of::(), 40); } #[test] @@ -637,8 +657,9 @@ mod tests { #[test] fn redox_private_cs_wait_size() { - // seqno(u64) + timeout_ns(u64) = 16 bytes - assert_eq!(size_of::(), 16); + // seqno(u64) + timeout_ns(u64) + completed(u8) + _pad([u8;7]) + completed_seqno(u64) + // = 8 + 8 + 8 + 8 = 32 bytes + assert_eq!(size_of::(), 32); } #[test] @@ -694,12 +715,15 @@ mod tests { assert_eq!(offset_of!(RedoxPrivateCsSubmit, src_offset), 8); assert_eq!(offset_of!(RedoxPrivateCsSubmit, dst_offset), 16); assert_eq!(offset_of!(RedoxPrivateCsSubmit, byte_count), 24); + assert_eq!(offset_of!(RedoxPrivateCsSubmit, seqno), 32); } #[test] fn redox_private_cs_wait_is_repr_c() { assert_eq!(offset_of!(RedoxPrivateCsWait, seqno), 0); assert_eq!(offset_of!(RedoxPrivateCsWait, timeout_ns), 8); + assert_eq!(offset_of!(RedoxPrivateCsWait, completed), 16); + assert_eq!(offset_of!(RedoxPrivateCsWait, completed_seqno), 24); } #[test] diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/amd/mod.rs b/local/recipes/gpu/redox-drm/source/src/drivers/amd/mod.rs index 37354f93bc..8c1230ac3d 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/amd/mod.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/amd/mod.rs @@ -570,6 +570,7 @@ impl GpuDriver for AmdDriver { src_offset: 0, dst_offset: 0, byte_count: 0, + ..Default::default() }; let result = self.redox_private_cs_submit(&submit)?; Ok(result.seqno) diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs index d4a178f4d5..4b201a8133 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs @@ -805,6 +805,7 @@ impl GpuDriver for IntelDriver { src_offset: batch_start_offset, dst_offset: 0, byte_count: batch_len as u64, + ..Default::default() }; let result = self.redox_private_cs_submit(&submit)?; Ok(result.seqno) diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/virtio/mod.rs b/local/recipes/gpu/redox-drm/source/src/drivers/virtio/mod.rs index 6b1e0be1e8..3907c8e138 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/virtio/mod.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/virtio/mod.rs @@ -925,6 +925,7 @@ mod tests { .redox_private_cs_wait(&RedoxPrivateCsWait { seqno: 41, timeout_ns: 0, + ..Default::default() }) .unwrap(); assert!(result.completed); diff --git a/local/recipes/gpu/redox-drm/source/src/scheme.rs b/local/recipes/gpu/redox-drm/source/src/scheme.rs index aa738db139..02c5f87a5a 100644 --- a/local/recipes/gpu/redox-drm/source/src/scheme.rs +++ b/local/recipes/gpu/redox-drm/source/src/scheme.rs @@ -1152,6 +1152,7 @@ impl DrmScheme { let req = crate::driver::RedoxPrivateCsWait { seqno: u64::MAX, timeout_ns: 0, + ..Default::default() }; match self.driver.redox_private_cs_wait(&req) { Ok(r) => r.completed_seqno, @@ -2155,6 +2156,7 @@ impl DrmScheme { src_offset: req.src_offset, dst_offset: req.dst_offset, byte_count: req.size, + ..Default::default() }; self.validate_private_cs_ranges(&submit, "AMD SDMA submit")?; req.seqno = self @@ -2356,6 +2358,7 @@ impl DrmScheme { .redox_private_cs_wait(&RedoxPrivateCsWait { seqno: req.seqno, timeout_ns: req.timeout_ns, + ..Default::default() }) .map_err(driver_to_syscall)?; req.completed = u32::from(result.completed); @@ -2433,7 +2436,7 @@ impl DrmScheme { } DRM_IOCTL_REDOX_PRIVATE_CS_SUBMIT => { - let req = decode_wire::(payload)?; + let mut req = decode_wire::(payload)?; self.validate_private_cs_handles( id, req.src_handle, @@ -2445,19 +2448,22 @@ impl DrmScheme { .driver .redox_private_cs_submit(&req) .map_err(driver_to_syscall)?; + req.seqno = resp.seqno; if let Some(handle) = self.handles.get_mut(&id) { handle.last_submitted_seqno = resp.seqno; } - bytes_of(&resp) + bytes_of(&req) } DRM_IOCTL_REDOX_PRIVATE_CS_WAIT => { - let req = decode_wire::(payload)?; + let mut req = decode_wire::(payload)?; let resp: RedoxPrivateCsWaitResult = self .driver .redox_private_cs_wait(&req) .map_err(driver_to_syscall)?; - bytes_of(&resp) + req.completed = u8::from(resp.completed); + req.completed_seqno = resp.completed_seqno; + bytes_of(&req) } // REDOX_SCANOUT_FLIP — flip the framebuffer on a CRTC. @@ -2909,6 +2915,7 @@ impl SchemeSync for DrmScheme { .redox_private_cs_wait(&RedoxPrivateCsWait { seqno, timeout_ns: FSYNC_TIMEOUT_NS, + ..Default::default() }) .map_err(driver_to_syscall)?; @@ -3393,6 +3400,7 @@ mod tests { src_offset: 0, dst_offset: 0, byte_count: 64, + ..Default::default() }; let err = write_ioctl( &mut scheme, @@ -3448,6 +3456,7 @@ mod tests { let wait = RedoxPrivateCsWait { seqno: 1, timeout_ns: 0, + ..Default::default() }; let err = @@ -3480,9 +3489,10 @@ mod tests { src_offset: 0, dst_offset: 0, byte_count: 128, + ..Default::default() }; write_ioctl(&mut scheme, card, DRM_IOCTL_REDOX_PRIVATE_CS_SUBMIT, &submit).unwrap(); - let _ = read_response::(&mut scheme, card); + let _ = read_response::(&mut scheme, card); scheme.fsync(card).unwrap(); } @@ -3512,6 +3522,7 @@ mod tests { src_offset: 0, dst_offset: 0, byte_count: 128, + ..Default::default() }; write_ioctl( @@ -3521,7 +3532,7 @@ mod tests { &submit, ) .unwrap(); - let response = read_response::(&mut scheme, card); + let response = read_response::(&mut scheme, card); assert_eq!(response.seqno, 7); assert_eq!(driver.submit_calls(), 1); @@ -3549,6 +3560,7 @@ mod tests { src_offset: 4090, dst_offset: 0, byte_count: 64, + ..Default::default() }; let err = write_ioctl( diff --git a/local/recipes/libs/mesa/recipe.toml b/local/recipes/libs/mesa/recipe.toml index 61ed6944a0..47320013bf 100644 --- a/local/recipes/libs/mesa/recipe.toml +++ b/local/recipes/libs/mesa/recipe.toml @@ -19,6 +19,11 @@ patches = [ # (enables with_dri → with_egl/gbm) and to the _GNU_SOURCE platform list. # Was an untracked in-place edit lost on every clean re-extract. "mesa/08-meson-redox-kms-drm.patch", + # Fix the CS submit seqno bug: merge the separate input/result wire structs + # into bidirectional structs so the kernel's global per-device seqno is + # read back instead of faked with a local counter. Required for correct + # multi-process GPU fence tracking (compositor + GPU clients). + "mesa/26-cs-submit-bidirectional-seqno.patch", ] [build] template = "custom" diff --git a/local/recipes/libs/mesa/source/src/gallium/winsys/redox/drm/redox_drm_cs.c b/local/recipes/libs/mesa/source/src/gallium/winsys/redox/drm/redox_drm_cs.c index 6440df45cf..f814a2bd00 100644 --- a/local/recipes/libs/mesa/source/src/gallium/winsys/redox/drm/redox_drm_cs.c +++ b/local/recipes/libs/mesa/source/src/gallium/winsys/redox/drm/redox_drm_cs.c @@ -5,14 +5,17 @@ * local/recipes/gpu/redox-drm/source/src/scheme.rs * local/recipes/gpu/redox-drm/source/src/driver.rs * - * Two ioctls are used: + * Two ioctls are used, both following the standard DRM bidirectional-ioctl + * pattern: the caller fills the input fields, passes the struct to drmIoctl, + * and the kernel writes the response fields back into the SAME struct. + * * - REDOX_PRIVATE_CS_SUBMIT (DRM_IOCTL_BASE + 31) - * in: src_handle (gem), dst_handle (gem, ignored for now), - * src_offset (offset into src buffer), byte_count (DWORDs to submit) + * in: src_handle (gem), dst_handle (gem, ignored for now), + * src_offset, dst_offset, byte_count * out: seqno (u64) — the kernel-assigned submission id * - REDOX_PRIVATE_CS_WAIT (DRM_IOCTL_BASE + 32) - * in: seqno, timeout_ns - * out: completed (bool), completed_seqno (u64) + * in: seqno, timeout_ns + * out: completed (u8), completed_seqno (u64) * * The kernel reads `byte_count` u32 dwords from the mapped GEM at * `src_handle + src_offset` and writes them into the GPU render ring. @@ -20,6 +23,12 @@ * We invoke these ioctls via `drmIoctl` on the winsys fd. libdrm's * redox patch redirects the ioctls to /scheme:drm/card0. * + * The seqno returned by SUBMIT is the kernel's global per-device counter. + * It must NOT be faked with a local counter: under multi-process GPU use + * (compositor + multiple clients sharing /scheme/drm/card0) a local counter + * diverges from the kernel's namespace and fence waits keyed on it never + * complete. + * * SPDX-License-Identifier: MIT */ @@ -51,7 +60,12 @@ #include "util/u_debug.h" /* Wire format of the kernel ABI structs. MUST match the kernel definitions - * (kernel's GemHandle = u32; the structs are #[repr(C)] in Rust). + * in local/recipes/gpu/redox-drm/source/src/driver.rs (#[repr(C)]). + * + * Both structs are bidirectional: the caller fills the input fields, and the + * kernel writes the response fields back into the same struct on return. + * There is no separate result struct on the wire — the kernel serializes the + * same struct (with response fields populated) back to userspace. */ struct RedoxCsSubmitWire { uint32_t src_handle; @@ -59,20 +73,15 @@ struct RedoxCsSubmitWire { uint64_t src_offset; uint64_t dst_offset; uint64_t byte_count; -}; - -struct RedoxCsSubmitResultWire { - uint64_t seqno; + uint64_t seqno; /* response: kernel-assigned submission id */ }; struct RedoxCsWaitWire { uint64_t seqno; uint64_t timeout_ns; -}; - -struct RedoxCsWaitResultWire { - uint8_t completed; - uint64_t completed_seqno; + uint8_t completed; /* response: non-zero if seqno completed */ + uint8_t _pad[7]; /* align completed_seqno to 8 bytes */ + uint64_t completed_seqno; /* response: highest completed seqno */ }; uint64_t @@ -81,7 +90,6 @@ redox_drm_cs_submit(struct redox_drm_winsys *rws, uint32_t batch_dword_count) { struct RedoxCsSubmitWire submit = {0}; - struct RedoxCsSubmitResultWire result = {0}; size_t byte_count; void *mapped; int err; @@ -143,30 +151,23 @@ redox_drm_cs_submit(struct redox_drm_winsys *rws, return 0; } - /* The ioctl is the kernel-side drmIoctl wrapper; the actual submit - * returns the seqno through a separate read (DRM protocol on Redox - * uses a write-then-read pattern). For now, this uses a placeholder - * ioctl that returns the result synchronously. - * - * TODO: integrate with the actual scheme:drm request/response - * protocol by calling drmCommandWriteRead or by using a dedicated - * 'ioctl with response' helper once the recipe-side IOCTL - * interface supports that mode. + /* The kernel writes the assigned seqno back into submit.seqno + * (bidirectional ioctl — no separate response struct on the wire). + * This is the kernel's global per-device counter, correct under + * multi-process GPU use where a local counter would diverge. */ - result.seqno = rws->cs ? rws->cs->last_seqno + 1 : 1; /* Drop the batch BO; the kernel has copied from it into the ring. */ redox_drm_bo_unmap(rws, batch_pr); redox_drm_bo_destroy(rws, batch_pr); - return result.seqno; + return submit.seqno; } uint64_t redox_drm_cs_query_last_seqno(struct redox_drm_winsys *rws) { struct RedoxCsWaitWire wait = {0}; - struct RedoxCsWaitResultWire result = {0}; int err; if (!rws) @@ -184,10 +185,10 @@ redox_drm_cs_query_last_seqno(struct redox_drm_winsys *rws) return rws->cs ? rws->cs->last_seqno : 0; } - /* Same write-then-read caveat as submit; for now the placeholder - * ioctl fills in result synchronously. + /* The kernel writes completed_seqno back into the wait struct + * (bidirectional ioctl — same struct for request and response). */ - return result.completed_seqno; + return wait.completed_seqno; } struct redox_drm_cs * diff --git a/local/sources/relibc b/local/sources/relibc index 27397a8ca1..79685cbe02 160000 --- a/local/sources/relibc +++ b/local/sources/relibc @@ -1 +1 @@ -Subproject commit 27397a8ca12513e06d7495a3a3b06d0ab2d2e7ad +Subproject commit 79685cbe02c912e65e4cf0880a61542f14766ce0