# Очередь отзыва 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 — детали ```rust 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) ```rust 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() ```rust pub fn pop(&self) -> Option { 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) } ``` ## Глобальный экземпляр ```rust pub static MMU_REVOCATION_QUEUE: RevocationQueue = RevocationQueue::new(); ``` ## VMM: обработка отзывов ```rust // В 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) { ... } } ``` ```rust // В 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); } } ``` ## Почему именно так? 1. **Lock-free**: отзыв capability не блокируется VMM. 2. **Batch processing**: VMM обрабатывает сразу все накопленные отзывы при следующем page fault, экономя TLB flush'ы. 3. **Deadlock prevention**: process_pending_revocations вызывается **до** handle_fault, избегая циклических зависимостей между блокировками capability и VMM. 4. **Простота**: кольцевой буфер с CAS — проверенная временем структура. ## Ограничения - Размер: 1024 entry. Если очередь переполняется — **паника**. Это проектный выбор: переполнение означает, что VMM не успевает обрабатывать отзывы быстрее, чем CNode генерирует новые. - Однопоточный pop (только page fault handler дренирует очередь).