ostd/cpu/local/
single_instr.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
// SPDX-License-Identifier: MPL-2.0

//! Extensions for CPU-local types that allows single-instruction operations.
//!
//! For some per-CPU objects, fetching or modifying the values of them can be
//! done in a single instruction. Then we would avoid turning off interrupts
//! when accessing them, which incurs non-trivial overhead.
//!
//! These traits are the architecture-specific interface for single-instruction
//! operations. The architecture-specific module can implement these traits for
//! common integer types. For architectures that don't support such single-
//! instruction operations, we emulate a single-instruction implementation by
//! disabling interruptions and preemptions.
//!
//! Currently we implement some of the [`core::ops`] operations. Bitwise shift
//! implementations are missing. Also for less-fundamental types such as
//! enumerations or boolean types, the caller can cast it themselves to the
//! integer types, for which the operations are implemented.
//!
//! # Safety
//!
//! All operations in the provided traits are unsafe, and the caller should
//! ensure that the offset is a valid pointer to a static [`CpuLocalCell`]
//! object. The offset of the object is relative to the base address of the
//! CPU-local storage. These operations are not atomic. Accessing the same
//! address from multiple CPUs produces undefined behavior.
//!
//! [`CpuLocalCell`]: crate::cpu::local::CpuLocalCell

/// An interface for architecture-specific single-instruction add operation.
pub trait SingleInstructionAddAssign<Rhs = Self> {
    /// Adds a value to the per-CPU object.
    ///
    /// This operation wraps on overflow.
    ///
    /// # Safety
    ///
    /// Please refer to the module-level documentation of [`self`].
    unsafe fn add_assign(offset: *mut Self, rhs: Rhs);
}

impl<T: num_traits::WrappingAdd + Copy> SingleInstructionAddAssign<T> for T {
    default unsafe fn add_assign(offset: *mut Self, rhs: T) {
        let _guard = crate::trap::irq::disable_local();
        let base = crate::arch::cpu::local::get_base() as usize;
        let addr = (base + offset as usize) as *mut Self;
        // SAFETY:
        // 1. `addr` represents the address of a CPU-local variable.
        // 2. The variable is only accessible in the current CPU, is
        //    `Copy`, and is is never borrowed, so it is valid to
        //    read/write.
        unsafe { addr.write(addr.read().wrapping_add(&rhs)) };
    }
}

/// An interface for architecture-specific single-instruction subtract operation.
pub trait SingleInstructionSubAssign<Rhs = Self> {
    /// Subtracts a value to the per-CPU object.
    ///
    /// This operation wraps on overflow.
    ///
    /// # Safety
    ///
    /// Please refer to the module-level documentation of [`self`].
    unsafe fn sub_assign(offset: *mut Self, rhs: Rhs);
}

impl<T: num_traits::WrappingSub + Copy> SingleInstructionSubAssign<T> for T {
    default unsafe fn sub_assign(offset: *mut Self, rhs: T) {
        let _guard = crate::trap::irq::disable_local();
        let base = crate::arch::cpu::local::get_base() as usize;
        let addr = (base + offset as usize) as *mut Self;
        // SAFETY: Same as `add_assign`.
        unsafe { addr.write(addr.read().wrapping_sub(&rhs)) };
    }
}

/// An interface for architecture-specific single-instruction bitwise OR.
pub trait SingleInstructionBitOrAssign<Rhs = Self> {
    /// Bitwise ORs a value to the per-CPU object.
    ///
    /// # Safety
    ///
    /// Please refer to the module-level documentation of [`self`].
    unsafe fn bitor_assign(offset: *mut Self, rhs: Rhs);
}

impl<T: core::ops::BitOr<Output = T> + Copy> SingleInstructionBitOrAssign<T> for T {
    default unsafe fn bitor_assign(offset: *mut Self, rhs: T) {
        let _guard = crate::trap::irq::disable_local();
        let base = crate::arch::cpu::local::get_base() as usize;
        let addr = (base + offset as usize) as *mut Self;
        // SAFETY: Same as `add_assign`.
        unsafe { addr.write(addr.read() | rhs) };
    }
}

/// An interface for architecture-specific single-instruction bitwise AND.
pub trait SingleInstructionBitAndAssign<Rhs = Self> {
    /// Bitwise ANDs a value to the per-CPU object.
    ///
    /// # Safety
    ///
    /// Please refer to the module-level documentation of [`self`].
    unsafe fn bitand_assign(offset: *mut Self, rhs: Rhs);
}

impl<T: core::ops::BitAnd<Output = T> + Copy> SingleInstructionBitAndAssign<T> for T {
    default unsafe fn bitand_assign(offset: *mut Self, rhs: T) {
        let _guard = crate::trap::irq::disable_local();
        let base = crate::arch::cpu::local::get_base() as usize;
        let addr = (base + offset as usize) as *mut Self;
        // SAFETY: Same as `add_assign`.
        unsafe { addr.write(addr.read() & rhs) };
    }
}

/// An interface for architecture-specific single-instruction bitwise XOR.
pub trait SingleInstructionBitXorAssign<Rhs = Self> {
    /// Bitwise XORs a value to the per-CPU object.
    ///
    /// # Safety
    ///
    /// Please refer to the module-level documentation of [`self`].
    unsafe fn bitxor_assign(offset: *mut Self, rhs: Rhs);
}

impl<T: core::ops::BitXor<Output = T> + Copy> SingleInstructionBitXorAssign<T> for T {
    default unsafe fn bitxor_assign(offset: *mut Self, rhs: T) {
        let _guard = crate::trap::irq::disable_local();
        let base = crate::arch::cpu::local::get_base() as usize;
        let addr = (base + offset as usize) as *mut Self;
        // SAFETY: Same as `add_assign`.
        unsafe { addr.write(addr.read() ^ rhs) };
    }
}

/// An interface for architecture-specific single-instruction get operation.
pub trait SingleInstructionLoad {
    /// Gets the value of the per-CPU object.
    ///
    /// # Safety
    ///
    /// Please refer to the module-level documentation of [`self`].
    unsafe fn load(offset: *const Self) -> Self;
}

impl<T: Copy> SingleInstructionLoad for T {
    default unsafe fn load(offset: *const Self) -> Self {
        let _guard = crate::trap::irq::disable_local();
        let base = crate::arch::cpu::local::get_base() as usize;
        let ptr = (base + offset as usize) as *const Self;
        // SAFETY: Same as `add_assign`.
        unsafe { ptr.read() }
    }
}

/// An interface for architecture-specific single-instruction set operation.
pub trait SingleInstructionStore {
    /// Writes a value to the per-CPU object.
    ///
    /// # Safety
    ///
    /// Please refer to the module-level documentation of [`self`].
    unsafe fn store(offset: *mut Self, val: Self);
}

impl<T: Copy> SingleInstructionStore for T {
    default unsafe fn store(offset: *mut Self, val: Self) {
        let _guard = crate::trap::irq::disable_local();
        let base = crate::arch::cpu::local::get_base() as usize;
        let ptr = (base + offset as usize) as *mut Self;
        // SAFETY: Same as `add_assign`.
        unsafe { ptr.write(val) };
    }
}