2.8 KiB
2.8 KiB
Очередь отзыва (RevocationQueue)
Файл: src/events.rs
Lock-free MPSC (Multi-Producer Single-Consumer) очередь для уведомления VMM об отзыве capability.
RevocationQueue
pub struct RevocationQueue {
buffer: [AtomicU64; QUEUE_SIZE], // 1024 слота
_pad0: [u8; 64], // cache-line padding
head: AtomicUsize, // индекс потребителя (VMM)
_pad1: [u8; 64], // ещё padding
tail: AtomicUsize, // индекс производителя (CNode)
}
Cache-line padding (64 байта) предотвращает false sharing между head и tail на разных кэш-линиях.
Константы
QUEUE_SIZE = 1024QUEUE_MASK = 1023
new() -> Self
Все слоты = 0, head = 0, tail = 0.
push(token_sig: u64) -> Result<(), &'static str>
- Читает
tail(Relaxed) - CAS-цикл: пытается захватить следующий слот
- Если буфер полон (
tail - head == QUEUE_SIZE): ошибка - Записывает
token_sigвbuffer[tail & QUEUE_MASK](Release ordering) - Обновляет
tail(Release)
Производители: CNode::revoke() — при каждом отзыве пушит token_sig.
pop() -> Option<u64>
- Читает
tail(Acquire) иhead(Relaxed) - Если
head == tail: буфер пуст, None - Читает
buffer[head & QUEUE_MASK](Acquire) - Обновляет
head(Release) - Возвращает token
Потребитель: VMM — AddressSpace::process_pending_revocations().
Глобальный экземпляр
pub static MMU_REVOCATION_QUEUE: RevocationQueue = RevocationQueue::new();
Полный цикл ревокации
CNode::revoke(slot_idx)
→ revoke_internal(slot_idx) // рекурсивное уничтожение потомков
→ slot = Empty
→ MMU_REVOCATION_QUEUE.push(token_sig)
... позже, при page fault ...
rust_page_fault_handler()
→ process_deferred_mmu_events()
→ KERNEL_SPACE.lock()
→ process_pending_revocations()
→ while let Some(token) = MMU_REVOCATION_QUEUE.pop()
→ revoke_by_token(token)
→ unmap matching VMAs (кроме PINNED)
→ TLB flush
→ KERNEL_SPACE.lock()
→ handle_fault(fault_addr, write) // обработка исходного fault
Почему такая очерёдность? Ревокации обрабатываются до захвата VMM для page fault, чтобы избежать deadlock'а: и revoke, и handle_fault хотят lock на KERNEL_SPACE.