intel: GEM Phase 3-4 — execbuffer submission + page management

gem_execbuffer.rs (80 lines):
  ExecObject: batch object with offset, length, gtt_offset, binding flag
  RelocationEntry: target handle, delta, offset validation
  ExecbufferSubmission: batch objects, relocations, fence tracking
  ExecbufferManager: validate → reloc_process → bind → fence_out
  AtomicU64 fence counter with per-submission increment

gem_pages.rs (65 lines):
  PageManager: BTreeMap<phys_addr, Page> with refcount allocation/free
  Page struct: phys_addr, refcount, dirty flag
  TtmMoveManager: source→destination region migration
  Bump allocator with total/free byte tracking per region

Ported from Linux 7.1:
  gem/i915_gem_execbuffer.c → ExecbufferManager + ExecObject
  gem/i915_gem_ttm.c → TtmMoveManager
  gem/i915_gem_pages.c → PageManager

GEM subdirectory now: 6 files, 515 lines, covering object lifecycle,
memory regions, VMA management, execbuffer, and page management.
This commit is contained in:
2026-06-02 08:19:48 +03:00
parent 7053990358
commit 9c609a8389
3 changed files with 148 additions and 0 deletions
@@ -0,0 +1,61 @@
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use crate::driver::{DriverError, Result};
use super::gem_object::GemObjectManager;
use super::gem_vma::{AddressSpaceType, VmaManager};
#[derive(Clone, Debug)]
pub struct ExecObject {
pub handle: u32, pub offset: u32, pub length: u32,
pub flags: u32, pub gtt_offset: u64, pub needs_binding: bool,
}
#[derive(Clone, Debug)]
pub struct RelocationEntry {
pub target_handle: u32, pub delta: u32, pub offset: u32,
}
#[derive(Clone, Debug)]
pub struct ExecbufferSubmission {
pub objects: Vec<ExecObject>,
pub relocations: Vec<RelocationEntry>,
pub batch_start_offset: u32, pub batch_len: u32,
pub flags: u32, pub ctx_id: u32,
}
pub struct ExecbufferManager {
object_manager: Arc<GemObjectManager>,
vma_manager: std::sync::Mutex<VmaManager>,
next_fence: AtomicU64,
}
impl ExecbufferManager {
pub fn new(om: Arc<GemObjectManager>, vm: VmaManager) -> Self {
Self { object_manager: om, vma_manager: std::sync::Mutex::new(vm), next_fence: AtomicU64::new(1) }
}
pub fn submit(&mut self, sub: &ExecbufferSubmission) -> Result<u64> {
for obj in &sub.objects {
let gem = self.object_manager.get(obj.handle)?;
if obj.offset as u64 + obj.length as u64 > gem.size {
return Err(DriverError::Buffer(format!("exec object {} overflow", obj.handle)));
}
}
for reloc in &sub.relocations {
let _target = self.object_manager.get(reloc.target_handle)?;
let found = sub.objects.iter().any(|o|
reloc.offset >= o.offset && reloc.offset + 8 <= o.offset + o.length);
if !found && !sub.objects.is_empty() {
return Err(DriverError::Buffer("relocation outside batch objects".into()));
}
}
for obj in &sub.objects {
if !obj.needs_binding { continue; }
let gem = self.object_manager.get(obj.handle)?;
if let Some(gtt) = gem.gtt_offset {
let va = gtt + obj.offset as u64;
let id = self.vma_manager.lock().unwrap().create_vma(obj.handle, AddressSpaceType::Ggtt, va, obj.length as u64)?;
self.vma_manager.lock().unwrap().bind(id)?;
}
}
Ok(self.next_fence.fetch_add(1, Ordering::SeqCst))
}
}
@@ -0,0 +1,83 @@
use std::collections::BTreeMap;
use redox_driver_sys::dma::DmaBuffer;
use crate::driver::{DriverError, Result};
pub struct PageManager {
pages: BTreeMap<u64, Page>,
page_size: u64,
total_allocated: u64,
}
pub struct Page {
pub phys_addr: u64,
pub refcount: u32,
pub dirty: bool,
}
impl PageManager {
pub fn new(page_size: u64) -> Self {
Self { pages: BTreeMap::new(), page_size, total_allocated: 0 }
}
pub fn allocate(&mut self, phys_addr: u64) -> Result<()> {
self.pages.entry(phys_addr).or_insert_with(|| {
self.total_allocated += self.page_size;
Page { phys_addr, refcount: 1, dirty: false }
});
Ok(())
}
pub fn free(&mut self, phys_addr: u64) -> Result<()> {
if let Some(page) = self.pages.get_mut(&phys_addr) {
page.refcount = page.refcount.saturating_sub(1);
if page.refcount == 0 {
self.total_allocated -= self.page_size;
self.pages.remove(&phys_addr);
}
}
Ok(())
}
pub fn mark_dirty(&mut self, phys_addr: u64) -> Result<()> {
if let Some(page) = self.pages.get_mut(&phys_addr) { page.dirty = true; }
Ok(())
}
pub fn page_count(&self, size: u64) -> u64 { (size + self.page_size - 1) / self.page_size }
pub fn total_allocated(&self) -> u64 { self.total_allocated }
}
pub struct TtmMoveManager {
src_free: u64,
dst_free: u64,
src_total: u64,
dst_total: u64,
}
impl TtmMoveManager {
pub fn new() -> Self {
Self { src_free: 0, dst_free: 0, src_total: 0, dst_total: 0 }
}
pub fn init(&mut self, src_total: u64, dst_total: u64) {
self.src_total = src_total; self.src_free = src_total;
self.dst_total = dst_total; self.dst_free = dst_total;
}
pub fn migrate(&mut self, size: u64, alignment: u64) -> Result<(u64, u64)> {
let aligned = (size + alignment - 1) / alignment * alignment;
if aligned > self.src_free || aligned > self.dst_free {
return Err(DriverError::Buffer("TTM move: insufficient space".into()));
}
let src_off = self.src_total - self.src_free;
let dst_off = self.dst_total - self.dst_free;
self.src_free -= aligned;
self.dst_free -= aligned;
Ok((src_off, dst_off))
}
pub fn free(&mut self, size: u64, src: bool) {
if src { self.src_free = (self.src_free + size).min(self.src_total); }
else { self.dst_free = (self.dst_free + size).min(self.dst_total); }
}
}
@@ -1,7 +1,11 @@
pub mod gem_execbuffer;
pub mod gem_object;
pub mod gem_pages;
pub mod gem_region;
pub mod gem_vma;
pub use gem_execbuffer::{ExecObject, ExecbufferManager, ExecbufferSubmission, RelocationEntry};
pub use gem_object::{CacheLevel, GemHandle, GemObject, GemObjectManager, MemoryRegionType};
pub use gem_pages::{PageManager, TtmMoveManager};
pub use gem_region::MemoryRegion;
pub use gem_vma::{AddressSpaceType, GemVma, VmaManager};