ostd/cpu/local/
static_cpu_local.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! Statically-allocated CPU-local objects.
4
5#![cfg_attr(
6    any(target_arch = "riscv64", target_arch = "loongarch64"),
7    expect(dead_code)
8)]
9
10use core::marker::PhantomData;
11
12use super::{__cpu_local_end, __cpu_local_start, AnyStorage, CpuLocal};
13use crate::{arch, cpu::CpuId, irq::DisabledLocalIrqGuard, util::id_set::Id};
14
15/// Defines a statically-allocated CPU-local variable.
16///
17/// The accessors of the CPU-local variables are defined with [`CpuLocal`].
18///
19/// You can get the reference to the inner object on one CPU by calling
20/// [`CpuLocal::get_on_cpu`]. Also if you intend to access the inner object
21/// on the current CPU, you can use [`CpuLocal::get_with`]. The latter
22/// accessors can be used even if the inner object is not `Sync`.
23///
24/// # Example
25///
26/// ```rust
27/// use ostd::{cpu_local, cpu::PinCurrentCpu, task::disable_preempt, trap};
28/// use core::{sync::atomic::{AtomicU32, Ordering}, cell::Cell};
29///
30/// cpu_local! {
31///     static FOO: AtomicU32 = AtomicU32::new(1);
32///     pub static BAR: Cell<usize> = Cell::new(2);
33/// }
34///
35/// fn not_an_atomic_function() {
36///     let preempt_guard = disable_preempt();
37///     let ref_of_foo = FOO.get_on_cpu(preempt_guard.current_cpu());
38///     let val_of_foo = ref_of_foo.load(Ordering::Relaxed);
39///     println!("FOO VAL: {}", val_of_foo);
40///
41///     let irq_guard = irq::disable_local();
42///     let bar_guard = BAR.get_with(&irq_guard);
43///     let val_of_bar = bar_guard.get();
44///     println!("BAR VAL: {}", val_of_bar);
45/// }
46/// ```
47#[macro_export]
48macro_rules! cpu_local {
49    ($( $(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; )*) => {
50        $(
51            // SAFETY: This is properly handled in the linker script.
52            #[unsafe(link_section = ".cpu_local")]
53            $(#[$attr])* $vis static $name: $crate::cpu::local::StaticCpuLocal<$t> = {
54                let val = $init;
55                // SAFETY: The per-CPU variable instantiated is statically
56                // stored in the special `.cpu_local` section.
57                unsafe {
58                    $crate::cpu::local::CpuLocal::__new_static(val)
59                }
60            };
61        )*
62    };
63}
64
65/// A static storage for a CPU-local variable of type `T`.
66///
67/// Such a CPU-local storage is not intended to be allocated directly.
68/// Use the `cpu_local` macro instead.
69pub struct StaticStorage<T: 'static>(T);
70
71impl<T: 'static> StaticStorage<T> {
72    /// Gets access to the underlying value through a raw pointer.
73    ///
74    /// This method is safe, but using the returned pointer will be unsafe.
75    fn as_ptr(&self) -> *const T {
76        super::is_used::debug_set_true();
77
78        let offset = self.get_offset();
79
80        let local_base = arch::cpu::local::get_base() as usize;
81        let local_va = local_base + offset;
82
83        // A sanity check about the alignment.
84        debug_assert_eq!(local_va % align_of::<T>(), 0);
85
86        local_va as *const T
87    }
88
89    /// Gets the offset of the CPU-local object in the CPU-local area.
90    fn get_offset(&self) -> usize {
91        let bsp_va = self as *const _ as usize;
92        let bsp_base = __cpu_local_start as *const () as usize;
93        // The implementation should ensure that the CPU-local object resides in the `.cpu_local`.
94        debug_assert!(bsp_va + size_of::<T>() <= __cpu_local_end as *const () as usize);
95
96        bsp_va - bsp_base
97    }
98}
99
100unsafe impl<T: 'static> AnyStorage<T> for StaticStorage<T> {
101    fn get_ptr_on_current(&self, _guard: &DisabledLocalIrqGuard) -> *const T {
102        self.as_ptr()
103    }
104
105    fn get_ptr_on_target(&self, cpu_id: CpuId) -> *const T {
106        super::is_used::debug_set_true();
107
108        let cpu_id = cpu_id.as_usize();
109
110        // If on the BSP, just use the statically linked storage.
111        if cpu_id == 0 {
112            return &self.0 as *const T;
113        }
114
115        let base = {
116            // SAFETY: At this time we have a non-BSP `CpuId`, which means that
117            // `init_cpu_nums` must have been called, so `copy_bsp_for_ap` must
118            // also have been called (see the implementation of `cpu::init_on_bsp`),
119            // so `CPU_LOCAL_STORAGES` must already be initialized.
120            let storages = unsafe { super::CPU_LOCAL_STORAGES.get_unchecked() };
121            // SAFETY: `cpu_id` is guaranteed to be in range because the type
122            // invariant of `CpuId`.
123            let storage = unsafe { *storages.get_unchecked(cpu_id - 1) };
124            crate::mm::paddr_to_vaddr(storage)
125        };
126
127        let offset = self.get_offset();
128        (base + offset) as *const T
129    }
130
131    fn get_mut_ptr_on_target(&mut self, _: CpuId) -> *mut T {
132        // `StaticStorage<T>` does not support `get_mut_ptr_on_target`, because
133        // statically-allocated CPU-local objects do not require per-CPU initialization.
134        panic!("Can't get the mutable pointer of StaticStorage<T> on a target CPU.");
135    }
136}
137
138impl<T: 'static> CpuLocal<T, StaticStorage<T>> {
139    /// Creates a new statically-allocated CPU-local object.
140    ///
141    /// Please do not call this function directly. Instead, use the
142    /// `cpu_local!` macro.
143    ///
144    /// # Safety
145    ///
146    /// The caller should ensure that the object initialized by this
147    /// function resides in the `.cpu_local` section. Otherwise the
148    /// behavior is undefined.
149    #[doc(hidden)]
150    pub const unsafe fn __new_static(val: T) -> Self {
151        Self {
152            storage: StaticStorage(val),
153            phantom: PhantomData,
154        }
155    }
156
157    /// Gets access to the underlying value through a raw pointer.
158    ///
159    /// This method is safe, but using the returned pointer will be unsafe.
160    pub(crate) fn as_ptr(&self) -> *const T {
161        self.storage.as_ptr()
162    }
163}