ostd/cpu/local/
cell.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! The implementation of CPU-local variables that have inner mutability.
4
5use core::cell::UnsafeCell;
6
7use super::{__cpu_local_end, __cpu_local_start, single_instr::*};
8use crate::arch;
9
10/// Defines an inner-mutable CPU-local variable.
11///
12/// The accessors of the CPU-local variables are defined with [`CpuLocalCell`].
13///
14/// It should be noted that if the interrupts or preemption is enabled, two
15/// operations on the same CPU-local cell variable may access different objects
16/// since the task may live on different CPUs.
17///
18/// # Example
19///
20/// ```rust
21/// use ostd::cpu_local_cell;
22///
23/// cpu_local_cell! {
24///     static FOO: u32 = 1;
25///     pub static BAR: *const usize = core::ptr::null();
26/// }
27///
28/// fn not_an_atomic_function() {
29///     let bar_var: usize = 1;
30///     BAR.store(&bar_var as *const _);
31///     // Note that the value of `BAR` here doesn't necessarily equal to the address
32///     // of `bar_var`, since the task may be preempted and moved to another CPU.
33///     // You can avoid this by disabling interrupts (and preemption, if needed).
34///     println!("BAR VAL: {:?}", BAR.load());
35///
36///     let _irq_guard = ostd::irq::disable_local();
37///     println!("1st FOO VAL: {:?}", FOO.load());
38///     // No surprises here, the two accesses must result in the same value.
39///     println!("2nd FOO VAL: {:?}", FOO.load());
40/// }
41/// ```
42#[macro_export]
43macro_rules! cpu_local_cell {
44    ($( $(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; )*) => {
45        $(
46            // SAFETY: This is properly handled in the linker script.
47            #[unsafe(link_section = ".cpu_local")]
48            $(#[$attr])* $vis static $name: $crate::cpu::local::CpuLocalCell<$t> = {
49                let val = $init;
50                // SAFETY: The CPU local variable instantiated is statically
51                // stored in the special `.cpu_local` section.
52                unsafe {
53                    $crate::cpu::local::CpuLocalCell::__new(val)
54                }
55            };
56        )*
57    };
58}
59
60/// Inner mutable CPU-local objects.
61///
62/// CPU-local cell objects are only accessible from the current CPU. When
63/// accessing an underlying object using the same `CpuLocalCell` instance, the
64/// actually accessed object is always on the current CPU. So in a preemptive
65/// kernel task, the operated object may change if interrupts are enabled.
66///
67/// The inner mutability is provided by single instruction operations, and the
68/// CPU-local cell objects will not ever be shared between CPUs. So it is safe
69/// to modify the inner value without any locks.
70///
71/// You should only create the CPU-local cell object using the macro
72/// [`cpu_local_cell!`].
73///
74/// Please exercise extreme caution when using `CpuLocalCell`. In most cases,
75/// it is necessary to disable interrupts or preemption when using it to prevent
76/// the operated object from being changed, which can lead to race conditions.
77///
78/// For the difference between [`super::CpuLocal`] and [`CpuLocalCell`], see
79/// [`super`].
80pub struct CpuLocalCell<T: 'static>(UnsafeCell<T>);
81
82impl<T: 'static> CpuLocalCell<T> {
83    /// Initializes a CPU-local object.
84    ///
85    /// Please do not call this function directly. Instead, use the
86    /// `cpu_local!` macro.
87    ///
88    /// # Safety
89    ///
90    /// The caller should ensure that the object initialized by this
91    /// function resides in the `.cpu_local` section. Otherwise the
92    /// behavior is undefined.
93    #[doc(hidden)]
94    pub const unsafe fn __new(val: T) -> Self {
95        Self(UnsafeCell::new(val))
96    }
97
98    /// Gets access to the underlying value through a raw pointer.
99    ///
100    /// This function calculates the virtual address of the CPU-local object
101    /// based on the CPU-local base address and the offset in the BSP.
102    ///
103    /// This method is safe, but using the returned pointer will be unsafe.
104    /// Specifically,
105    /// - Preemption should be disabled from the time this method is called
106    ///   to the time the pointer is used. Otherwise, the pointer may point
107    ///   to the variable on another CPU, making it difficult or impossible
108    ///   to determine if the data can be borrowed.
109    /// - If the variable can be used in interrupt handlers, borrowing the
110    ///   data should be done with interrupts disabled. Otherwise, more care
111    ///   must be taken to ensure that the borrowing rules are correctly
112    ///   enforced, since the interrupts may come asynchronously.
113    pub fn as_mut_ptr(&'static self) -> *mut T {
114        super::is_used::debug_set_true();
115
116        let offset = {
117            let bsp_va = self as *const _ as usize;
118            let bsp_base = __cpu_local_start as *const () as usize;
119            // The implementation should ensure that the CPU-local object resides in the `.cpu_local`.
120            debug_assert!(bsp_va + size_of::<T>() <= __cpu_local_end as *const () as usize);
121
122            bsp_va - bsp_base as usize
123        };
124
125        let local_base = arch::cpu::local::get_base() as usize;
126        let local_va = local_base + offset;
127
128        // A sanity check about the alignment.
129        debug_assert_eq!(local_va % align_of::<T>(), 0);
130
131        local_va as *mut T
132    }
133}
134
135// SAFETY: At any given time, only one task can access the inner value T
136// of a cpu-local variable even if `T` is not `Sync`.
137unsafe impl<T: 'static> Sync for CpuLocalCell<T> {}
138
139// Prevent valid instances of CpuLocalCell from being copied to any memory
140// area outside the `.cpu_local` section.
141impl<T: 'static> !Copy for CpuLocalCell<T> {}
142impl<T: 'static> !Clone for CpuLocalCell<T> {}
143
144// In general, it does not make any sense to send instances of CpuLocalCell to
145// other tasks as they should live on other CPUs to make sending useful.
146impl<T: 'static> !Send for CpuLocalCell<T> {}
147
148// Accessors for the per-CPU objects whose type implements the single-
149// instruction operations.
150
151impl<T: 'static + SingleInstructionAddAssign<T>> CpuLocalCell<T> {
152    /// Adds a value to the per-CPU object in a single instruction.
153    ///
154    /// This operation wraps on overflow/underflow.
155    ///
156    /// Note that this memory operation will not be elided or reordered by the
157    /// compiler since it is a black-box.
158    pub fn add_assign(&'static self, rhs: T) {
159        let offset = self as *const _ as usize - __cpu_local_start as *const () as usize;
160        // SAFETY: The CPU-local object is defined in the `.cpu_local` section,
161        // so the pointer to the object is valid. And the reference is never shared.
162        unsafe {
163            T::add_assign(offset as *mut T, rhs);
164        }
165    }
166}
167
168impl<T: 'static + SingleInstructionSubAssign<T>> CpuLocalCell<T> {
169    /// Subtracts a value to the per-CPU object in a single instruction.
170    ///
171    /// This operation wraps on overflow/underflow.
172    ///
173    /// Note that this memory operation will not be elided or reordered by the
174    /// compiler since it is a black-box.
175    pub fn sub_assign(&'static self, rhs: T) {
176        let offset = self as *const _ as usize - __cpu_local_start as *const () as usize;
177        // SAFETY: The CPU-local object is defined in the `.cpu_local` section,
178        // so the pointer to the object is valid. And the reference is never shared.
179        unsafe {
180            T::sub_assign(offset as *mut T, rhs);
181        }
182    }
183}
184
185impl<T: 'static + SingleInstructionBitAndAssign<T>> CpuLocalCell<T> {
186    /// Bitwise ANDs a value to the per-CPU object in a single instruction.
187    ///
188    /// Note that this memory operation will not be elided or reordered by the
189    /// compiler since it is a black-box.
190    pub fn bitand_assign(&'static self, rhs: T) {
191        let offset = self as *const _ as usize - __cpu_local_start as *const () as usize;
192        // SAFETY: The CPU-local object is defined in the `.cpu_local` section,
193        // so the pointer to the object is valid. And the reference is never shared.
194        unsafe {
195            T::bitand_assign(offset as *mut T, rhs);
196        }
197    }
198}
199
200impl<T: 'static + SingleInstructionBitOrAssign<T>> CpuLocalCell<T> {
201    /// Bitwise ORs a value to the per-CPU object in a single instruction.
202    ///
203    /// Note that this memory operation will not be elided or reordered by the
204    /// compiler since it is a black-box.
205    pub fn bitor_assign(&'static self, rhs: T) {
206        let offset = self as *const _ as usize - __cpu_local_start as *const () as usize;
207        // SAFETY: The CPU-local object is defined in the `.cpu_local` section,
208        // so the pointer to the object is valid. And the reference is never shared.
209        unsafe {
210            T::bitor_assign(offset as *mut T, rhs);
211        }
212    }
213}
214
215impl<T: 'static + SingleInstructionBitXorAssign<T>> CpuLocalCell<T> {
216    /// Bitwise XORs a value to the per-CPU object in a single instruction.
217    ///
218    /// Note that this memory operation will not be elided or reordered by the
219    /// compiler since it is a black-box.
220    pub fn bitxor_assign(&'static self, rhs: T) {
221        let offset = self as *const _ as usize - __cpu_local_start as *const () as usize;
222        // SAFETY: The CPU-local object is defined in the `.cpu_local` section,
223        // so the pointer to the object is valid. And the reference is never shared.
224        unsafe {
225            T::bitxor_assign(offset as *mut T, rhs);
226        }
227    }
228}
229
230impl<T: 'static + SingleInstructionLoad> CpuLocalCell<T> {
231    /// Gets the value of the per-CPU object in a single instruction.
232    ///
233    /// Note that this memory operation will not be elided or reordered by the
234    /// compiler since it is a black-box.
235    pub fn load(&'static self) -> T {
236        let offset = self as *const _ as usize - __cpu_local_start as *const () as usize;
237        // SAFETY: The CPU-local object is defined in the `.cpu_local` section,
238        // so the pointer to the object is valid.
239        unsafe { T::load(offset as *const T) }
240    }
241}
242
243impl<T: 'static + SingleInstructionStore> CpuLocalCell<T> {
244    /// Writes a value to the per-CPU object in a single instruction.
245    ///
246    /// Note that this memory operation will not be elided or reordered by the
247    /// compiler since it is a black-box.
248    pub fn store(&'static self, val: T) {
249        let offset = self as *const _ as usize - __cpu_local_start as *const () as usize;
250        // SAFETY: The CPU-local object is defined in the `.cpu_local` section,
251        // so the pointer to the object is valid. And the reference is never shared.
252        unsafe {
253            T::store(offset as *mut T, val);
254        }
255    }
256}