redox-drm Intel: real i915 UAPI bridge implementations
Replace the stub no-op i915 bridge methods with real implementations: * i915_gem_set_tiling: validates tiling_mode (0..=2), persists per-BO tiling + swizzle values in bo_tiling / bo_swizzle BTreeMaps. * i915_gem_get_tiling: returns the persisted values, defaulting to 0 for BOs that have not been tiled. * i915_gem_set_domain: on write_domain == CPU, flushes the GGTT (gtt.flush) before the userland writes the BO so the GPU sees the data coherently. Other domain transitions are accepted as a no-op for now (no per-domain tracking; the global GGTT is non-coherent by default). * i915_gem_busy: reads sync_from_hw and returns true if the BO's last-used seqno (recorded on i915_gem_execbuffer2) is greater than the ring's last submitted seqno. * i915_gem_wait: invokes redox_private_cs_wait with the BO's tracked seqno and the userland's timeout_ns. Converts the i64 timeout to the wire-struct u64 with .max(0). * i915_gem_vm_bind: validates flags (I915_VMA_BIND / I915_VMA_UNBIND); the global GGTT makes bind a no-op but flag validation prevents Mesa's iris from passing garbage in the future when we add per-VM isolation. * i915_gem_madvise: changes the trait return to Result<bool> to match the wire protocol; on state == 0 (DONTNEED) the BO's GPU mapping is unmapped + released from the GTT and the call returns false (not retained). * i915_query: serves I915_QUERY_TOPOLOGY_INFO (13), I915_QUERY_ENGINE_INFO (14), and I915_QUERY_PERF_CONFIG (16) with minimal valid responses. * i915_gem_execbuffer2: records the returned seqno in bo_seqnos for every handle in bo_handles so subsequent busy/wait calls have a real GPU completion reference. * register_fence_eventfd: libc::dup's the userland eventfd, stores it in fence_eventfds: BTreeMap<u64,i32>, and tracks the seqno in signalled_fences so signal_completed_fences can fire it. * signal_completed_fences: walks fence_eventfds, libc::write(1) to every fd whose seqno has been completed (per ring.last_seqno() after sync_from_hw), then libc::close the fd. Hooked into redox_private_cs_submit so every submission fires completed fences. Adds three BTreeMap fields to IntelDriver: bo_seqnos, bo_tiling, bo_swizzle, plus a BTreeMap<u64,i32> for fence_eventfds and a BTreeSet<u64> for signalled_fences. All Mutex-protected.
This commit is contained in:
@@ -338,6 +338,18 @@ pub trait GpuDriver: Send + Sync {
|
||||
))
|
||||
}
|
||||
|
||||
/// I915_GEM_GET_TILING_SWIZZLE — return only the swizzle mode for
|
||||
/// a BO (used by Mesa's iris internal layout calculations when
|
||||
/// it doesn't need the full tiling query).
|
||||
fn i915_gem_get_tiling_swizzle(
|
||||
&self,
|
||||
_handle: u32,
|
||||
) -> Result<u32> {
|
||||
Err(DriverError::Unsupported(
|
||||
"i915_gem_get_tiling_swizzle is not available on this GPU backend",
|
||||
))
|
||||
}
|
||||
|
||||
/// I915_GEM_SET_TILING — set the tiling mode and stride for a BO.
|
||||
/// `tiling_mode` is one of I915_TILING_NONE / X / Y. `stride` is
|
||||
/// the row stride in bytes (must be tile-width-aligned for X/Y).
|
||||
@@ -404,12 +416,15 @@ pub trait GpuDriver: Send + Sync {
|
||||
}
|
||||
|
||||
/// I915_GEM_MADVISE — advise the kernel about userland's BO use.
|
||||
/// 1 = WILL_NEED (prefetch into GPU cache), 0 = DONTNEED (release).
|
||||
/// `state` is 1 (I915_MADV_WILLNEED, prefetch into GPU cache) or
|
||||
/// 0 (I915_MADV_DONTNEED, release). Returns true if the BO would
|
||||
/// still be retained in cache after the call (Mesa's iris uses this
|
||||
/// to decide whether to repopulate the BO cache after memory pressure).
|
||||
fn i915_gem_madvise(
|
||||
&self,
|
||||
_handle: u32,
|
||||
_state: u32,
|
||||
) -> Result<()> {
|
||||
) -> Result<bool> {
|
||||
Err(DriverError::Unsupported(
|
||||
"i915_gem_madvise is not available on this GPU backend",
|
||||
))
|
||||
@@ -617,6 +632,21 @@ pub trait GpuDriver: Send + Sync {
|
||||
))
|
||||
}
|
||||
|
||||
/// AMDGPU_FENCE_TO_HANDLE — export a fence seqno to a sync object
|
||||
/// handle that userland can pass to other driver calls.
|
||||
/// `flags` selects the export type (syncobj, syncobj-fd, or raw-fd).
|
||||
/// `*handle` receives the new sync object id on success.
|
||||
fn amdgpu_fence_to_handle(
|
||||
&self,
|
||||
_fence: u64,
|
||||
_flags: u32,
|
||||
_handle: &mut u32,
|
||||
) -> Result<()> {
|
||||
Err(DriverError::Unsupported(
|
||||
"amdgpu_fence_to_handle is not available on this GPU backend",
|
||||
))
|
||||
}
|
||||
|
||||
/// Fence eventfd — set up an eventfd that signals on a given
|
||||
/// seqno. The kernel duplicates the eventfd across the
|
||||
/// userland-kernel boundary (via `dup` semantics) and writes 1
|
||||
@@ -634,6 +664,12 @@ pub trait GpuDriver: Send + Sync {
|
||||
"fence eventfd is not available on this GPU backend",
|
||||
))
|
||||
}
|
||||
|
||||
/// Signal any registered fence eventfds whose seqno has been
|
||||
/// completed by the GPU. Called by drivers after a CS submission
|
||||
/// or sync_from_hw to fire notifications without an explicit wait.
|
||||
/// Default is a no-op for drivers that don't implement eventfd fences.
|
||||
fn signal_completed_fences(&self) {}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -4,7 +4,7 @@ pub mod dmc;
|
||||
pub mod gtt;
|
||||
pub mod ring;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::Mutex;
|
||||
|
||||
@@ -56,6 +56,11 @@ pub struct IntelDriver {
|
||||
gen: u8,
|
||||
next_ctx_id: AtomicU64,
|
||||
next_vm_id: AtomicU64,
|
||||
bo_seqnos: Mutex<BTreeMap<GemHandle, u64>>,
|
||||
bo_tiling: Mutex<BTreeMap<GemHandle, u32>>,
|
||||
bo_swizzle: Mutex<BTreeMap<GemHandle, u32>>,
|
||||
fence_eventfds: Mutex<BTreeMap<u64, i32>>,
|
||||
signalled_fences: Mutex<BTreeSet<u64>>,
|
||||
}
|
||||
|
||||
impl IntelDriver {
|
||||
@@ -222,6 +227,11 @@ impl IntelDriver {
|
||||
gen: intel_gen_for_device_id(info.device_id),
|
||||
next_ctx_id: AtomicU64::new(1),
|
||||
next_vm_id: AtomicU64::new(1),
|
||||
bo_seqnos: Mutex::new(BTreeMap::new()),
|
||||
bo_tiling: Mutex::new(BTreeMap::new()),
|
||||
bo_swizzle: Mutex::new(BTreeMap::new()),
|
||||
fence_eventfds: Mutex::new(BTreeMap::new()),
|
||||
signalled_fences: Mutex::new(BTreeSet::new()),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -555,6 +565,10 @@ impl GpuDriver for IntelDriver {
|
||||
.map_err(|e| DriverError::Io(format!("Intel ring flush failed: {e}")))?;
|
||||
ring.last_seqno()
|
||||
};
|
||||
drop(ring);
|
||||
|
||||
self.signal_completed_fences();
|
||||
|
||||
Ok(RedoxPrivateCsSubmitResult { seqno })
|
||||
}
|
||||
|
||||
@@ -726,23 +740,46 @@ impl GpuDriver for IntelDriver {
|
||||
|
||||
fn i915_gem_set_tiling(
|
||||
&self,
|
||||
_handle: GemHandle,
|
||||
_tiling_mode: u32,
|
||||
handle: GemHandle,
|
||||
tiling_mode: u32,
|
||||
_stride: u32,
|
||||
_swizzle_mode: u32,
|
||||
swizzle_mode: u32,
|
||||
) -> Result<()> {
|
||||
if tiling_mode > 2 {
|
||||
return Err(DriverError::InvalidArgument("i915_gem_set_tiling invalid tiling_mode"));
|
||||
}
|
||||
self.bo_tiling
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Buffer("Intel BO tiling table poisoned".into()))?
|
||||
.insert(handle, tiling_mode);
|
||||
self.bo_swizzle
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Buffer("Intel BO swizzle table poisoned".into()))?
|
||||
.insert(handle, swizzle_mode);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn i915_gem_get_tiling(
|
||||
&self,
|
||||
_handle: GemHandle,
|
||||
handle: GemHandle,
|
||||
tiling_mode: &mut u32,
|
||||
_stride: &mut u32,
|
||||
swizzle_mode: &mut u32,
|
||||
) -> Result<()> {
|
||||
*tiling_mode = 0;
|
||||
*swizzle_mode = 0;
|
||||
*tiling_mode = self
|
||||
.bo_tiling
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Buffer("Intel BO tiling table poisoned".into()))?
|
||||
.get(&handle)
|
||||
.copied()
|
||||
.unwrap_or(0);
|
||||
*swizzle_mode = self
|
||||
.bo_swizzle
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Buffer("Intel BO swizzle table poisoned".into()))?
|
||||
.get(&handle)
|
||||
.copied()
|
||||
.unwrap_or(0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -752,34 +789,112 @@ impl GpuDriver for IntelDriver {
|
||||
|
||||
fn i915_gem_set_domain(
|
||||
&self,
|
||||
_handle: GemHandle,
|
||||
_read_domains: u32,
|
||||
_write_domain: u32,
|
||||
handle: GemHandle,
|
||||
read_domains: u32,
|
||||
write_domain: u32,
|
||||
) -> Result<()> {
|
||||
const I915_GEM_DOMAIN_CPU: u32 = 0x1;
|
||||
const I915_GEM_DOMAIN_RENDER: u32 = 0x2;
|
||||
const I915_GEM_DOMAIN_SAMPLER: u32 = 0x4;
|
||||
const I915_GEM_DOMAIN_VERTEX: u32 = 0x8;
|
||||
const I915_GEM_DOMAIN_GTT: u32 = 0x10;
|
||||
|
||||
let _ = handle;
|
||||
let _ = read_domains;
|
||||
|
||||
if write_domain == I915_GEM_DOMAIN_CPU {
|
||||
let gem = self
|
||||
.gem
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Buffer("Intel GEM manager poisoned".into()))?;
|
||||
if let Ok(object) = gem.object(handle) {
|
||||
if let Some(gpu_addr) = object.gpu_addr {
|
||||
let mut gtt = self
|
||||
.gtt
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Initialization("Intel GGTT poisoned".into()))?;
|
||||
gtt.flush()?;
|
||||
let _ = gpu_addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let _ = write_domain;
|
||||
let _ = I915_GEM_DOMAIN_RENDER;
|
||||
let _ = I915_GEM_DOMAIN_SAMPLER;
|
||||
let _ = I915_GEM_DOMAIN_VERTEX;
|
||||
let _ = I915_GEM_DOMAIN_GTT;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn i915_gem_busy(&self, _handle: GemHandle) -> Result<bool> {
|
||||
Ok(false)
|
||||
fn i915_gem_busy(&self, handle: GemHandle) -> Result<bool> {
|
||||
let busy_seqno = self
|
||||
.bo_seqnos
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Buffer("Intel BO seqno table poisoned".into()))?
|
||||
.get(&handle)
|
||||
.copied();
|
||||
let busy_seqno = match busy_seqno {
|
||||
Some(seq) => seq,
|
||||
None => return Ok(false),
|
||||
};
|
||||
let mut ring = self
|
||||
.ring
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Initialization("Intel ring poisoned".into()))?;
|
||||
ring.sync_from_hw()
|
||||
.map_err(|e| DriverError::Io(format!("Intel ring sync_from_hw: {e}")))?;
|
||||
Ok(ring.last_seqno() < busy_seqno)
|
||||
}
|
||||
|
||||
fn i915_gem_wait(
|
||||
&self,
|
||||
_handle: GemHandle,
|
||||
handle: GemHandle,
|
||||
timeout_ns: i64,
|
||||
) -> Result<bool> {
|
||||
if timeout_ns > 0 {
|
||||
std::thread::sleep(std::time::Duration::from_nanos(timeout_ns as u64));
|
||||
let target_seqno = self
|
||||
.bo_seqnos
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Buffer("Intel BO seqno table poisoned".into()))?
|
||||
.get(&handle)
|
||||
.copied();
|
||||
let target_seqno = match target_seqno {
|
||||
Some(seq) => seq,
|
||||
None => return Ok(true),
|
||||
};
|
||||
self.redox_private_cs_wait(&crate::driver::RedoxPrivateCsWait {
|
||||
seqno: target_seqno,
|
||||
timeout_ns: timeout_ns.max(0) as u64,
|
||||
..Default::default()
|
||||
})
|
||||
.map(|result| result.completed)
|
||||
}
|
||||
|
||||
fn i915_gem_madvise(&self, handle: GemHandle, state: u32) -> Result<bool> {
|
||||
if state == 0 {
|
||||
let mut gem = self
|
||||
.gem
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Buffer("Intel GEM manager poisoned".into()))?;
|
||||
if let Ok(object) = gem.object(handle) {
|
||||
if let Some(gpu_addr) = object.gpu_addr {
|
||||
let mut gtt = self
|
||||
.gtt
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Initialization("Intel GGTT poisoned".into()))?;
|
||||
gtt.flush()?;
|
||||
gtt.unmap_range(gpu_addr, object.size)?;
|
||||
gtt.release_range(gpu_addr, object.size);
|
||||
}
|
||||
}
|
||||
return Ok(false);
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn i915_gem_madvise(&self, _handle: GemHandle, _state: u32) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn i915_gem_context_create(&self, ctx_id: &mut u32) -> Result<()> {
|
||||
*ctx_id = self.next_ctx_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
|
||||
*ctx_id = self.next_ctx_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst) as u32;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -789,30 +904,42 @@ impl GpuDriver for IntelDriver {
|
||||
|
||||
fn i915_gem_execbuffer2(
|
||||
&self,
|
||||
_ctx_id: u32,
|
||||
ctx_id: u32,
|
||||
batch_start_offset: u64,
|
||||
batch_len: u32,
|
||||
_bo_handles: &[u32],
|
||||
bo_handles: &[u32],
|
||||
) -> Result<u64> {
|
||||
let _ = ctx_id;
|
||||
// NO_RELOC semantics: the batch is a slice of a single BO
|
||||
// and the userland has pre-computed all GPU addresses. We
|
||||
// submit the slice as an opaque CS packet; the kernel's
|
||||
// CS ring will treat the bytes as the GPU's instruction
|
||||
// stream.
|
||||
let submit = crate::driver::RedoxPrivateCsSubmit {
|
||||
src_handle: GemHandle(0),
|
||||
dst_handle: GemHandle(0),
|
||||
src_handle: 0,
|
||||
dst_handle: 0,
|
||||
src_offset: batch_start_offset,
|
||||
dst_offset: 0,
|
||||
byte_count: batch_len 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("Intel BO seqno table poisoned".into()))?;
|
||||
for handle in bo_handles {
|
||||
seqnos.insert(*handle, result.seqno);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result.seqno)
|
||||
}
|
||||
|
||||
fn i915_gem_vm_create(&self, vm_id: &mut u32) -> Result<()> {
|
||||
*vm_id = self.next_vm_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
|
||||
*vm_id = self.next_vm_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst) as u32;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -823,23 +950,55 @@ impl GpuDriver for IntelDriver {
|
||||
fn i915_gem_vm_bind(
|
||||
&self,
|
||||
_vm_id: u32,
|
||||
_handle: GemHandle,
|
||||
handle: GemHandle,
|
||||
_offset: u64,
|
||||
_size: u64,
|
||||
_flags: u32,
|
||||
flags: u32,
|
||||
) -> Result<()> {
|
||||
Ok(())
|
||||
const I915_VMA_BIND: u32 = 0x1;
|
||||
const I915_VMA_UNBIND: u32 = 0x2;
|
||||
match flags & 0xff {
|
||||
x if x == I915_VMA_BIND || x == I915_VMA_UNBIND => {
|
||||
let _ = handle;
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(DriverError::InvalidArgument("i915_gem_vm_bind unknown flags")),
|
||||
}
|
||||
}
|
||||
|
||||
fn i915_query(&self, query_id: u32, data: &mut [u8]) -> Result<()> {
|
||||
const I915_QUERY_TOPOLOGY_INFO: u32 = 13;
|
||||
const I915_QUERY_ENGINE_INFO: u32 = 14;
|
||||
const I915_QUERY_PERF_CONFIG: u32 = 16;
|
||||
|
||||
match query_id {
|
||||
13 => {
|
||||
I915_QUERY_TOPOLOGY_INFO => {
|
||||
if data.len() >= 12 {
|
||||
data[0..4].copy_from_slice(&13u32.to_ne_bytes());
|
||||
data[4..8].copy_from_slice(&0u32.to_ne_bytes());
|
||||
data[8..12].copy_from_slice(&1u32.to_ne_bytes());
|
||||
}
|
||||
}
|
||||
I915_QUERY_ENGINE_INFO => {
|
||||
if data.len() >= 32 {
|
||||
data[0..4].copy_from_slice(&14u32.to_ne_bytes());
|
||||
data[4..8].copy_from_slice(&0u32.to_ne_bytes());
|
||||
data[8..12].copy_from_slice(&1u32.to_ne_bytes());
|
||||
data[12..16].copy_from_slice(&0u32.to_ne_bytes());
|
||||
data[16..20].copy_from_slice(&0u32.to_ne_bytes());
|
||||
data[20..24].copy_from_slice(&0u32.to_ne_bytes());
|
||||
data[24..28].copy_from_slice(&0u32.to_ne_bytes());
|
||||
data[28..32].copy_from_slice(&0u32.to_ne_bytes());
|
||||
}
|
||||
}
|
||||
I915_QUERY_PERF_CONFIG => {
|
||||
if data.len() >= 16 {
|
||||
data[0..4].copy_from_slice(&16u32.to_ne_bytes());
|
||||
for i in 4..16 {
|
||||
data[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
@@ -847,10 +1006,72 @@ impl GpuDriver for IntelDriver {
|
||||
|
||||
fn register_fence_eventfd(
|
||||
&self,
|
||||
_userland_eventfd: i32,
|
||||
_seqno: u64,
|
||||
userland_eventfd: i32,
|
||||
seqno: u64,
|
||||
) -> Result<i32> {
|
||||
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("Intel fence eventfd table poisoned".into()))?
|
||||
.insert(seqno, kernel_fd);
|
||||
self.signalled_fences
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Buffer("Intel signalled fence set poisoned".into()))?
|
||||
.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<u64> = 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: Intel fence eventfd write failed for seqno={}, fd={}, {}",
|
||||
seq,
|
||||
fd,
|
||||
std::io::Error::last_os_error()
|
||||
);
|
||||
} else {
|
||||
self.signalled_fences
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Buffer("Intel signalled fence set poisoned".into()))
|
||||
.ok()
|
||||
.map(|mut s| s.remove(&seq));
|
||||
}
|
||||
unsafe { libc::close(fd) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_edid(&self, connector_id: u32) -> Vec<u8> {
|
||||
|
||||
Reference in New Issue
Block a user