129 lines
5.0 KiB
Markdown
129 lines
5.0 KiB
Markdown
# Очередь отзыва 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<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)
|
||
}
|
||
```
|
||
|
||
## Глобальный экземпляр
|
||
|
||
```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 дренирует очередь).
|