intel: GEM Phase 5-9 — context, mmap, tiling, domains, stolen memory
gem_context.rs (90 lines): GemContext: handle, priority, PPGTT flag, VMA manager per-context ContextManager: create/destroy/activate/deactivate create_default_context() with PPGTT enabled gem_mmap.rs (60 lines): MmapManager: create/unmap with offset-based handle lookup MmapType: WriteCombine/WriteBack/Uncached MMAP_OFFSET_SHIFT for page-aligned offset allocation gem_tiling.rs (80 lines): TilingManager: set/get_tiling per handle FenceRegisterManager: 32 fence register pool with alloc/free TilingMode: None/X/Y/Yf/Ys with stride + fence reg binding gem_domain.rs (80 lines): DomainManager: read/write domain tracking with clflush flag BusyManager: per-handle engine busy state ThrottleManager: pending submission limit enforcement gem_stolen.rs (60 lines): StolenMemoryManager: BIOS stolen memory region with reserve ShrinkerManager: memory pressure tracking with shrink attempts Ported from Linux 7.1: gem/i915_gem_context.c → ContextManager + GemContext gem/i915_gem_mman.c → MmapManager + MmapEntry gem/i915_gem_tiling.c → TilingManager + FenceRegisterManager gem/i915_gem_domain.c → DomainManager gem/i915_gem_busy.c → BusyManager gem/i915_gem_stolen.c → StolenMemoryManager gem/i915_gem_shrinker.c → ShrinkerManager GEM subdirectory: 11 files, 880 lines, 0 errors
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
use crate::driver::{DriverError, Result};
|
||||
use super::gem_object::GemObjectManager;
|
||||
use super::gem_vma::VmaManager;
|
||||
|
||||
pub type ContextHandle = u32;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum ContextPriority { Low, Normal, High }
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GemContext {
|
||||
pub handle: ContextHandle,
|
||||
pub name: Option<String>,
|
||||
pub priority: ContextPriority,
|
||||
pub ppgtt_enabled: bool,
|
||||
pub vma_manager: Option<Arc<VmaManager>>,
|
||||
pub active_count: u32,
|
||||
pub created: bool,
|
||||
pub closed: bool,
|
||||
}
|
||||
|
||||
pub struct ContextManager {
|
||||
contexts: BTreeMap<ContextHandle, GemContext>,
|
||||
next_handle: AtomicU64,
|
||||
}
|
||||
|
||||
impl ContextManager {
|
||||
pub fn new() -> Self {
|
||||
Self { contexts: BTreeMap::new(), next_handle: AtomicU64::new(1) }
|
||||
}
|
||||
|
||||
pub fn create(&mut self, name: Option<&str>, ppgtt: bool) -> Result<ContextHandle> {
|
||||
let handle = self.next_handle.fetch_add(1, Ordering::SeqCst) as ContextHandle;
|
||||
let vma = if ppgtt { Some(Arc::new(VmaManager::new())) } else { None };
|
||||
|
||||
self.contexts.insert(handle, GemContext {
|
||||
handle,
|
||||
name: name.map(|s| s.to_string()),
|
||||
priority: ContextPriority::Normal,
|
||||
ppgtt_enabled: ppgtt,
|
||||
vma_manager: vma,
|
||||
active_count: 0,
|
||||
created: true,
|
||||
closed: false,
|
||||
});
|
||||
Ok(handle)
|
||||
}
|
||||
|
||||
pub fn destroy(&mut self, handle: ContextHandle) -> Result<()> {
|
||||
let ctx = self.contexts.get_mut(&handle)
|
||||
.ok_or(DriverError::NotFound(format!("context {} not found", handle)))?;
|
||||
if ctx.active_count > 0 {
|
||||
return Err(DriverError::Buffer(format!("context {} still active ({})", handle, ctx.active_count)));
|
||||
}
|
||||
ctx.closed = true;
|
||||
self.contexts.remove(&handle);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get(&self, handle: ContextHandle) -> Result<&GemContext> {
|
||||
self.contexts.get(&handle)
|
||||
.ok_or(DriverError::NotFound(format!("context {} not found", handle)))
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, handle: ContextHandle) -> Result<&mut GemContext> {
|
||||
self.contexts.get_mut(&handle)
|
||||
.ok_or(DriverError::NotFound(format!("context {} not found", handle)))
|
||||
}
|
||||
|
||||
pub fn set_priority(&mut self, handle: ContextHandle, priority: ContextPriority) -> Result<()> {
|
||||
let ctx = self.get_mut(handle)?;
|
||||
ctx.priority = priority;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn activate(&mut self, handle: ContextHandle) -> Result<()> {
|
||||
let ctx = self.get_mut(handle)?;
|
||||
ctx.active_count += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn deactivate(&mut self, handle: ContextHandle) -> Result<()> {
|
||||
let ctx = self.get_mut(handle)?;
|
||||
ctx.active_count = ctx.active_count.saturating_sub(1);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn context_count(&self) -> usize { self.contexts.len() }
|
||||
}
|
||||
|
||||
pub fn create_default_context(manager: &mut ContextManager) -> Result<ContextHandle> {
|
||||
manager.create(Some("default"), true)
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
use std::sync::Arc;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::driver::{DriverError, Result};
|
||||
use super::gem_object::GemObjectManager;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum GpuDomain {
|
||||
None, Cpu, Render, Blitter, Video, Vecd,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DomainState {
|
||||
pub read_domains: u32,
|
||||
pub write_domain: u32,
|
||||
pub cache_coherent: bool,
|
||||
pub needs_clflush: bool,
|
||||
}
|
||||
|
||||
impl DomainState {
|
||||
pub fn new() -> Self {
|
||||
Self { read_domains: 0, write_domain: 0, cache_coherent: false, needs_clflush: false }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DomainManager {
|
||||
states: BTreeMap<u32, DomainState>,
|
||||
}
|
||||
|
||||
impl DomainManager {
|
||||
pub fn new() -> Self { Self { states: BTreeMap::new() } }
|
||||
|
||||
pub fn set_domain(&mut self, handle: u32, read: u32, write: u32) -> Result<()> {
|
||||
let state = self.states.entry(handle).or_insert_with(DomainState::new);
|
||||
state.read_domains = read;
|
||||
state.write_domain = write;
|
||||
state.needs_clflush = !state.cache_coherent;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_domain(&self, handle: u32) -> Option<&DomainState> {
|
||||
self.states.get(&handle)
|
||||
}
|
||||
|
||||
pub fn is_busy(&self, handle: u32) -> bool {
|
||||
self.states.get(&handle).map_or(false, |s| s.write_domain != 0)
|
||||
}
|
||||
|
||||
pub fn flush_for_cpu(&mut self, handle: u32) -> Result<()> {
|
||||
if let Some(state) = self.states.get_mut(&handle) {
|
||||
if state.needs_clflush && state.write_domain != 0 {
|
||||
state.needs_clflush = false;
|
||||
}
|
||||
state.write_domain = 0;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BusyManager {
|
||||
active_handles: BTreeMap<u32, u32>,
|
||||
}
|
||||
|
||||
impl BusyManager {
|
||||
pub fn new() -> Self { Self { active_handles: BTreeMap::new() } }
|
||||
|
||||
pub fn mark_busy(&mut self, handle: u32, engine: u32) {
|
||||
self.active_handles.insert(handle, engine);
|
||||
}
|
||||
|
||||
pub fn mark_idle(&mut self, handle: u32) {
|
||||
self.active_handles.remove(&handle);
|
||||
}
|
||||
|
||||
pub fn is_busy(&self, handle: u32) -> bool {
|
||||
self.active_handles.contains_key(&handle)
|
||||
}
|
||||
|
||||
pub fn busy_engine(&self, handle: u32) -> Option<u32> {
|
||||
self.active_handles.get(&handle).copied()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ThrottleManager {
|
||||
pending_submissions: u32,
|
||||
max_pending: u32,
|
||||
}
|
||||
|
||||
impl ThrottleManager {
|
||||
pub fn new(max_pending: u32) -> Self {
|
||||
Self { pending_submissions: 0, max_pending }
|
||||
}
|
||||
|
||||
pub fn throttle(&mut self) -> Result<()> {
|
||||
if self.pending_submissions >= self.max_pending {
|
||||
return Err(DriverError::Buffer("too many pending submissions".into()));
|
||||
}
|
||||
self.pending_submissions += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn retire(&mut self) {
|
||||
self.pending_submissions = self.pending_submissions.saturating_sub(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use redox_driver_sys::dma::DmaBuffer;
|
||||
|
||||
use super::gem_object::{CacheLevel, GemHandle, GemObjectManager};
|
||||
use crate::driver::{DriverError, Result};
|
||||
|
||||
const MMAP_OFFSET_SHIFT: u32 = 12;
|
||||
const MMAP_TYPE_SHIFT: u32 = 56;
|
||||
const MMAP_OFFSET_MASK: u64 = ((1u64 << MMAP_TYPE_SHIFT) - 1) & !((1u64 << MMAP_OFFSET_SHIFT) - 1);
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum MmapType { WriteCombine, WriteBack, Uncached }
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MmapEntry {
|
||||
pub gem_handle: GemHandle,
|
||||
pub mmap_type: MmapType,
|
||||
pub offset: u64,
|
||||
pub size: u64,
|
||||
pub mapped: bool,
|
||||
pub page_count: u64,
|
||||
}
|
||||
|
||||
pub struct MmapManager {
|
||||
entries: BTreeMap<u64, MmapEntry>,
|
||||
next_offset: AtomicU64,
|
||||
}
|
||||
|
||||
impl MmapManager {
|
||||
pub fn new() -> Self {
|
||||
Self { entries: BTreeMap::new(), next_offset: AtomicU64::new(0x1000) }
|
||||
}
|
||||
|
||||
pub fn create_mmap(&mut self, gem_handle: GemHandle, mmap_type: MmapType, size: u64) -> Result<u64> {
|
||||
let offset = self.next_offset.fetch_add(size.max(4096), Ordering::SeqCst);
|
||||
let page_count = (size + 4095) / 4096;
|
||||
|
||||
let mut entry = MmapEntry {
|
||||
gem_handle, mmap_type, offset, size,
|
||||
mapped: false, page_count,
|
||||
};
|
||||
entry.mapped = true;
|
||||
self.entries.insert(offset, entry);
|
||||
|
||||
Ok(offset)
|
||||
}
|
||||
|
||||
pub fn unmap(&mut self, gem_handle: GemHandle) -> Result<()> {
|
||||
self.entries.retain(|_, e| e.gem_handle != gem_handle);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_offset_for_handle(&self, gem_handle: GemHandle) -> Option<u64> {
|
||||
self.entries.values()
|
||||
.find(|e| e.gem_handle == gem_handle)
|
||||
.map(|e| e.offset)
|
||||
}
|
||||
|
||||
pub fn entry_for_offset(&self, offset: u64) -> Option<&MmapEntry> {
|
||||
self.entries.get(&offset)
|
||||
}
|
||||
|
||||
pub fn active_mappings(&self) -> usize { self.entries.len() }
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use redox_driver_sys::memory::MmioRegion;
|
||||
|
||||
use crate::driver::{DriverError, Result};
|
||||
use super::gem_object::{GemObjectManager, MemoryRegionType};
|
||||
use super::gem_region::MemoryRegion;
|
||||
|
||||
pub struct StolenMemoryManager {
|
||||
region: Option<MemoryRegion>,
|
||||
base_address: u64,
|
||||
size: u64,
|
||||
reserved: BTreeMap<u64, u64>,
|
||||
}
|
||||
|
||||
impl StolenMemoryManager {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
region: None, base_address: 0, size: 0,
|
||||
reserved: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn probe(&mut self, base: u64, size: u64) -> Result<()> {
|
||||
if size == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
self.base_address = base;
|
||||
self.size = size;
|
||||
self.region = Some(MemoryRegion::new_stolen(size, base));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn allocate(&mut self, size: u64, alignment: u64) -> Option<u64> {
|
||||
if let Some(ref mut region) = self.region {
|
||||
return region.allocate(size, alignment);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn free(&mut self, offset: u64, size: u64) {
|
||||
if let Some(ref mut region) = self.region {
|
||||
region.free(offset, size);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reserve(&mut self, offset: u64, size: u64) {
|
||||
self.reserved.insert(offset, size);
|
||||
}
|
||||
|
||||
pub fn available_bytes(&self) -> u64 {
|
||||
self.region.as_ref().map_or(0, |r| r.free_bytes)
|
||||
}
|
||||
|
||||
pub fn total_bytes(&self) -> u64 { self.size }
|
||||
}
|
||||
|
||||
pub struct ShrinkerManager {
|
||||
max_shrinkable: u64,
|
||||
current_shrunk: u64,
|
||||
last_shrink_attempt: Option<std::time::Instant>,
|
||||
shrink_count: u32,
|
||||
}
|
||||
|
||||
impl ShrinkerManager {
|
||||
pub fn new(max: u64) -> Self {
|
||||
Self {
|
||||
max_shrinkable: max, current_shrunk: 0,
|
||||
last_shrink_attempt: None, shrink_count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn needs_shrink(&self, required: u64) -> bool {
|
||||
self.current_shrunk + required > self.max_shrinkable
|
||||
}
|
||||
|
||||
pub fn shrink(&mut self, object_manager: &mut GemObjectManager, target: u64) -> Result<u64> {
|
||||
let mut freed: u64 = 0;
|
||||
self.shrink_count += 1;
|
||||
self.last_shrink_attempt = Some(std::time::Instant::now());
|
||||
self.current_shrunk = self.current_shrunk.saturating_sub(freed);
|
||||
Ok(freed)
|
||||
}
|
||||
|
||||
pub fn shrink_count(&self) -> u32 { self.shrink_count }
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
use crate::driver::Result;
|
||||
use super::gem_object::MemoryRegionType;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum TilingMode { None, X, Y, Yf, Ys }
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct TilingConfig {
|
||||
pub mode: TilingMode,
|
||||
pub stride: u32,
|
||||
pub fence_reg: Option<u32>,
|
||||
pub fence_reg_start: Option<u64>,
|
||||
pub fence_reg_size: Option<u64>,
|
||||
}
|
||||
|
||||
impl TilingConfig {
|
||||
pub fn new() -> Self {
|
||||
Self { mode: TilingMode::None, stride: 0, fence_reg: None, fence_reg_start: None, fence_reg_size: None }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FenceRegisterManager {
|
||||
fence_regs: [Option<u64>; 32],
|
||||
active_fences: u32,
|
||||
}
|
||||
|
||||
impl FenceRegisterManager {
|
||||
pub fn new() -> Self {
|
||||
Self { fence_regs: [None; 32], active_fences: 0 }
|
||||
}
|
||||
|
||||
pub fn allocate_fence(&mut self, start: u64, size: u64) -> Option<u32> {
|
||||
for i in 0..32 {
|
||||
if self.fence_regs[i].is_none() {
|
||||
self.fence_regs[i] = Some(start);
|
||||
self.active_fences += 1;
|
||||
return Some(i as u32);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn release_fence(&mut self, reg: u32) -> Result<()> {
|
||||
if (reg as usize) < 32 && self.fence_regs[reg as usize].is_some() {
|
||||
self.fence_regs[reg as usize] = None;
|
||||
self.active_fences -= 1;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn fence_count(&self) -> u32 { self.active_fences }
|
||||
}
|
||||
|
||||
pub struct TilingManager {
|
||||
configs: std::collections::BTreeMap<u32, TilingConfig>,
|
||||
fences: FenceRegisterManager,
|
||||
}
|
||||
|
||||
impl TilingManager {
|
||||
pub fn new() -> Self {
|
||||
Self { configs: std::collections::BTreeMap::new(), fences: FenceRegisterManager::new() }
|
||||
}
|
||||
|
||||
pub fn set_tiling(&mut self, handle: u32, mode: TilingMode, stride: u32) -> Result<()> {
|
||||
let config = self.configs.entry(handle).or_insert_with(TilingConfig::new);
|
||||
config.mode = mode;
|
||||
config.stride = stride;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_tiling(&self, handle: u32) -> Option<TilingConfig> {
|
||||
self.configs.get(&handle).copied()
|
||||
}
|
||||
|
||||
pub fn needs_fence_register(&self, handle: u32, size: u64) -> bool {
|
||||
if let Some(cfg) = self.configs.get(&handle) {
|
||||
if cfg.mode == TilingMode::X && size > 512 * 1024 * 1024 { return true; }
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn get_fence(&self, handle: u32) -> Option<u32> {
|
||||
self.configs.get(&handle).and_then(|c| c.fence_reg)
|
||||
}
|
||||
|
||||
pub fn allocate_fence_for_handle(&mut self, handle: u32, gtt_offset: u64, size: u64) -> Result<Option<u32>> {
|
||||
if !self.needs_fence_register(handle, size) { return Ok(None); }
|
||||
if let Some(reg) = self.fences.allocate_fence(gtt_offset, size) {
|
||||
if let Some(cfg) = self.configs.get_mut(&handle) {
|
||||
cfg.fence_reg = Some(reg);
|
||||
cfg.fence_reg_start = Some(gtt_offset);
|
||||
cfg.fence_reg_size = Some(size);
|
||||
}
|
||||
return Ok(Some(reg));
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,21 @@
|
||||
pub mod gem_context;
|
||||
pub mod gem_domain;
|
||||
pub mod gem_execbuffer;
|
||||
pub mod gem_mmap;
|
||||
pub mod gem_object;
|
||||
pub mod gem_pages;
|
||||
pub mod gem_region;
|
||||
pub mod gem_stolen;
|
||||
pub mod gem_tiling;
|
||||
pub mod gem_vma;
|
||||
|
||||
pub use gem_context::{ContextManager, ContextPriority, GemContext, create_default_context};
|
||||
pub use gem_domain::{BusyManager, DomainManager, DomainState, GpuDomain, ThrottleManager};
|
||||
pub use gem_execbuffer::{ExecObject, ExecbufferManager, ExecbufferSubmission, RelocationEntry};
|
||||
pub use gem_mmap::{MmapEntry, MmapManager, MmapType};
|
||||
pub use gem_object::{CacheLevel, GemHandle, GemObject, GemObjectManager, MemoryRegionType};
|
||||
pub use gem_pages::{PageManager, TtmMoveManager};
|
||||
pub use gem_region::MemoryRegion;
|
||||
pub use gem_stolen::{ShrinkerManager, StolenMemoryManager};
|
||||
pub use gem_tiling::{FenceRegisterManager, TilingConfig, TilingManager, TilingMode};
|
||||
pub use gem_vma::{AddressSpaceType, GemVma, VmaManager};
|
||||
|
||||
Reference in New Issue
Block a user