5.0 KiB
5.0 KiB
Очередь отзыва RevocationQueue: events.rs
Концептуальная модель
RevocationQueue — это глобальная lock-free кольцевая очередь, которая соединяет capability-систему (CNode) с виртуальной памятью (VMM).
Назначение
Когда capability отзывается, VMM должна аннулировать все VMA, связанные с этим capability. Но:
- CNode.revoke() не может напрямую обратиться к VMM (разные подсистемы, разное время жизни блокировок).
- VMM может быть в обработке page fault'а.
- Отзыв может быть каскадным и рекурсивным.
Решение: асинхронная очередь.
CNode.revoke(token_1)
│
└──→ push(token_1) ───→ MMU_REVOCATION_QUEUE ──→ pop() ──→ VMM
│
CNode.revoke(token_2) │
│ │
└──→ push(token_2) ─────────────────────────────────────────┤
│
Page Fault handler │
│ │
└──→ process_pending_revocations() ←────────────────────────┘
RevocationQueue — детали
pub struct RevocationQueue {
buffer: [AtomicU64; QUEUE_SIZE], // 1024 entry
_pad0: [u8; 64], // padding: кэш-линия
head: AtomicUsize, // читатель (VMM)
_pad1: [u8; 64], // padding: кэш-линия
tail: AtomicUsize, // писатели (CNode revoke)
}
Тот же паттерн, что и в PMActorQueue: разделение head и tail
по разным кэш-линиям предотвращает false sharing.
push(token_sig)
pub fn push(&self, token_sig: u64) -> Result<(), &'static str> {
let mut tail = self.tail.load(Ordering::Relaxed);
loop {
let head = self.head.load(Ordering::Acquire);
if tail.wrapping_sub(head) >= QUEUE_SIZE {
return Err("Revocation queue overflow");
}
match self.tail.compare_exchange_weak(tail, tail+1, AcqRel, Relaxed) {
Ok(_) => {
self.buffer[tail & QUEUE_MASK].store(token_sig, Release);
return Ok(());
}
Err(actual) => tail = actual,
}
}
}
pop()
pub fn pop(&self) -> Option<u64> {
let head = self.head.load(Ordering::Relaxed);
let tail = self.tail.load(Ordering::Acquire);
if head == tail { return None; }
let token = self.buffer[head & QUEUE_MASK].load(Ordering::Acquire);
self.head.store(head.wrapping_add(1), Ordering::Release);
Some(token)
}
Глобальный экземпляр
pub static MMU_REVOCATION_QUEUE: RevocationQueue = RevocationQueue::new();
VMM: обработка отзывов
// В interrupts.rs: rust_page_fault_handler()
let mut vmm_guard = KERNEL_SPACE.lock();
if let Some(space) = vmm_guard.as_mut() {
space.process_pending_revocations(); // ← дренирует очередь
match space.handle_fault(fault_addr, write) { ... }
}
// В vmm.rs: AddressSpace
pub fn process_pending_revocations(&mut self) {
let mut needs_flush = false;
while let Some(token) = MMU_REVOCATION_QUEUE.pop() {
self.do_revoke_by_token(token);
needs_flush = true;
}
if needs_flush {
tlb_flush_asid(self.asid);
}
}
Почему именно так?
- Lock-free: отзыв capability не блокируется VMM.
- Batch processing: VMM обрабатывает сразу все накопленные отзывы при следующем page fault, экономя TLB flush'ы.
- Deadlock prevention: process_pending_revocations вызывается до handle_fault, избегая циклических зависимостей между блокировками capability и VMM.
- Простота: кольцевой буфер с CAS — проверенная временем структура.
Ограничения
- Размер: 1024 entry. Если очередь переполняется — паника. Это проектный выбор: переполнение означает, что VMM не успевает обрабатывать отзывы быстрее, чем CNode генерирует новые.
- Однопоточный pop (только page fault handler дренирует очередь).