1use core::{
4 panic::Location,
5 sync::atomic::{AtomicBool, AtomicUsize},
6};
7
8#[cfg(feature = "lockdep")]
9use {
10 crate::sync::context::IrqSaveGuard, crate::sync::lockdep::LockdepMapView,
11 crate::sync::spin::lockdep, crate::sync::spin::lockdep::LockdepAcquireRequest,
12};
13
14use super::{
15 context::{ContextOperations, ContextState, context_enter, context_exit},
16 lockdep::LockClass,
17};
18use crate::sync::spin::atomic;
19
20const LOCK_MODE_READ: u8 = 1;
21const LOCK_MODE_WRITE: u8 = 2;
22
23pub struct SpinAcquireRequest<'lock> {
25 pub locked: &'lock AtomicBool,
26 pub class: LockClass<'lock>,
27 pub lock_addr: usize,
28 pub context: u8,
29 pub subclass: u32,
30 pub caller: &'static Location<'static>,
31}
32
33pub struct RwLockAcquireRequest<'lock> {
35 pub state: &'lock AtomicUsize,
36 pub class: LockClass<'lock>,
37 pub lock_addr: usize,
38 pub context: u8,
39 pub mode: u8,
40 pub caller: &'static Location<'static>,
41}
42
43pub fn spin_acquire(
45 request: SpinAcquireRequest<'_>,
46 operations: &ContextOperations,
47) -> ContextState {
48 let pending_context = PendingContext::enter(request.context, operations);
49 let lockdep = prepare_spin_lockdep(&request, AcquireKind::Blocking);
50 acquire_spin_state(request.locked, lockdep);
51 pending_context.into_state()
52}
53
54pub fn spin_try_acquire(
56 request: SpinAcquireRequest<'_>,
57 operations: &ContextOperations,
58) -> (bool, ContextState) {
59 let pending_context = PendingContext::enter(request.context, operations);
60 let lockdep = prepare_spin_lockdep(&request, AcquireKind::Try);
61 let acquired = try_acquire_spin_state(request.locked, lockdep);
62 if acquired {
63 (true, pending_context.into_state())
64 } else {
65 drop(pending_context);
66 (false, ContextState::new(0, 0))
67 }
68}
69
70pub fn spin_release(
72 locked: &AtomicBool,
73 lock_addr: usize,
74 context: u8,
75 context_state: ContextState,
76 operations: &ContextOperations,
77) {
78 release_spin_state(locked, lock_addr, context);
79 context_exit(context, context_state, operations);
80}
81
82pub fn spin_force_release(locked: &AtomicBool, lock_addr: usize, context: u8) {
84 force_release_spin_state(locked, lock_addr, context);
85}
86
87pub fn spin_is_locked(locked: &AtomicBool) -> bool {
89 #[cfg(feature = "smp")]
90 {
91 atomic::spin_is_locked(locked)
92 }
93
94 #[cfg(not(feature = "smp"))]
95 {
96 let _ = locked;
97 false
98 }
99}
100
101pub fn rwlock_acquire(
103 request: RwLockAcquireRequest<'_>,
104 operations: &ContextOperations,
105) -> ContextState {
106 let pending_context = PendingContext::enter(request.context, operations);
107 let lockdep = prepare_rwlock_lockdep(&request, AcquireKind::Blocking);
108 acquire_rwlock_state(request.state, request.mode);
109 finish_rwlock_lockdep(lockdep, true);
110 pending_context.into_state()
111}
112
113pub fn rwlock_try_acquire(
115 request: RwLockAcquireRequest<'_>,
116 operations: &ContextOperations,
117) -> (bool, ContextState) {
118 let pending_context = PendingContext::enter(request.context, operations);
119 let lockdep = prepare_rwlock_lockdep(&request, AcquireKind::Try);
120 let acquired = try_acquire_rwlock_state(request.state, request.mode);
121 finish_rwlock_lockdep(lockdep, acquired);
122 if acquired {
123 (true, pending_context.into_state())
124 } else {
125 drop(pending_context);
126 (false, ContextState::new(0, 0))
127 }
128}
129
130pub fn rwlock_release(
132 state: &AtomicUsize,
133 lock_addr: usize,
134 context: u8,
135 context_state: ContextState,
136 mode: u8,
137 operations: &ContextOperations,
138) {
139 release_rwlock_state(state, lock_addr, context, mode);
140 context_exit(context, context_state, operations);
141}
142
143pub fn rwlock_force_read_decrement(state: &AtomicUsize, lock_addr: usize, context: u8) {
145 if atomic::rw_force_read_decrement(state) {
146 release_rwlock_read_lockdep(lock_addr, context);
147 }
148}
149
150struct PendingContext<'operations> {
151 context: u8,
152 state: Option<ContextState>,
153 operations: &'operations ContextOperations,
154}
155
156impl<'operations> PendingContext<'operations> {
157 fn enter(context: u8, operations: &'operations ContextOperations) -> Self {
158 Self {
159 context,
160 state: Some(context_enter(context, operations)),
161 operations,
162 }
163 }
164
165 fn into_state(mut self) -> ContextState {
166 self.state
167 .take()
168 .expect("pending external context state must be owned")
169 }
170}
171
172impl Drop for PendingContext<'_> {
173 fn drop(&mut self) {
174 if let Some(state) = self.state.take() {
175 context_exit(self.context, state, self.operations);
176 }
177 }
178}
179
180#[cfg(feature = "lockdep")]
181type LockdepAcquire = lockdep::Lockdep;
182
183#[cfg(not(feature = "lockdep"))]
184#[derive(Clone, Copy)]
185struct LockdepAcquire;
186
187#[derive(Clone, Copy)]
188enum AcquireKind {
189 Blocking,
190 Try,
191}
192
193#[cfg(feature = "lockdep")]
194struct BridgeLockdepRequest<'lock> {
195 class: LockClass<'lock>,
196 lock_kind: &'static str,
197 trace_kind: &'static str,
198 lock_addr: usize,
199 context: u8,
200 subclass: u32,
201 is_try: bool,
202 caller: &'static Location<'static>,
203 track_task_lock: bool,
204}
205
206fn prepare_spin_lockdep(
207 request: &SpinAcquireRequest<'_>,
208 acquire_kind: AcquireKind,
209) -> LockdepAcquire {
210 #[cfg(feature = "lockdep")]
211 {
212 prepare_lockdep(BridgeLockdepRequest {
213 class: request.class,
214 lock_kind: "spin lock",
215 trace_kind: "spin",
216 lock_addr: request.lock_addr,
217 context: request.context,
218 subclass: request.subclass,
219 is_try: matches!(acquire_kind, AcquireKind::Try),
220 caller: request.caller,
221 track_task_lock: true,
222 })
223 }
224
225 #[cfg(not(feature = "lockdep"))]
226 {
227 let _ = (request, acquire_kind);
228 LockdepAcquire
229 }
230}
231
232fn prepare_rwlock_lockdep(
233 request: &RwLockAcquireRequest<'_>,
234 acquire_kind: AcquireKind,
235) -> LockdepAcquire {
236 let track_task_lock = match request.mode {
237 LOCK_MODE_READ => false,
238 LOCK_MODE_WRITE => true,
239 mode => panic!("unknown external rwlock mode {mode}"),
240 };
241 #[cfg(feature = "lockdep")]
242 {
243 prepare_lockdep(BridgeLockdepRequest {
244 class: request.class,
245 lock_kind: "spin rwlock",
246 trace_kind: "spin-rwlock",
247 lock_addr: request.lock_addr,
248 context: request.context,
249 subclass: 0,
250 is_try: matches!(acquire_kind, AcquireKind::Try),
251 caller: request.caller,
252 track_task_lock,
253 })
254 }
255
256 #[cfg(not(feature = "lockdep"))]
257 {
258 let _ = (request, acquire_kind, track_task_lock);
259 LockdepAcquire
260 }
261}
262
263#[cfg(feature = "lockdep")]
264fn prepare_lockdep(request: BridgeLockdepRequest<'_>) -> LockdepAcquire {
265 LockdepAcquire::prepare_view(LockdepAcquireRequest {
266 map: LockdepMapView::new(request.class.class_id, request.class.class_key),
267 lock_kind: request.lock_kind,
268 trace_kind: request.trace_kind,
269 addr: request.lock_addr,
270 is_try: request.is_try,
271 subclass: request.subclass,
272 caller: request.caller,
273 detail: context_detail(request.context),
274 track_task_lock: request.track_task_lock && context_tracks_task_locks(request.context),
275 })
276}
277
278fn acquire_spin_state(locked: &AtomicBool, lockdep: LockdepAcquire) {
279 #[cfg(feature = "smp")]
280 {
281 atomic::spin_acquire(locked, || {
282 spin_acquire_once_weak_with_lockdep(locked, lockdep)
283 });
284 }
285
286 #[cfg(not(feature = "smp"))]
287 {
288 let _ = locked;
289 finish_lockdep_with_irqsave(lockdep, true);
290 }
291}
292
293fn try_acquire_spin_state(locked: &AtomicBool, lockdep: LockdepAcquire) -> bool {
294 #[cfg(feature = "smp")]
295 {
296 let acquired = spin_try_acquire_with_lockdep(locked, lockdep);
297 if !acquired {
298 finish_spin_try_failure(lockdep);
299 }
300 acquired
301 }
302
303 #[cfg(not(feature = "smp"))]
304 {
305 let _ = locked;
306 finish_lockdep_with_irqsave(lockdep, true);
307 true
308 }
309}
310
311#[cfg(feature = "smp")]
312fn spin_acquire_once_weak_with_lockdep(locked: &AtomicBool, lockdep: LockdepAcquire) -> bool {
313 with_lockdep_irqsave(|| {
314 let acquired = atomic::spin_try_acquire_weak(locked);
315 if acquired {
316 finish_lockdep(lockdep, true);
317 }
318 acquired
319 })
320}
321
322#[cfg(feature = "smp")]
323fn spin_try_acquire_with_lockdep(locked: &AtomicBool, lockdep: LockdepAcquire) -> bool {
324 with_lockdep_irqsave(|| {
325 let acquired = atomic::spin_try_acquire_strong(locked);
326 if acquired {
327 finish_lockdep(lockdep, true);
328 }
329 acquired
330 })
331}
332
333fn release_spin_state(locked: &AtomicBool, lock_addr: usize, context: u8) {
334 with_lockdep_irqsave(|| {
335 release_spin_lockdep(lock_addr, context, false);
336 #[cfg(feature = "smp")]
337 atomic::spin_release(locked);
338 #[cfg(not(feature = "smp"))]
339 let _ = locked;
340 });
341}
342
343fn force_release_spin_state(locked: &AtomicBool, lock_addr: usize, context: u8) {
344 with_lockdep_irqsave(|| {
345 release_spin_lockdep(lock_addr, context, true);
346 #[cfg(feature = "smp")]
347 atomic::spin_release(locked);
348 #[cfg(not(feature = "smp"))]
349 let _ = locked;
350 });
351}
352
353fn acquire_rwlock_state(state: &AtomicUsize, mode: u8) {
354 match mode {
355 LOCK_MODE_READ => {
356 atomic::rw_acquire_read(state);
357 }
358 LOCK_MODE_WRITE => {
359 atomic::rw_acquire_write(state);
360 }
361 mode => panic!("unknown external rwlock mode {mode}"),
362 }
363}
364
365fn try_acquire_rwlock_state(state: &AtomicUsize, mode: u8) -> bool {
366 match mode {
367 LOCK_MODE_READ => atomic::rw_try_acquire_read(state),
368 LOCK_MODE_WRITE => atomic::rw_try_acquire_write(state),
369 mode => panic!("unknown external rwlock mode {mode}"),
370 }
371}
372
373fn release_rwlock_state(state: &AtomicUsize, lock_addr: usize, context: u8, mode: u8) {
374 with_lockdep_irqsave(|| match mode {
375 LOCK_MODE_READ => {
376 release_rwlock_read_lockdep(lock_addr, context);
377 atomic::rw_release_read(state);
378 }
379 LOCK_MODE_WRITE => {
380 release_rwlock_write_lockdep(lock_addr, context);
381 atomic::rw_release_write(state);
382 }
383 mode => panic!("unknown external rwlock mode {mode}"),
384 });
385}
386
387fn finish_rwlock_lockdep(lockdep: LockdepAcquire, acquired: bool) {
388 finish_lockdep_with_irqsave(lockdep, acquired);
389}
390
391fn finish_lockdep_with_irqsave(lockdep: LockdepAcquire, acquired: bool) {
392 with_lockdep_irqsave(|| finish_lockdep(lockdep, acquired));
393}
394
395fn finish_lockdep(lockdep: LockdepAcquire, acquired: bool) {
396 #[cfg(feature = "lockdep")]
397 lockdep.finish(acquired);
398
399 #[cfg(not(feature = "lockdep"))]
400 let _ = (lockdep, acquired);
401}
402
403#[cfg(feature = "smp")]
404fn finish_spin_try_failure(lockdep: LockdepAcquire) {
405 finish_lockdep(lockdep, false);
406}
407
408fn release_spin_lockdep(lock_addr: usize, context: u8, force: bool) {
409 #[cfg(feature = "lockdep")]
410 if force {
411 lockdep::force_release_external(
412 "spin",
413 lock_addr,
414 context_detail(context),
415 context_tracks_task_locks(context),
416 );
417 } else {
418 lockdep::release_external(
419 "spin",
420 lock_addr,
421 context_detail(context),
422 context_tracks_task_locks(context),
423 );
424 }
425
426 #[cfg(not(feature = "lockdep"))]
427 let _ = (lock_addr, context, force);
428}
429
430fn release_rwlock_read_lockdep(lock_addr: usize, context: u8) {
431 #[cfg(feature = "lockdep")]
432 lockdep::release_external("spin-rwlock", lock_addr, context_detail(context), false);
433
434 #[cfg(not(feature = "lockdep"))]
435 let _ = (lock_addr, context);
436}
437
438fn release_rwlock_write_lockdep(lock_addr: usize, context: u8) {
439 #[cfg(feature = "lockdep")]
440 lockdep::release_external(
441 "spin-rwlock",
442 lock_addr,
443 context_detail(context),
444 context_tracks_task_locks(context),
445 );
446
447 #[cfg(not(feature = "lockdep"))]
448 let _ = (lock_addr, context);
449}
450
451fn with_lockdep_irqsave<R>(operation: impl FnOnce() -> R) -> R {
452 #[cfg(feature = "lockdep")]
453 let _irq_guard = IrqSaveGuard::new();
454 operation()
455}
456
457#[cfg(feature = "lockdep")]
458fn context_tracks_task_locks(context: u8) -> bool {
459 match context {
460 super::context::CONTEXT_RAW
461 | super::context::CONTEXT_PREEMPT
462 | super::context::CONTEXT_PREEMPT_IRQSAVE => true,
463 super::context::CONTEXT_IRQSAVE => false,
464 context => panic!("unknown external lock context {context}"),
465 }
466}
467
468#[cfg(feature = "lockdep")]
469fn context_detail(context: u8) -> &'static str {
470 match context {
471 super::context::CONTEXT_RAW => "external raw context",
472 super::context::CONTEXT_PREEMPT => "external preempt context",
473 super::context::CONTEXT_IRQSAVE => "external irq-save context",
474 super::context::CONTEXT_PREEMPT_IRQSAVE => "external preempt+irq-save context",
475 context => panic!("unknown external lock context {context}"),
476 }
477}