Skip to main content

atmos/kernel/
allocator.rs

1//! # TLSF (Two-Level Segregated Fit) ヒープアロケータ
2//!
3//! `allocator.rs` - 512MBのカーネルヒープ領域を管理するアロケータです。
4//!
5//! ## 採用理由と仕様
6//! 以前の first-fit フリーリスト方式では、ブラウザのパース処理などでフリーリストが数万ノードに肥大化した際、
7//! 確保・解放が $O(N)$ になり数秒間のフリーズを引き起こしていました。
8//! 本実装に用いられている TLSF 方式(`rlsf` クレートを利用)は、確保・解放ともに **最悪計算量 $O(1)$** が保証されており、
9//! メモリ断片化も有界に抑えられるため、ベアメタル環境において非常に高い安定性とパフォーマンスを発揮します。
10//!
11//! ## パニック回復とスレッド安全性
12//! パニック時のデッドロックを防止するための `Locked` ラッパー(コア別のロック保持追跡と強制解放機能)と、
13//! 割り込みセーフな `spin::Mutex` を統合しています。これにより、ロックを保持したままコアが例外で落ちた場合でも、
14//! 例外ハンドラのログ出力時にアロケータロックの再デッドロックを防止できます。
15
16#![allow(dead_code)]
17
18use core::alloc::{GlobalAlloc, Layout};
19use core::ptr::{null_mut, NonNull};
20use core::sync::atomic::{AtomicBool, Ordering};
21use rlsf::Tlsf;
22
23// 512MB ヒープに適合する TLSF 構成:
24//   GRANULARITY = size_of::<usize>()*4 = 32B、最大ブロック = (32 << FLLEN) - 32。
25//   FLLEN=25 → 最大ブロック ≈ 1GB、MAX_POOL_SIZE = 1<<(5+25) = 1GB ≥ 512MB。
26//   FLBitmap=u32(≥25bit)、SLLEN=16(2 のべき・≤16bit)→ SLBitmap=u16。
27type TheTlsf = Tlsf<'static, u32, u16, 25, 16>;
28
29/// TLSFアロケータの実体を保持し、確保・解放および使用量計測を行う内部構造体。
30struct TlsfHeap {
31    tlsf: TheTlsf,
32    total: usize,
33    used: usize,
34}
35
36impl TlsfHeap {
37    /// 空のアロケータ実体を初期生成します。
38    const fn empty() -> Self {
39        Self {
40            tlsf: Tlsf::new(),
41            total: 0,
42            used: 0,
43        }
44    }
45
46    /// メモリプールを登録してヒープ領域を初期化します。
47    ///
48    /// # Safety
49    /// 与えられた `start` アドレスから `size` バイトのメモリ領域が、排他的に利用可能な物理アドレス領域である必要があります。
50    unsafe fn init(&mut self, start: usize, size: usize) {
51        self.total = size;
52        self.used = 0;
53        let slice = core::ptr::slice_from_raw_parts_mut(start as *mut u8, size);
54        if let Some(nn) = NonNull::new(slice) {
55            self.tlsf.insert_free_block_ptr(nn);
56        }
57    }
58
59    unsafe fn alloc(&mut self, layout: Layout) -> *mut u8 {
60        match self.tlsf.allocate(layout) {
61            Some(p) => {
62                self.used = self.used.saturating_add(layout.size());
63                p.as_ptr()
64            }
65            None => null_mut(),
66        }
67    }
68
69    unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout) {
70        // rlsf::deallocate は確保時と同じ align を要求する(layout.align() で一致)。
71        self.tlsf.deallocate(ptr, layout.align());
72        self.used = self.used.saturating_sub(layout.size());
73    }
74
75    fn free_space(&self) -> usize {
76        self.total.saturating_sub(self.used)
77    }
78}
79
80/// カーネル全体のアロケータのグローバルなインスタンス。
81#[global_allocator]
82static ALLOCATOR: Locked<TlsfHeap> = Locked::new(TlsfHeap::empty());
83
84/// カーネルヒープの大きさ。
85///
86/// 【2026-08-25】この値は **2 箇所で使われる**。
87///
88/// 1. `main.rs` … ヒープをこの大きさで初期化する
89/// 2. `fs/mod.rs` … ヒープの**直後**から RAMDisk を確保する
90///
91/// 両方が別々に `512 * 1024 * 1024` と書いていた。
92/// 片方だけ変えると RAMDisk がヒープに**重なって**、
93/// 記憶装置の書き込みがヒープを破壊する(発見が極めて難しい)。
94/// 定数を 1 つにして、ずれようがないようにする。
95pub const KERNEL_HEAP_SIZE: usize = 512 * 1024 * 1024;
96
97/// カーネルヒープ領域全体の総バイト数。
98static mut TOTAL_HEAP_SIZE: usize = 0;
99
100/// 指定されたメモリ開始アドレスとサイズで、カーネルアロケータを初期化します。
101pub fn init(start: usize, size: usize) {
102    unsafe {
103        TOTAL_HEAP_SIZE = size;
104        ALLOCATOR.lock().init(start, size);
105    }
106}
107
108/// ヒープの (使用中の概算バイト数, 総バイト数) を取得します。
109///
110/// ※ 使用中サイズはアロケータのヘッダやパディング分を考慮しない概算値です。
111pub fn get_heap_stats() -> (usize, usize) {
112    let total = unsafe { TOTAL_HEAP_SIZE };
113    let free = ALLOCATOR.lock().free_space();
114    (total.saturating_sub(free), total)
115}
116
117/// 旧 first-fit の canary 整合性検査との互換スタブです。
118///
119/// TLSF 方式では境界タグ方式によりアロケータが独自に健全性を管理しているため、
120/// この検査は常に `false`(破損未検出)を返します。
121pub fn check_heap_integrity() -> bool {
122    false
123}
124
125/// パニック回復処理において、指定された CPU コア ID が現在ヒープロックを保持している場合、
126/// そのロックを強制解放します。
127pub fn force_release_lock(cid: i16) {
128    unsafe {
129        ALLOCATOR.force_release_if_core(cid);
130    }
131}
132
133unsafe impl GlobalAlloc for Locked<TlsfHeap> {
134    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
135        self.lock().alloc(layout)
136    }
137
138    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
139        if let Some(nn) = NonNull::new(ptr) {
140            self.lock().dealloc(nn, layout);
141        }
142    }
143}
144
145// ---------------------------------------------------------------------------
146// パニック回復対応ロックラッパー
147// ---------------------------------------------------------------------------
148
149/// 割り込みセーフな `spin::Mutex` をラップし、パニック発生時のデッドロックを防止するために
150/// ロックを現在保持している CPU コア ID の追跡記録機能を追加したラッパー構造体。
151pub struct Locked<A> {
152    inner: spin::Mutex<A>,
153    /// ロックを現在保持しているコア ID(-1 の場合は未保持)。
154    holder: core::sync::atomic::AtomicI16,
155}
156
157impl<A> Locked<A> {
158    /// 与えられたアロケータをラップする `Locked` インスタンスを新規生成します。
159    pub const fn new(inner: A) -> Self {
160        Self {
161            inner: spin::Mutex::new(inner),
162            holder: core::sync::atomic::AtomicI16::new(-1),
163        }
164    }
165
166    /// ロックを獲得し、保持するコア ID を自動記録してガードオブジェクトを返します。
167    ///
168    /// # プリエンプション禁止は不要(2026-08-05 検討済み)
169    ///
170    /// 一度 `preempt_disable()` を足したが、**誤りだったので取り消した**。
171    /// 下の `spin::Mutex::lock` は取得時に**割り込みを禁止**する
172    /// (`is_interrupt_enabled` / `disable_interrupts`)。
173    /// 割り込みが来ないのだからタイマ割り込み経由のプリエンプションも起きず、
174    /// 「ロックを握ったまま切り替わって、他スレッドが空回りする」経路は存在しない。
175    ///
176    /// 効果が無いばかりか、`preempt_disable`/`enable` は SeqCst のアトミック
177    /// 読み書きなので、**全確保・全解放**に余計なコストを足すだけだった。
178    /// ここは最も呼ばれる経路なので、意味の無い命令を置いてはいけない。
179    pub fn lock(&self) -> LockedGuard<'_, A> {
180        let guard = self.inner.lock();
181        self.holder.store(
182            crate::kernel::scheduler::core_id() as i16,
183            Ordering::Relaxed,
184        );
185        LockedGuard {
186            guard: Some(guard),
187            holder: &self.holder,
188        }
189    }
190
191    /// パニック回復時に、指定された CPU コア `cid` が現在このロックを保持している場合、
192    /// そのロックを強制解放してリカバリ可能にします。
193    ///
194    /// # Safety
195    /// コア `cid` の現在実行中のタスク(スレッド)がアボートされ、その文脈が破棄される状況でのみ
196    /// 安全に呼び出すことができます。
197    pub unsafe fn force_release_if_core(&self, cid: i16) {
198        if self.holder.load(Ordering::Relaxed) == cid {
199            self.holder.store(-1, Ordering::Relaxed);
200            self.inner.force_unlock();
201        }
202    }
203}
204
205pub struct LockedGuard<'a, A> {
206    guard: Option<spin::MutexGuard<'a, A>>,
207    holder: &'a core::sync::atomic::AtomicI16,
208}
209
210impl<'a, A> Drop for LockedGuard<'a, A> {
211    fn drop(&mut self) {
212        self.holder.store(-1, Ordering::Relaxed);
213        self.guard.take(); // spin ロック解放
214    }
215}
216
217impl<'a, A> core::ops::Deref for LockedGuard<'a, A> {
218    type Target = A;
219    fn deref(&self) -> &A {
220        match self.guard.as_ref() {
221            Some(g) => g,
222            None => loop {
223                core::hint::spin_loop();
224            },
225        }
226    }
227}
228
229impl<'a, A> core::ops::DerefMut for LockedGuard<'a, A> {
230    fn deref_mut(&mut self) -> &mut A {
231        match self.guard.as_mut() {
232            Some(g) => g,
233            None => loop {
234                core::hint::spin_loop();
235            },
236        }
237    }
238}
239
240// ---------------------------------------------------------------------------
241// IRQ セーフな自前スピンロック(旧実装から温存)
242// ---------------------------------------------------------------------------
243
244mod spin {
245    use super::*;
246
247    pub struct Mutex<T> {
248        lock: AtomicBool,
249        data: core::cell::UnsafeCell<T>,
250    }
251
252    unsafe impl<T: Send> Sync for Mutex<T> {}
253    unsafe impl<T: Send> Send for Mutex<T> {}
254
255    pub struct MutexGuard<'a, T> {
256        mutex: &'a Mutex<T>,
257        interrupt_state: bool,
258    }
259
260    impl<T> Mutex<T> {
261        pub const fn new(value: T) -> Self {
262            Self {
263                lock: AtomicBool::new(false),
264                data: core::cell::UnsafeCell::new(value),
265            }
266        }
267
268        pub fn lock(&self) -> MutexGuard<'_, T> {
269            let irq_enabled = is_interrupt_enabled();
270            if irq_enabled {
271                disable_interrupts();
272            }
273
274            while self
275                .lock
276                .compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed)
277                .is_err()
278            {
279                core::hint::spin_loop();
280            }
281
282            MutexGuard {
283                mutex: self,
284                interrupt_state: irq_enabled,
285            }
286        }
287
288        /// パニック回復専用: ガードを経由せずロックを強制解放する。
289        /// 割込み状態は復元しない(呼び出し直後にコンテキスト切替で SPSR から復元される前提)。
290        ///
291        /// # Safety
292        /// 当該クリティカルセクションが破棄される(パニックしたプロセスが exit する)場合のみ。
293        pub unsafe fn force_unlock(&self) {
294            self.lock.store(false, Ordering::Release);
295        }
296    }
297
298    impl<'a, T> core::ops::Deref for MutexGuard<'a, T> {
299        type Target = T;
300        fn deref(&self) -> &Self::Target {
301            unsafe { &*self.mutex.data.get() }
302        }
303    }
304
305    impl<'a, T> core::ops::DerefMut for MutexGuard<'a, T> {
306        fn deref_mut(&mut self) -> &mut Self::Target {
307            unsafe { &mut *self.mutex.data.get() }
308        }
309    }
310
311    impl<'a, T> Drop for MutexGuard<'a, T> {
312        fn drop(&mut self) {
313            self.mutex.lock.store(false, Ordering::Release);
314            if self.interrupt_state {
315                enable_interrupts();
316            }
317        }
318    }
319
320    pub(super) fn is_interrupt_enabled() -> bool {
321        let daif: u64;
322        unsafe {
323            core::arch::asm!("mrs {}, daif", out(reg) daif);
324        }
325        (daif & (1 << 7)) == 0
326    }
327
328    pub(super) fn disable_interrupts() {
329        unsafe {
330            core::arch::asm!("msr daifset, #2", options(nomem, nostack));
331        }
332    }
333
334    pub(super) fn enable_interrupts() {
335        unsafe {
336            core::arch::asm!("msr daifclr, #2", options(nomem, nostack));
337        }
338    }
339}