Files
Elyz/kernel/docs/pmactor.md
2026-07-06 00:25:49 +03:00

5.1 KiB
Raw Blame History

Акторы физической памяти (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

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

pub enum PMResult {
    Allocated { cap: Capability, order: usize },
    OutOfMemory { size_pages: usize },
    Carved { cap: Capability },
    Freed { pages_returned: usize },
}

PMResponse

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

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))]

struct Channel {
    state: AtomicU8,
    next_free: AtomicU16,
    result: UnsafeCell<Option<PMResult>>,
}

PMRouter

pub struct PMRouter {
    channels: Box<[Channel]>,  // 65536 каналов
    free_head: AtomicU16,      // голова free list (0 = sentinel)
}

GlobalRouter

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() (вызов на глобальном роутере).