1 /*
2 * Copyright (c) 2018 Intel Corporation.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /**
8 * @file
9 * @brief Public interface for spinlocks
10 */
11
12 #ifndef ZEPHYR_INCLUDE_SPINLOCK_H_
13 #define ZEPHYR_INCLUDE_SPINLOCK_H_
14
15 #include <sys/atomic.h>
16 #include <sys/__assert.h>
17 #include <stdbool.h>
18 #include <arch/cpu.h>
19
20 #ifdef __cplusplus
21 extern "C" {
22 #endif
23
24 /**
25 * @brief Spinlock APIs
26 * @defgroup spinlock_apis Spinlock APIs
27 * @ingroup kernel_apis
28 * @{
29 */
30
31 struct z_spinlock_key {
32 int key;
33 };
34
35 /**
36 * @brief Kernel Spin Lock
37 *
38 * This struct defines a spin lock record on which CPUs can wait with
39 * k_spin_lock(). Any number of spinlocks may be defined in
40 * application code.
41 */
42 struct k_spinlock {
43 #ifdef CONFIG_SMP
44 atomic_t locked;
45 #endif
46
47 #ifdef CONFIG_SPIN_VALIDATE
48 /* Stores the thread that holds the lock with the locking CPU
49 * ID in the bottom two bits.
50 */
51 uintptr_t thread_cpu;
52 #endif
53
54 #if defined(CONFIG_CPLUSPLUS) && !defined(CONFIG_SMP) && \
55 !defined(CONFIG_SPIN_VALIDATE)
56 /* If CONFIG_SMP and CONFIG_SPIN_VALIDATE are both not defined
57 * the k_spinlock struct will have no members. The result
58 * is that in C sizeof(k_spinlock) is 0 and in C++ it is 1.
59 *
60 * This size difference causes problems when the k_spinlock
61 * is embedded into another struct like k_msgq, because C and
62 * C++ will have different ideas on the offsets of the members
63 * that come after the k_spinlock member.
64 *
65 * To prevent this we add a 1 byte dummy member to k_spinlock
66 * when the user selects C++ support and k_spinlock would
67 * otherwise be empty.
68 */
69 char dummy;
70 #endif
71 };
72
73 /* There's a spinlock validation framework available when asserts are
74 * enabled. It adds a relatively hefty overhead (about 3k or so) to
75 * kernel code size, don't use on platforms known to be small.
76 */
77 #ifdef CONFIG_SPIN_VALIDATE
78 bool z_spin_lock_valid(struct k_spinlock *l);
79 bool z_spin_unlock_valid(struct k_spinlock *l);
80 void z_spin_lock_set_owner(struct k_spinlock *l);
81 BUILD_ASSERT(CONFIG_MP_NUM_CPUS <= 4, "Too many CPUs for mask");
82
83 # ifdef CONFIG_KERNEL_COHERENCE
84 bool z_spin_lock_mem_coherent(struct k_spinlock *l);
85 # endif /* CONFIG_KERNEL_COHERENCE */
86
87 #endif /* CONFIG_SPIN_VALIDATE */
88
89 /**
90 * @brief Spinlock key type
91 *
92 * This type defines a "key" value used by a spinlock implementation
93 * to store the system interrupt state at the time of a call to
94 * k_spin_lock(). It is expected to be passed to a matching
95 * k_spin_unlock().
96 *
97 * This type is opaque and should not be inspected by application
98 * code.
99 */
100 typedef struct z_spinlock_key k_spinlock_key_t;
101
102 /**
103 * @brief Lock a spinlock
104 *
105 * This routine locks the specified spinlock, returning a key handle
106 * representing interrupt state needed at unlock time. Upon
107 * returning, the calling thread is guaranteed not to be suspended or
108 * interrupted on its current CPU until it calls k_spin_unlock(). The
109 * implementation guarantees mutual exclusion: exactly one thread on
110 * one CPU will return from k_spin_lock() at a time. Other CPUs
111 * trying to acquire a lock already held by another CPU will enter an
112 * implementation-defined busy loop ("spinning") until the lock is
113 * released.
114 *
115 * Separate spin locks may be nested. It is legal to lock an
116 * (unlocked) spin lock while holding a different lock. Spin locks
117 * are not recursive, however: an attempt to acquire a spin lock that
118 * the CPU already holds will deadlock.
119 *
120 * In circumstances where only one CPU exists, the behavior of
121 * k_spin_lock() remains as specified above, though obviously no
122 * spinning will take place. Implementations may be free to optimize
123 * in uniprocessor contexts such that the locking reduces to an
124 * interrupt mask operation.
125 *
126 * @param l A pointer to the spinlock to lock
127 * @return A key value that must be passed to k_spin_unlock() when the
128 * lock is released.
129 */
k_spin_lock(struct k_spinlock * l)130 static ALWAYS_INLINE k_spinlock_key_t k_spin_lock(struct k_spinlock *l)
131 {
132 ARG_UNUSED(l);
133 k_spinlock_key_t k;
134
135 /* Note that we need to use the underlying arch-specific lock
136 * implementation. The "irq_lock()" API in SMP context is
137 * actually a wrapper for a global spinlock!
138 */
139 k.key = arch_irq_lock();
140
141 #ifdef CONFIG_SPIN_VALIDATE
142 __ASSERT(z_spin_lock_valid(l), "Recursive spinlock %p", l);
143 # ifdef CONFIG_KERNEL_COHERENCE
144 __ASSERT_NO_MSG(z_spin_lock_mem_coherent(l));
145 # endif
146 #endif
147
148 #ifdef CONFIG_SMP
149 while (!atomic_cas(&l->locked, 0, 1)) {
150 }
151 #endif
152
153 #ifdef CONFIG_SPIN_VALIDATE
154 z_spin_lock_set_owner(l);
155 #endif
156 return k;
157 }
158
159 /**
160 * @brief Unlock a spin lock
161 *
162 * This releases a lock acquired by k_spin_lock(). After this
163 * function is called, any CPU will be able to acquire the lock. If
164 * other CPUs are currently spinning inside k_spin_lock() waiting for
165 * this lock, exactly one of them will return synchronously with the
166 * lock held.
167 *
168 * Spin locks must be properly nested. A call to k_spin_unlock() must
169 * be made on the lock object most recently locked using
170 * k_spin_lock(), using the key value that it returned. Attempts to
171 * unlock mis-nested locks, or to unlock locks that are not held, or
172 * to passing a key parameter other than the one returned from
173 * k_spin_lock(), are illegal. When CONFIG_SPIN_VALIDATE is set, some
174 * of these errors can be detected by the framework.
175 *
176 * @param l A pointer to the spinlock to release
177 * @param key The value returned from k_spin_lock() when this lock was
178 * acquired
179 */
k_spin_unlock(struct k_spinlock * l,k_spinlock_key_t key)180 static ALWAYS_INLINE void k_spin_unlock(struct k_spinlock *l,
181 k_spinlock_key_t key)
182 {
183 ARG_UNUSED(l);
184 #ifdef CONFIG_SPIN_VALIDATE
185 __ASSERT(z_spin_unlock_valid(l), "Not my spinlock %p", l);
186 #endif
187
188 #ifdef CONFIG_SMP
189 /* Strictly we don't need atomic_clear() here (which is an
190 * exchange operation that returns the old value). We are always
191 * setting a zero and (because we hold the lock) know the existing
192 * state won't change due to a race. But some architectures need
193 * a memory barrier when used like this, and we don't have a
194 * Zephyr framework for that.
195 */
196 atomic_clear(&l->locked);
197 #endif
198 arch_irq_unlock(key.key);
199 }
200
201 /* Internal function: releases the lock, but leaves local interrupts
202 * disabled
203 */
k_spin_release(struct k_spinlock * l)204 static ALWAYS_INLINE void k_spin_release(struct k_spinlock *l)
205 {
206 ARG_UNUSED(l);
207 #ifdef CONFIG_SPIN_VALIDATE
208 __ASSERT(z_spin_unlock_valid(l), "Not my spinlock %p", l);
209 #endif
210 #ifdef CONFIG_SMP
211 atomic_clear(&l->locked);
212 #endif
213 }
214
215 /** @} */
216
217 #ifdef __cplusplus
218 }
219 #endif
220
221 #endif /* ZEPHYR_INCLUDE_SPINLOCK_H_ */
222