1 /*
2  * Copyright (c) 2014 Travis Geiselbrecht
3  *
4  * Use of this source code is governed by a MIT-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/MIT
7  */
8 #pragma once
9 
10 #include <arch/spinlock.h>
11 #include <lk/compiler.h>
12 
13 __BEGIN_CDECLS
14 
15 /* interrupts should already be disabled */
spin_lock(spin_lock_t * lock)16 static inline void spin_lock(spin_lock_t *lock) {
17     arch_spin_lock(lock);
18 }
19 
20 /* Returns 0 on success, non-0 on failure */
spin_trylock(spin_lock_t * lock)21 static inline int spin_trylock(spin_lock_t *lock) {
22     return arch_spin_trylock(lock);
23 }
24 
25 /* interrupts should already be disabled */
spin_unlock(spin_lock_t * lock)26 static inline void spin_unlock(spin_lock_t *lock) {
27     arch_spin_unlock(lock);
28 }
29 
spin_lock_init(spin_lock_t * lock)30 static inline void spin_lock_init(spin_lock_t *lock) {
31     arch_spin_lock_init(lock);
32 }
33 
spin_lock_held(spin_lock_t * lock)34 static inline bool spin_lock_held(spin_lock_t *lock) {
35     return arch_spin_lock_held(lock);
36 }
37 
38 /* spin lock irq save flags: */
39 
40 /* Possible future flags:
41  * SPIN_LOCK_FLAG_PMR_MASK         = 0x000000ff
42  * SPIN_LOCK_FLAG_PREEMPTION       = 0x00000100
43  * SPIN_LOCK_FLAG_SET_PMR          = 0x00000200
44  */
45 
46 /* Generic flags */
47 #define SPIN_LOCK_FLAG_INTERRUPTS ARCH_DEFAULT_SPIN_LOCK_FLAG_INTERRUPTS
48 
49 /* same as spin lock, but save disable and save interrupt state first */
spin_lock_save(spin_lock_t * lock,spin_lock_saved_state_t * statep,spin_lock_save_flags_t flags)50 static inline void spin_lock_save(
51     spin_lock_t *lock,
52     spin_lock_saved_state_t *statep,
53     spin_lock_save_flags_t flags) {
54     arch_interrupt_save(statep, flags);
55     spin_lock(lock);
56 }
57 
58 /* restore interrupt state before unlocking */
spin_unlock_restore(spin_lock_t * lock,spin_lock_saved_state_t old_state,spin_lock_save_flags_t flags)59 static inline void spin_unlock_restore(
60     spin_lock_t *lock,
61     spin_lock_saved_state_t old_state,
62     spin_lock_save_flags_t flags) {
63     spin_unlock(lock);
64     arch_interrupt_restore(old_state, flags);
65 }
66 
67 /* hand(ier) routines */
68 #define spin_lock_irqsave(lock, statep) spin_lock_save(lock, &(statep), SPIN_LOCK_FLAG_INTERRUPTS)
69 #define spin_unlock_irqrestore(lock, statep) spin_unlock_restore(lock, statep, SPIN_LOCK_FLAG_INTERRUPTS)
70 
71 __END_CDECLS
72 
73 #ifdef __cplusplus
74 
75 #include <lk/cpp.h>
76 #include <assert.h>
77 
78 // C++ wrapper around a C spinlock_t
79 class SpinLock {
80 public:
81     constexpr SpinLock() = default;
~SpinLock()82     ~SpinLock() { DEBUG_ASSERT(!is_held()); }
83 
lock()84     void lock() { spin_lock(&lock_); }
trylock()85     int trylock() { return spin_trylock(&lock_); }
unlock()86     void unlock() { spin_unlock(&lock_); }
is_held()87     bool is_held() { return spin_lock_held(&lock_); }
88 
lock_irqsave(spin_lock_saved_state_t * state)89     void lock_irqsave(spin_lock_saved_state_t *state) {
90         spin_lock_save(&lock_, state, SPIN_LOCK_FLAG_INTERRUPTS);
91     }
92 
unlock_irqrestore(spin_lock_saved_state_t state)93     void unlock_irqrestore(spin_lock_saved_state_t state) {
94         spin_unlock_restore(&lock_, state, SPIN_LOCK_FLAG_INTERRUPTS);
95     }
96 
97     // suppress default constructors
98     DISALLOW_COPY_ASSIGN_AND_MOVE(SpinLock);
99 
100 private:
101     spin_lock_t lock_ = SPIN_LOCK_INITIAL_VALUE;
102 
103     // friend classes to get to the inner lock
104     friend class AutoSpinLock;
105     friend class AutoSpinLockNoIrqSave;
106 };
107 
108 // RAII wrappers for a spinlock, with and without IRQ Save
109 class AutoSpinLock {
110 public:
AutoSpinLock(spin_lock_t * lock)111     explicit AutoSpinLock(spin_lock_t *lock) : lock_(lock) { spin_lock_irqsave(lock_, state_); }
AutoSpinLock(SpinLock * lock)112     explicit AutoSpinLock(SpinLock *lock) : AutoSpinLock(&lock->lock_) {}
~AutoSpinLock()113     ~AutoSpinLock() { release(); }
114 
release()115     void release() {
116         if (likely(lock_)) {
117             spin_unlock_irqrestore(lock_, state_);
118             lock_ = nullptr;
119         }
120     }
121 
122     // suppress default constructors
123     DISALLOW_COPY_ASSIGN_AND_MOVE(AutoSpinLock);
124 
125 private:
126     spin_lock_t *lock_;
127     spin_lock_saved_state_t state_;
128 };
129 
130 class AutoSpinLockNoIrqSave {
131 public:
AutoSpinLockNoIrqSave(spin_lock_t * lock)132     explicit AutoSpinLockNoIrqSave(spin_lock_t *lock) : lock_(lock) { spin_lock(lock_); }
AutoSpinLockNoIrqSave(SpinLock * lock)133     explicit AutoSpinLockNoIrqSave(SpinLock *lock) : AutoSpinLockNoIrqSave(&lock->lock_) {}
~AutoSpinLockNoIrqSave()134     ~AutoSpinLockNoIrqSave() { release(); }
135 
release()136     void release() {
137         if (likely(lock_)) {
138             spin_unlock(lock_);
139             lock_ = nullptr;
140         }
141     }
142 
143     // suppress default constructors
144     DISALLOW_COPY_ASSIGN_AND_MOVE(AutoSpinLockNoIrqSave);
145 
146 private:
147     spin_lock_t *lock_;
148 };
149 
150 #endif // __cplusplus
151