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 8c1230ac3d..f30213eaed 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 @@ -2,7 +2,7 @@ pub mod display; pub mod gtt; pub mod ring; -use std::collections::HashMap; +use std::collections::{BTreeMap, BTreeSet, HashMap}; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use std::sync::Mutex; @@ -62,6 +62,9 @@ pub struct AmdDriver { next_vm_id: AtomicU64, next_bo_list_id: AtomicU64, next_fence_handle_id: AtomicU64, + bo_seqnos: Mutex>, + fence_eventfds: Mutex>, + signalled_fences: Mutex>, } impl AmdDriver { @@ -165,6 +168,9 @@ impl AmdDriver { vblank_count: AtomicU64::new(0), hotplug_pending: AtomicBool::new(false), next_ctx_id: AtomicU64::new(1), + bo_seqnos: Mutex::new(BTreeMap::new()), + fence_eventfds: Mutex::new(BTreeMap::new()), + signalled_fences: Mutex::new(BTreeSet::new()), next_vm_id: AtomicU64::new(1), next_bo_list_id: AtomicU64::new(1), next_fence_handle_id: AtomicU64::new(1), @@ -485,11 +491,24 @@ impl GpuDriver for AmdDriver { } fn gem_create(&self, size: u64) -> Result { - let mut gem = self - .gem - .lock() - .map_err(|_| DriverError::Buffer("GEM manager poisoned".to_string()))?; - gem.create(size) + let handle = { + let mut gem = self + .gem + .lock() + .map_err(|_| DriverError::Buffer("GEM manager poisoned".to_string()))?; + gem.create(size)? + }; + + if let Err(error) = self.ensure_gem_gpu_mapping(handle) { + let _ = self + .gem + .lock() + .map_err(|_| DriverError::Buffer("GEM manager poisoned".to_string()))? + .close(handle); + return Err(error); + } + + Ok(handle) } fn gem_close(&self, handle: GemHandle) -> Result<()> { @@ -547,87 +566,245 @@ impl GpuDriver for AmdDriver { fn amdgpu_ctx( &self, - _op: u32, + op: u32, ctx_id: &mut u32, _param: u64, ) -> Result<()> { - *ctx_id = self.next_ctx_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst); - Ok(()) + const AMDGPU_CTX_OP_ALLOC_CTX: u32 = 0; + const AMDGPU_CTX_OP_FREE_CTX: u32 = 1; + const AMDGPU_CTX_OP_SETPARAM_PERSO: u32 = 4; + const AMDGPU_CTX_OP_SETPARAM_PREAMBLE_LOCATION: u32 = 5; + const AMDGPU_CTX_OP_SETPARAM_RESET_GPU: u32 = 7; + const AMDGPU_CTX_OP_SETPARAM_QUERY_STATE: u32 = 8; + const AMDGPU_CTX_OP_SETPARAM_RUNALU: u32 = 9; + + match op { + AMDGPU_CTX_OP_ALLOC_CTX => { + *ctx_id = self.next_ctx_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst) as u32; + Ok(()) + } + AMDGPU_CTX_OP_FREE_CTX + | AMDGPU_CTX_OP_SETPARAM_PERSO + | AMDGPU_CTX_OP_SETPARAM_PREAMBLE_LOCATION + | AMDGPU_CTX_OP_SETPARAM_RESET_GPU + | AMDGPU_CTX_OP_SETPARAM_QUERY_STATE + | AMDGPU_CTX_OP_SETPARAM_RUNALU => { + Ok(()) + } + _ => Err(DriverError::InvalidArgument("amdgpu_ctx unknown op")), + } } fn amdgpu_cs( &self, - _ctx_id: u32, - _bo_handles: &[u32], - _cmd_dwords: &[u32], + ctx_id: u32, + bo_handles: &[u32], + cmd_dwords: &[u32], ) -> Result { - // Submit a no-op CS packet; the SDMA ring's idle state - // means the seqno is reached immediately. Mesa's radeonsi - // treats the returned seqno as the fence for this submission. + let dword_count = cmd_dwords.len(); + if dword_count == 0 || dword_count > 1024 { + return Err(DriverError::InvalidArgument("amdgpu_cs dword count out of range")); + } + let src_handle = bo_handles.first().copied().unwrap_or(0); let submit = crate::driver::RedoxPrivateCsSubmit { - src_handle: GemHandle(0), - dst_handle: GemHandle(0), + src_handle, + dst_handle: 0, src_offset: 0, dst_offset: 0, - byte_count: 0, + byte_count: (dword_count * core::mem::size_of::()) as u64, ..Default::default() }; let result = self.redox_private_cs_submit(&submit)?; + + if !bo_handles.is_empty() { + let mut seqnos = self + .bo_seqnos + .lock() + .map_err(|_| DriverError::Buffer("AMD BO seqno table poisoned".to_string()))?; + for handle in bo_handles { + seqnos.insert(*handle, result.seqno); + } + } + let _ = ctx_id; + + self.signal_completed_fences(); + Ok(result.seqno) } fn amdgpu_vm( &self, - _op: u32, + op: u32, vm_id: &mut u32, ) -> Result<()> { - *vm_id = self.next_vm_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst); - Ok(()) + const AMDGPU_VM_OP_ALLOC_VM: u32 = 0; + const AMDGPU_VM_OP_FREE_VM: u32 = 1; + const AMDGPU_VM_OP_MAP_DROPPABLE: u32 = 2; + const AMDGPU_VM_OP_UPDATE_PARAMETERS: u32 = 3; + const AMDGPU_VM_OP_SET_PASID: u32 = 4; + match op { + AMDGPU_VM_OP_ALLOC_VM => { + *vm_id = self.next_vm_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst) as u32; + Ok(()) + } + AMDGPU_VM_OP_FREE_VM + | AMDGPU_VM_OP_MAP_DROPPABLE + | AMDGPU_VM_OP_UPDATE_PARAMETERS + | AMDGPU_VM_OP_SET_PASID => Ok(()), + _ => Err(DriverError::InvalidArgument("amdgpu_vm unknown op")), + } } fn amdgpu_bo_list( &self, - _op: u32, + op: u32, list_handle: &mut u32, - _bo_handles: &[u32], + bo_handles: &[u32], ) -> Result<()> { - *list_handle = self.next_bo_list_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst); - Ok(()) + const AMDGPU_BO_LIST_OP_CREATE: u32 = 0; + const AMDGPU_BO_LIST_OP_DESTROY: u32 = 2; + match op { + AMDGPU_BO_LIST_OP_CREATE => { + *list_handle = self.next_bo_list_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst) as u32; + let _ = bo_handles; + Ok(()) + } + AMDGPU_BO_LIST_OP_DESTROY => Ok(()), + _ => Err(DriverError::InvalidArgument("amdgpu_bo_list unknown op")), + } } fn amdgpu_wait_fences( &self, - _handles: &[u64], - _timeout_ns: u64, + handles: &[u64], + timeout_ns: u64, _flags: u32, ) -> Result { - Ok(true) + if handles.is_empty() { + return Ok(true); + } + let target = *handles.iter().max().unwrap(); + self.redox_private_cs_wait(&crate::driver::RedoxPrivateCsWait { + seqno: target, + timeout_ns, + ..Default::default() + }) + .map(|r| r.completed) } fn amdgpu_info( &self, - _query_id: u32, - _data: &mut [u8], + query_id: u32, + data: &mut [u8], ) -> Result<()> { - Ok(()) + const AMDGPU_INFO_DEV_INFO: u32 = 3; + match query_id { + AMDGPU_INFO_DEV_INFO => { + if data.len() >= 76 { + data.fill(0); + let device_id = self.info.device_id; + let vendor_id = self.info.vendor_id; + data[0..2].copy_from_slice(&device_id.to_ne_bytes()); + data[2..4].copy_from_slice(&vendor_id.to_ne_bytes()); + data[4..8].copy_from_slice(&1u32.to_ne_bytes()); + } + Ok(()) + } + _ => { + data.fill(0); + Ok(()) + } + } } fn amdgpu_fence_to_handle( &self, _fence: u64, - _flags: u32, + flags: u32, handle: &mut u32, ) -> Result<()> { - *handle = self.next_fence_handle_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst); - Ok(()) + const AMDGPU_FENCE_TO_HANDLE_GET_SYNCOBJ: u32 = 0; + const AMDGPU_FENCE_TO_HANDLE_GET_SYNCOBJ_FD: u32 = 1; + const AMDGPU_FENCE_TO_HANDLE_GET_FD: u32 = 2; + match flags & 0xff { + AMDGPU_FENCE_TO_HANDLE_GET_SYNCOBJ + | AMDGPU_FENCE_TO_HANDLE_GET_SYNCOBJ_FD + | AMDGPU_FENCE_TO_HANDLE_GET_FD => { + *handle = self.next_fence_handle_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst) as u32; + Ok(()) + } + _ => Err(DriverError::InvalidArgument("amdgpu_fence_to_handle unknown flags")), + } } fn register_fence_eventfd( &self, - _userland_eventfd: i32, - _seqno: u64, + userland_eventfd: i32, + seqno: u64, ) -> Result { - Ok(-1) + if userland_eventfd < 0 { + return Err(DriverError::InvalidArgument( + "register_fence_eventfd requires a valid eventfd", + )); + } + let kernel_fd = unsafe { libc::dup(userland_eventfd) }; + if kernel_fd < 0 { + return Err(DriverError::Io(format!( + "register_fence_eventfd dup failed: {}", + std::io::Error::last_os_error() + ))); + } + self.fence_eventfds + .lock() + .map_err(|_| DriverError::Buffer("AMD fence eventfd table poisoned".to_string()))? + .insert(seqno, kernel_fd); + self.signalled_fences + .lock() + .map_err(|_| DriverError::Buffer("AMD signalled fence set poisoned".to_string()))? + .insert(seqno); + Ok(kernel_fd) + } + + fn signal_completed_fences(&self) { + let Ok(mut ring) = self.ring.lock() else { + return; + }; + if ring.sync_from_hw().is_err() { + return; + } + let hw_seqno = ring.last_seqno(); + drop(ring); + + let mut fences = match self.fence_eventfds.lock() { + Ok(f) => f, + Err(_) => return, + }; + let pending: Vec = fences + .keys() + .copied() + .filter(|seq| *seq <= hw_seqno) + .collect(); + for seq in pending { + if let Some(fd) = fences.remove(&seq) { + let payload: u64 = 1; + let written = unsafe { libc::write(fd, &payload as *const u64 as *const _, 8) }; + if written < 0 { + warn!( + "redox-drm: AMD fence eventfd write failed for seqno={}, fd={}, {}", + seq, + fd, + std::io::Error::last_os_error() + ); + } else { + self.signalled_fences + .lock() + .map_err(|_| DriverError::Buffer("AMD signalled fence set poisoned".to_string())) + .ok() + .map(|mut s| s.remove(&seq)); + } + unsafe { libc::close(fd) }; + } + } } fn get_edid(&self, connector_id: u32) -> Vec {