Skip to main content

ax_task/sync/bridge/
spin.rs

1//! Complete external spin and read-write lock transactions.
2
3use 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
23/// One complete external exclusive-spin acquisition request.
24pub 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
33/// One complete external spin read-write acquisition request.
34pub 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
43/// Acquires an external exclusive spin lock through the native state machine.
44pub 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
54/// Attempts an external exclusive spin acquisition through the native state machine.
55pub 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
70/// Releases an external exclusive spin lock and its execution context.
71pub 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
82/// Releases a deliberately leaked external exclusive spin acquisition.
83pub fn spin_force_release(locked: &AtomicBool, lock_addr: usize, context: u8) {
84    force_release_spin_state(locked, lock_addr, context);
85}
86
87/// Returns a diagnostic snapshot of an external spin lock word.
88pub 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
101/// Acquires an external spin read-write lock through the native state machine.
102pub 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
113/// Attempts an external spin read-write acquisition through the native state machine.
114pub 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
130/// Releases an external spin read-write acquisition and its context.
131pub 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
143/// Removes one deliberately leaked external raw read acquisition.
144pub 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}