use core::sync::atomic::Ordering; use crate::mem::phys::BitmapFrameAllocator; use crate::proc::PROCESSES; use crate::ring::{ CompletionEntry, MAX_CQ_ENTRIES, MAX_SQ_ENTRIES, RING_OP_NOP, RingHeader, RingIndex, SubmissionEntry, ring_cq_offset, ring_sq_offset, }; fn setup_ring_page() -> (x86_64::PhysAddr, *mut u8) { let allocator = BitmapFrameAllocator; let frame = allocator.allocate().expect("alloc ring frame"); let phys = frame.phys_addr(); crate::mem::addr::zero_frame(phys); let virt = crate::mem::addr::phys_to_virt(phys); let _ = frame.inner(); (phys, virt.as_mut_ptr::()) } fn teardown_ring_page(phys: x86_64::PhysAddr) { BitmapFrameAllocator::free_frame_by_addr(phys); } unsafe fn write_sqe(ring_base: *mut u8, index: u32, sqe: SubmissionEntry) { let sq_base = unsafe { ring_base.add(ring_sq_offset()) }; let entry_ptr = unsafe { sq_base.add((index as usize) * core::mem::size_of::()) }; unsafe { core::ptr::write_volatile(entry_ptr as *mut SubmissionEntry, sqe) }; } unsafe fn read_cqe(ring_base: *const u8, index: u32) -> CompletionEntry { let cq_base = unsafe { ring_base.add(ring_cq_offset()) }; let entry_ptr = unsafe { cq_base.add((index as usize) * core::mem::size_of::()) }; unsafe { core::ptr::read_volatile(entry_ptr as *const CompletionEntry) } } unsafe fn set_sq_tail(ring_base: *mut u8, val: u32) { let header = unsafe { &*(ring_base as *const RingHeader) }; header.sq_tail.store(val, Ordering::Release); } fn alloc_test_process() -> ( crate::types::Pid, crate::sync::IrqMutexGuard<'static, crate::proc::ProcessManager, 0>, ) { let mut allocator = BitmapFrameAllocator; let mut ptable = PROCESSES.lock(); let created = ptable.allocate(&mut allocator).expect("alloc process"); ptable.start(created).expect("start"); let pid = created.pid(); (pid, ptable) } crate::kernel_test!( fn ring_sq_full_processes_all() { let (phys, ring_base) = setup_ring_page(); let (pid, ptable) = alloc_test_process(); drop(ptable); let count = MAX_SQ_ENTRIES.min(16); (0..count).for_each(|i| unsafe { write_sqe( ring_base, i, SubmissionEntry { opcode: RING_OP_NOP, user_data: i, ..SubmissionEntry::zeroed() }, ); }); unsafe { set_sq_tail(ring_base, count) }; let result = crate::ring::process::ring_enter(phys, pid, 0).expect("ring_enter"); assert!( result == count as i64, "should process all {} entries, got {}", count, result ); (0..count).for_each(|i| { let cqe = unsafe { read_cqe(ring_base, i) }; assert!(cqe.result == 0, "NOP entry {} should return 0", i); assert!( cqe.user_data == i as u64, "user_data for entry {} should be {}, got {}", i, i, cqe.user_data ); }); let mut ptable = PROCESSES.lock(); ptable.destroy(pid, &mut BitmapFrameAllocator); drop(ptable); teardown_ring_page(phys); } ); crate::kernel_test!( fn ring_cq_wraparound() { let (phys, ring_base) = setup_ring_page(); let (pid, ptable) = alloc_test_process(); drop(ptable); let header = unsafe { &*(ring_base as *const RingHeader) }; let wrap_start = MAX_CQ_ENTRIES - 2; header.cq_head.store(wrap_start, Ordering::Release); header.cq_tail.store(wrap_start, Ordering::Release); header.sq_head.store(0, Ordering::Release); { let mut ptable = PROCESSES.lock(); let exec = ptable.exec_mut(pid).expect("get exec"); exec.ring_cq_tail = RingIndex::new(wrap_start); } (0..4u32).for_each(|i| unsafe { write_sqe( ring_base, i, SubmissionEntry { opcode: RING_OP_NOP, user_data: 1000 + i, ..SubmissionEntry::zeroed() }, ); }); unsafe { set_sq_tail(ring_base, 4) }; let result = crate::ring::process::ring_enter(phys, pid, 0).expect("ring_enter"); assert!(result == 4, "should process 4 entries, got {}", result); (0..4u32).for_each(|i| { let cq_idx = (wrap_start + i) % MAX_CQ_ENTRIES; let cqe = unsafe { read_cqe(ring_base, cq_idx) }; assert!( cqe.user_data == 1000 + i as u64, "cqe at wrapped index {} should have user_data {}, got {}", cq_idx, 1000 + i, cqe.user_data ); }); let mut ptable = PROCESSES.lock(); ptable.destroy(pid, &mut BitmapFrameAllocator); drop(ptable); teardown_ring_page(phys); } );