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