174 lines
5.1 KiB
Markdown
174 lines
5.1 KiB
Markdown
# Акторы физической памяти (PMActor) и маршрутизатор (PM Router)
|
||
|
||
---
|
||
|
||
## Формат сообщений
|
||
|
||
Вся коммуникация использует упаковку в `u64`:
|
||
|
||
```
|
||
[8b opcode | 16b channel_id | 20b arg1 | 20b arg2]
|
||
```
|
||
|
||
Константы: `OPCODE_SHIFT = 56`, `CHAN_SHIFT = 40`, `ARG1_SHIFT = 20`
|
||
Маски: `CHAN_MASK = 0x0000_FFFF`, `ARG_MASK = 0x000F_FFFF`
|
||
|
||
---
|
||
|
||
## PMActor
|
||
|
||
**Файл**: `src/mem/pm_manages.rs`
|
||
|
||
Распределённый актор физической памяти. Каждый актор владеет фиксрованным диапазоном и управляет им через BuddyAllocator.
|
||
|
||
### PMRequest
|
||
|
||
```rust
|
||
pub enum PMRequest {
|
||
Allocate { size_pages: usize, token_sig: u32, channel_id: u16 },
|
||
Free { local_frame_idx: usize, order: usize },
|
||
Carve { offset_pages: usize, size_pages: usize, channel_id: u16 },
|
||
None, // sentinel (не может быть отправлен)
|
||
}
|
||
```
|
||
|
||
Методы: `pack() -> u64`, `unpack(val: u64) -> Self`.
|
||
|
||
### PMResult
|
||
|
||
```rust
|
||
pub enum PMResult {
|
||
Allocated { cap: Capability, order: usize },
|
||
OutOfMemory { size_pages: usize },
|
||
Carved { cap: Capability },
|
||
Freed { pages_returned: usize },
|
||
}
|
||
```
|
||
|
||
### PMResponse
|
||
|
||
```rust
|
||
pub struct PMResponse {
|
||
pub channel_id: u16,
|
||
pub result: PMResult,
|
||
}
|
||
```
|
||
|
||
### PMActorQueue — Lock-free MPSC
|
||
|
||
Кольцевой буфер на 1024 слота (`AtomicU64`) с cache-line padding.
|
||
|
||
- `send(req)` — CAS loop на `tail` (multi-producer). Отклоняет None.
|
||
- `pop()` — single-consumer. `Acquire`/`Release` ordering.
|
||
|
||
### PMActor
|
||
|
||
```rust
|
||
pub struct PMActor {
|
||
actor_id: u64,
|
||
root_untyped: Capability,
|
||
managed_range: (PhysAddr, PhysAddr), // [start, end)
|
||
queue: PMActorQueue,
|
||
buddy: BuddyAllocator,
|
||
}
|
||
```
|
||
|
||
#### `new(actor_id, root_cap, start, size_bytes) -> Self`
|
||
|
||
Конструктор.
|
||
|
||
#### `submit_request(req) -> Result<(), &str>`
|
||
|
||
Thread-safe: пушит в `queue`.
|
||
|
||
#### `process_messages(&mut self) -> Vec<PMResponse>`
|
||
|
||
Дренирует очередь, обрабатывает каждый запрос через приватные методы.
|
||
|
||
**Только из контекста актора** (`&mut self` гарантирует single consumer).
|
||
|
||
#### Приватные обработчики
|
||
|
||
**`handle_allocate(size_pages, token_sig, channel_id) -> Option<PMResponse>`**
|
||
|
||
1. `buddy.alloc_pages(size_pages)` → `(local_idx, order)`
|
||
2. Строит сильную дочернюю Capability с уникальным `token_sig`
|
||
3. Возвращает `Allocated { cap, order }`
|
||
4. При неудаче: `OutOfMemory` (планируется ballooning)
|
||
|
||
**`handle_free(local_frame_idx, order) -> Option<PMResponse>`**
|
||
|
||
1. Валидация границ
|
||
2. `buddy.free(local_frame_idx, order)`
|
||
3. Возвращает None (маршрутизация не нужна)
|
||
|
||
**`handle_carve(offset_pages, size_pages, channel_id) -> Option<PMResponse>`**
|
||
|
||
1. Валидация диапазона
|
||
2. Возвращает `Carved { cap }`
|
||
3. Не взаимодействует с buddy
|
||
|
||
---
|
||
|
||
## PMRouter
|
||
|
||
**Файл**: `src/mem/pm_router.rs`
|
||
|
||
Глобальная ткань маршрутизации: 65536 lock-free каналов.
|
||
|
||
### Константы
|
||
|
||
- `CHANNEL_COUNT = 65536`
|
||
- Состояния: `STATE_FREE = 0`, `STATE_PENDING = 1`, `STATE_READY = 2`
|
||
|
||
### Channel — `#[repr(align(64))]`
|
||
|
||
```rust
|
||
struct Channel {
|
||
state: AtomicU8,
|
||
next_free: AtomicU16,
|
||
result: UnsafeCell<Option<PMResult>>,
|
||
}
|
||
```
|
||
|
||
### PMRouter
|
||
|
||
```rust
|
||
pub struct PMRouter {
|
||
channels: Box<[Channel]>, // 65536 каналов
|
||
free_head: AtomicU16, // голова free list (0 = sentinel)
|
||
}
|
||
```
|
||
|
||
### GlobalRouter
|
||
|
||
```rust
|
||
static ROUTER: GlobalRouter; // синглтон
|
||
```
|
||
|
||
### `init()`
|
||
|
||
1. Создаёт Vec из 65536 каналов, соединённых в linked list (каждый `next_free` указывает на следующий)
|
||
2. Преобразует в `Box<[Channel]>`
|
||
3. Сохраняет в `ROUTER.inner`, устанавливает `is_ready`
|
||
|
||
### `alloc_channel() -> Option<u16>`
|
||
|
||
CAS-цикл на `free_head`: извлекает голову списка, устанавливает `state = PENDING`.
|
||
|
||
### `route_responses(responses: Vec<PMResponse>)`
|
||
|
||
Для каждого PMResponse с ненулевым `channel_id`: сохраняет `result` в канале, устанавливает `state = READY`.
|
||
|
||
### `wait_for_response(id: u16) -> PMResult`
|
||
|
||
Spin-wait до `state == READY`. Забирает результат, пушит канал обратно в freelist, возвращает результат.
|
||
|
||
### `request_and_wait(actor, req_builder) -> PMResult`
|
||
|
||
Convenience: аллоцирует канал, строит запрос с channel_id, отправляет актору, ждёт ответ. (process_messages должен быть вызван внешне.)
|
||
|
||
### `dispatch(responses: Vec<PMResponse>)`
|
||
|
||
Точка входа для маршрутизации ответов через `PMActor::route_responses()` (вызов на глобальном роутере).
|