1use super::{
29 guardian::{Guardian, GuardianState, IrqDisabled},
30 IrqDisabledToken,
31};
32use core::{
33 cell::UnsafeCell,
34 marker::PhantomData,
35 mem::ManuallyDrop,
36 ops::{Deref, DerefMut},
37 sync::atomic::{AtomicBool, AtomicUsize, Ordering},
38};
39
40static DEBUG_WATCH_LOCK_ADDR: AtomicUsize = AtomicUsize::new(usize::MAX);
41
42pub struct SpinLock<T: ?Sized, G: Guardian = IrqDisabled> {
52 locked: AtomicBool,
53 owner_cpu: AtomicUsize,
56 _guardian: PhantomData<G>,
57 data: UnsafeCell<T>,
60}
61
62unsafe impl<T: ?Sized + Send, G: Guardian> Sync for SpinLock<T, G> {}
64unsafe impl<T: ?Sized + Send, G: Guardian> Send for SpinLock<T, G> {}
65
66impl<T, G: Guardian> SpinLock<T, G> {
67 pub const fn new(data: T) -> Self {
71 SpinLock {
72 locked: AtomicBool::new(false),
73 owner_cpu: AtomicUsize::new(usize::MAX),
74 _guardian: PhantomData,
75 data: UnsafeCell::new(data),
76 }
77 }
78}
79
80impl<T: ?Sized, G: Guardian> SpinLock<T, G> {
81 pub fn lock(&self) -> SpinLockGuard<'_, T, G> {
88 let state = G::enter();
89 let mut spins: usize = 0;
90 let this_cpu = crate::arch::x86_64::percpu::current_cpu_index();
91
92 while self
93 .locked
94 .compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed)
95 .is_err()
96 {
97 spins = spins.saturating_add(1);
98 if spins == 5_000_000 {
99 let owner = self.owner_cpu.load(Ordering::Relaxed);
100 crate::serial_println!(
101 "[trace][spin] long-wait lock={:#x} cpu={} owner_cpu={}",
102 self as *const _ as *const () as usize,
103 this_cpu,
104 owner,
105 );
106 spins = 0;
107 }
108 core::hint::spin_loop();
109 }
110 self.owner_cpu.store(this_cpu, Ordering::Relaxed);
111 emit_trace_e9(self as *const _ as *const () as usize, 0);
113
114 SpinLockGuard {
115 lock: self,
116 state: ManuallyDrop::new(state),
117 }
118 }
119
120 pub fn try_lock(&self) -> Option<SpinLockGuard<'_, T, G>> {
122 let state = G::enter();
123 if self
124 .locked
125 .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
126 .is_ok()
127 {
128 let this_cpu = crate::arch::x86_64::percpu::current_cpu_index();
129 self.owner_cpu.store(this_cpu, Ordering::Relaxed);
130 emit_trace_e9(self as *const _ as *const () as usize, 0);
131 Some(SpinLockGuard {
132 lock: self,
133 state: ManuallyDrop::new(state),
134 })
135 } else {
136 G::exit(state);
137 None
138 }
139 }
140
141 pub fn owner_cpu(&self) -> usize {
143 self.owner_cpu.load(Ordering::Relaxed)
144 }
145
146 pub fn get_mut(&mut self) -> &mut T {
152 self.data.get_mut()
153 }
154}
155
156impl<T: ?Sized> SpinLock<T, IrqDisabled> {
159 pub fn try_lock_no_irqsave(&self) -> Option<SpinLockGuard<'_, T, IrqDisabled>> {
165 let token = match IrqDisabledToken::verify() {
166 Some(token) => token,
167 None => {
168 unsafe { core::arch::asm!("mov al, 'V'; out 0xe9, al", out("al") _) };
172 return None;
173 }
174 };
175 if self
176 .locked
177 .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
178 .is_ok()
179 {
180 let this_cpu = crate::arch::x86_64::percpu::current_cpu_index();
181 self.owner_cpu.store(this_cpu, Ordering::Relaxed);
182 emit_trace_e9(self as *const _ as *const () as usize, 0);
183 Some(SpinLockGuard {
184 lock: self,
185 state: ManuallyDrop::new(GuardianState {
186 token,
187 saved_flags: 0,
188 restore_flags: false,
189 }),
190 })
191 } else {
192 None
193 }
194 }
195}
196
197pub fn debug_set_watch_lock_addr(addr: usize) {
201 DEBUG_WATCH_LOCK_ADDR.store(addr, Ordering::Relaxed);
202}
203
204pub fn debug_clear_watch_lock_addr() {
206 DEBUG_WATCH_LOCK_ADDR.store(usize::MAX, Ordering::Relaxed);
207}
208
209const DEBUG_TRACE_WATCH_SLOTS: usize = 8;
211
212static DEBUG_TRACE_WATCH_ADDRS: [AtomicUsize; DEBUG_TRACE_WATCH_SLOTS] =
215 [const { AtomicUsize::new(usize::MAX) }; DEBUG_TRACE_WATCH_SLOTS];
216
217pub fn debug_set_trace_lock_addr(addr: usize) -> Option<usize> {
220 for (i, slot) in DEBUG_TRACE_WATCH_ADDRS.iter().enumerate() {
221 if slot.load(Ordering::Relaxed) == usize::MAX {
222 slot.store(addr, Ordering::Relaxed);
223 return Some(i);
224 }
225 }
226 None
227}
228
229pub fn debug_clear_trace_slot(index: usize) {
231 if index < DEBUG_TRACE_WATCH_SLOTS {
232 DEBUG_TRACE_WATCH_ADDRS[index].store(usize::MAX, Ordering::Relaxed);
233 }
234}
235
236pub fn debug_set_trace_buddy_addr(addr: usize) {
238 let _ = debug_set_trace_lock_addr(addr);
239}
240
241pub fn debug_set_trace_slab_addr(addr: usize) {
243 let _ = debug_set_trace_lock_addr(addr);
244}
245
246#[inline]
248fn emit_trace_e9(lock_addr: usize, tag_offset: u8) {
249 for (i, slot) in DEBUG_TRACE_WATCH_ADDRS.iter().enumerate() {
250 if slot.load(Ordering::Relaxed) == lock_addr {
251 let ch = b'A' + tag_offset + (i as u8);
252 unsafe { core::arch::asm!("out 0xe9, al", in("al") ch) };
253 }
254 }
255}
256
257pub struct SpinLockGuard<'a, T: ?Sized, G: Guardian = IrqDisabled> {
261 lock: &'a SpinLock<T, G>,
262 state: ManuallyDrop<GuardianState<G::Token>>,
266}
267
268impl<'a, T: ?Sized> SpinLockGuard<'a, T, IrqDisabled> {
269 #[inline]
271 pub fn token(&self) -> &IrqDisabledToken {
272 &self.state.token
273 }
274
275 #[inline]
276 pub(crate) fn with_mut_and_token<R>(
277 &mut self,
278 f: impl FnOnce(&mut T, &IrqDisabledToken) -> R,
279 ) -> R {
280 let token = &self.state.token;
281 let data = unsafe { &mut *self.lock.data.get() };
283 f(data, token)
284 }
285}
286
287impl<'a, T: ?Sized, G: Guardian> Deref for SpinLockGuard<'a, T, G> {
288 type Target = T;
289
290 fn deref(&self) -> &T {
291 unsafe { &*self.lock.data.get() }
293 }
294}
295
296impl<'a, T: ?Sized, G: Guardian> DerefMut for SpinLockGuard<'a, T, G> {
297 fn deref_mut(&mut self) -> &mut T {
298 unsafe { &mut *self.lock.data.get() }
300 }
301}
302
303impl<'a, T: ?Sized, G: Guardian> Drop for SpinLockGuard<'a, T, G> {
304 fn drop(&mut self) {
305 let lock_addr = self.lock as *const _ as *const () as usize;
306 let watched = DEBUG_WATCH_LOCK_ADDR.load(Ordering::Relaxed);
307 let trace = watched == lock_addr;
308
309 if trace {
310 crate::serial_force_println!(
311 "[trace][spin] drop begin lock={:#x} owner_cpu={} saved_flags={:#x}",
312 lock_addr,
313 self.lock.owner_cpu.load(Ordering::Relaxed),
314 self.state.saved_flags,
315 );
316 }
317
318 self.lock.owner_cpu.store(usize::MAX, Ordering::Relaxed);
319 self.lock.locked.store(false, Ordering::Release);
320
321 if trace {
322 crate::serial_force_println!("[trace][spin] drop unlocked lock={:#x}", lock_addr);
323 }
324 for (i, slot) in DEBUG_TRACE_WATCH_ADDRS.iter().enumerate() {
327 if slot.load(Ordering::Relaxed) == lock_addr {
328 let ch = b'a' + (i as u8);
329 unsafe { core::arch::asm!("out 0xe9, al", in("al") ch) };
330 }
331 }
332
333 let state = unsafe { ManuallyDrop::take(&mut self.state) };
338 G::exit(state);
339
340 if trace {
341 crate::serial_force_println!(
342 "[trace][spin] drop guardian-exit done lock={:#x}",
343 lock_addr
344 );
345 }
346 }
347}
348
349impl<T: ?Sized, G: Guardian> !Send for SpinLockGuard<'_, T, G> {}