Files
Elyz/kernel/docs/events/introduction.md
2026-07-07 16:40:41 +03:00

5.0 KiB
Raw Blame History

Очередь отзыва 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);
    }
}

Почему именно так?

  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 дренирует очередь).