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}