Skip to main content

strat9_kernel/ipc/
n1.rs

1//! N1 Type-Safe IPC : annotation macro and kernel-internal wiring.
2//!
3//! The N1 (TypeSafe) isolation level guarantees that communicating
4//! components share the same address space (Ring 0) and that every
5//! module tagged `#[n1_safe]` contains **zero `unsafe` blocks**.
6//! This is verified by `cargo-geiger` in CI; the macro itself is a
7//! documentation + compile-time annotation marker.
8//!
9//! # Usage
10//!
11//! ```rust
12//! use crate::ipc::n1::n1_safe;
13//!
14//! #[n1_safe]
15//! /// This function is N1-safe: no raw pointers, no `asm!`, no FFI.
16//! fn notify_scheduler(event: N1Event) { /* safe code only */ }
17//! ```
18//!
19//! # Kernel-internal N1 channels
20//!
21//! The following kernel component pairs use `IntrusiveMailbox` for
22//! zero-copy kernel-internal IPC (N1):
23//!
24//! | Producer      | Consumer     | Channel                | Purpose              |
25//! |---------------|--------------|------------------------|----------------------|
26//! | NIC IRQ       | Scheduler    | `NIC_SCHED_MAILBOX`    | Link up/down events  |
27//! | Scheduler     | NIC          | `SCHED_NIC_MAILBOX`    | Flow control hints   |
28
29use crate::ipc::mailbox::IntrusiveMailbox;
30
31// ---------------------------------------------------------------------------
32// n1_safe attribute macro
33// ---------------------------------------------------------------------------
34
35/// Marks a function or module as N1 (Type-Safe IPC) compliant.
36///
37/// N1 compliance means:
38/// - Zero `unsafe` blocks in the annotated code.
39/// - No raw pointer dereferences.
40/// - No `asm!` or FFI calls.
41/// - Only uses safe Rust abstractions.
42///
43/// The actual verification is done by `cargo-geiger` in CI.
44/// This macro is a **documentation marker** and a compile-time assertion
45/// that helps reviewers identify N1-safe boundaries.
46///
47/// # Example
48///
49/// ```ignore
50/// use crate::ipc::n1::n1_safe;
51///
52/// #[n1_safe]
53/// fn handle_nic_event(event: NicEvent) -> Result<(), IpcError> {
54///     NIC_SCHED_MAILBOX.send(&event.encode())?;
55///     Ok(())
56/// }
57/// ```
58#[macro_export]
59macro_rules! n1_safe {
60    // Function form: marks a function as N1-safe.
61    // Attributes are captured and re-emitted; #[inline(always)] is added.
62    ($(#[$attr:meta])* $vis:vis fn $name:ident($($arg:ident: $argty:ty),*) $(-> $ret:ty)? $body:block) => {
63        $(#[$attr])*
64        #[doc(hidden)]
65        #[allow(unused_attributes)]
66        #[inline(always)]
67        $vis fn $name($($arg: $argty),*) $(-> $ret)? $body
68    };
69    // Module form: marks an entire module as N1-safe.
70    ($(#[$attr:meta])* $vis:vis mod $name:ident $body:tt) => {
71        $(#[$attr])*
72        #[doc(hidden)]
73        $vis mod $name $body
74    };
75}
76
77// ---------------------------------------------------------------------------
78// N1 event types
79// ---------------------------------------------------------------------------
80
81/// Events that can flow over N1 kernel-internal channels.
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83#[repr(u8)]
84pub enum N1Event {
85    /// NIC link status changed (up/down).
86    NicLinkChange,
87    /// NIC ring backpressure (consumer too slow).
88    NicBackpressure,
89    /// Memory pressure notification.
90    MemoryPressure,
91    /// Filesystem event (file created, deleted, etc.).
92    FsNotification,
93    /// Scheduler tick / timeslice hint.
94    SchedTick,
95    /// Generic wakeup signal.
96    Wakeup,
97}
98
99impl N1Event {
100    /// Encode this event as a byte slice for mailbox transport.
101    pub fn encode(&self) -> [u8; 2] {
102        [*self as u8, 0u8]
103    }
104
105    /// Decode an event from a byte slice.
106    pub fn decode(buf: &[u8]) -> Option<Self> {
107        if buf.is_empty() {
108            return None;
109        }
110        match buf[0] {
111            0 => Some(Self::NicLinkChange),
112            1 => Some(Self::NicBackpressure),
113            2 => Some(Self::MemoryPressure),
114            3 => Some(Self::FsNotification),
115            4 => Some(Self::SchedTick),
116            5 => Some(Self::Wakeup),
117            _ => None,
118        }
119    }
120}
121
122// ---------------------------------------------------------------------------
123// Kernel-internal N1 mailboxes
124// ---------------------------------------------------------------------------
125
126/// Mailbox for NIC => scheduler notifications (link up/down, backpressure).
127///
128/// The NIC IRQ handler sends events here; the scheduler polls or waits
129/// on this mailbox during idle loops.
130pub static NIC_SCHED_MAILBOX: IntrusiveMailbox = IntrusiveMailbox::new_empty();
131
132/// Mailbox for Scheduler => NIC flow-control hints.
133///
134/// When the scheduler detects that a silo is overloading the NIC, it
135/// sends a throttling hint here. The NIC driver checks this mailbox
136/// during `handle_interrupt()`.
137pub static SCHED_NIC_MAILBOX: IntrusiveMailbox = IntrusiveMailbox::new_empty();
138
139/// Initialise N1 subsystem: pre-allocate mailbox nodes for IRQ-safe push.
140/// Must be called once at kernel init (after heap is available).
141pub fn init() {
142    NIC_SCHED_MAILBOX.preallocate_nodes(32);
143    SCHED_NIC_MAILBOX.preallocate_nodes(32);
144    log::info!("[n1] mailboxes initialised (32 pre-allocated slots each)");
145}
146
147// ---------------------------------------------------------------------------
148// Helper: send an N1 event (safe wrapper)
149// ---------------------------------------------------------------------------
150
151n1_safe! {
152    /// Send an N1 event to the NIC => Scheduler mailbox.
153    /// Uses only safe Rust: `IntrusiveMailbox` push(), no raw pointers.
154    /// Logs a warning if the push fails (heap allocation failure).
155    #[inline(always)] // N1 path
156    pub fn notify_scheduler(event: N1Event) {
157        if NIC_SCHED_MAILBOX.push(&event.encode()).is_err() {
158            log::warn!("[n1] notify_scheduler({:?}): mailbox push failed (OOM?)", event);
159        }
160    }
161}
162
163n1_safe! {
164    /// Send an N1 event to the Scheduler => NIC mailbox.
165    /// Logs a warning if the push fails (heap allocation failure).
166    #[inline(always)] // N1 path
167    pub fn notify_nic_driver(event: N1Event) {
168        if SCHED_NIC_MAILBOX.push(&event.encode()).is_err() {
169            log::warn!("[n1] notify_nic_driver({:?}): mailbox push failed (OOM?)", event);
170        }
171    }
172}
173
174n1_safe! {
175    /// Poll the NIC => Scheduler mailbox for pending events.
176    /// Returns `None` if empty.
177    #[inline(always)] // N1 path
178    pub fn poll_scheduler_events() -> Option<N1Event> {
179        match NIC_SCHED_MAILBOX.pop() {
180            Some(msg) => N1Event::decode(&msg.payload),
181            None => None,
182        }
183    }
184}
185
186n1_safe! {
187    /// Poll the Scheduler => NIC mailbox for pending flow-control hints.
188    #[inline(always)] // N1 path
189    pub fn poll_nic_events() -> Option<N1Event> {
190        match SCHED_NIC_MAILBOX.pop() {
191            Some(msg) => N1Event::decode(&msg.payload),
192            None => None,
193        }
194    }
195}
196
197// ---------------------------------------------------------------------------
198// Tests
199// ---------------------------------------------------------------------------
200
201#[cfg(test)]
202mod tests {
203    use super::*;
204
205    #[test]
206    fn n1_event_roundtrip() {
207        let events = [
208            N1Event::NicLinkChange,
209            N1Event::NicBackpressure,
210            N1Event::MemoryPressure,
211            N1Event::FsNotification,
212            N1Event::SchedTick,
213            N1Event::Wakeup,
214        ];
215        for ev in &events {
216            let encoded = ev.encode();
217            let decoded = N1Event::decode(&encoded).unwrap();
218            assert_eq!(*ev, decoded);
219        }
220    }
221
222    #[test]
223    fn n1_mailbox_send_recv() {
224        notify_scheduler(N1Event::NicLinkChange);
225        assert_eq!(poll_scheduler_events(), Some(N1Event::NicLinkChange));
226        assert_eq!(poll_scheduler_events(), None);
227    }
228}