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