2 * Copyright (c) 2015, Linaro Limited
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
28 #include <kernel/mutex.h>
29 #include <kernel/panic.h>
30 #include <kernel/spinlock.h>
31 #include <kernel/thread.h>
34 void mutex_init(struct mutex *m)
36 *m = (struct mutex)MUTEX_INITIALIZER;
39 static void __mutex_lock(struct mutex *m, const char *fname, int lineno)
41 assert_have_no_spinlock();
42 assert(thread_get_id_may_fail() != -1);
45 uint32_t old_itr_status;
46 enum mutex_value old_value;
47 struct wait_queue_elem wqe;
50 * If the mutex is locked we need to initialize the wqe
51 * before releasing the spinlock to guarantee that we don't
52 * miss the wakeup from mutex_unlock().
54 * If the mutex is unlocked we don't need to use the wqe at
58 old_itr_status = thread_mask_exceptions(THREAD_EXCP_ALL);
59 cpu_spin_lock(&m->spin_lock);
62 if (old_value == MUTEX_VALUE_LOCKED) {
63 wq_wait_init(&m->wq, &wqe);
65 m->value = MUTEX_VALUE_LOCKED;
69 cpu_spin_unlock(&m->spin_lock);
70 thread_unmask_exceptions(old_itr_status);
72 if (old_value == MUTEX_VALUE_LOCKED) {
74 * Someone else is holding the lock, wait in normal
75 * world for the lock to become available.
77 wq_wait_final(&m->wq, &wqe, m, fname, lineno);
83 static void __mutex_unlock(struct mutex *m, const char *fname, int lineno)
85 uint32_t old_itr_status;
87 assert_have_no_spinlock();
88 assert(thread_get_id_may_fail() != -1);
90 old_itr_status = thread_mask_exceptions(THREAD_EXCP_ALL);
91 cpu_spin_lock(&m->spin_lock);
93 if (m->value != MUTEX_VALUE_LOCKED)
97 m->value = MUTEX_VALUE_UNLOCKED;
99 cpu_spin_unlock(&m->spin_lock);
100 thread_unmask_exceptions(old_itr_status);
102 wq_wake_one(&m->wq, m, fname, lineno);
105 static bool __mutex_trylock(struct mutex *m, const char *fname __unused,
108 uint32_t old_itr_status;
109 enum mutex_value old_value;
111 assert_have_no_spinlock();
112 assert(thread_get_id_may_fail() != -1);
114 old_itr_status = thread_mask_exceptions(THREAD_EXCP_ALL);
115 cpu_spin_lock(&m->spin_lock);
117 old_value = m->value;
118 if (old_value == MUTEX_VALUE_UNLOCKED) {
119 m->value = MUTEX_VALUE_LOCKED;
123 cpu_spin_unlock(&m->spin_lock);
124 thread_unmask_exceptions(old_itr_status);
126 return old_value == MUTEX_VALUE_UNLOCKED;
129 #ifdef CFG_MUTEX_DEBUG
130 void mutex_unlock_debug(struct mutex *m, const char *fname, int lineno)
132 __mutex_unlock(m, fname, lineno);
135 void mutex_lock_debug(struct mutex *m, const char *fname, int lineno)
137 __mutex_lock(m, fname, lineno);
140 bool mutex_trylock_debug(struct mutex *m, const char *fname, int lineno)
142 return __mutex_trylock(m, fname, lineno);
145 void mutex_unlock(struct mutex *m)
147 __mutex_unlock(m, NULL, -1);
150 void mutex_lock(struct mutex *m)
152 __mutex_lock(m, NULL, -1);
155 bool mutex_trylock(struct mutex *m)
157 return __mutex_trylock(m, NULL, -1);
163 void mutex_destroy(struct mutex *m)
166 * Caller guarantees that no one will try to take the mutex so
167 * there's no need to take the spinlock before accessing it.
169 if (m->value != MUTEX_VALUE_UNLOCKED)
171 if (!wq_is_empty(&m->wq))
172 panic("waitqueue not empty");
175 void condvar_init(struct condvar *cv)
177 *cv = (struct condvar)CONDVAR_INITIALIZER;
180 void condvar_destroy(struct condvar *cv)
182 if (cv->m && wq_have_condvar(&cv->m->wq, cv))
188 static void cv_signal(struct condvar *cv, bool only_one, const char *fname,
191 uint32_t old_itr_status;
194 old_itr_status = thread_mask_exceptions(THREAD_EXCP_ALL);
195 cpu_spin_lock(&cv->spin_lock);
197 cpu_spin_unlock(&cv->spin_lock);
198 thread_unmask_exceptions(old_itr_status);
201 wq_promote_condvar(&m->wq, cv, only_one, m, fname, lineno);
205 #ifdef CFG_MUTEX_DEBUG
206 void condvar_signal_debug(struct condvar *cv, const char *fname, int lineno)
208 cv_signal(cv, true /* only one */, fname, lineno);
211 void condvar_broadcast_debug(struct condvar *cv, const char *fname, int lineno)
213 cv_signal(cv, false /* all */, fname, lineno);
217 void condvar_signal(struct condvar *cv)
219 cv_signal(cv, true /* only one */, NULL, -1);
222 void condvar_broadcast(struct condvar *cv)
224 cv_signal(cv, false /* all */, NULL, -1);
226 #endif /*CFG_MUTEX_DEBUG*/
228 static void __condvar_wait(struct condvar *cv, struct mutex *m,
229 const char *fname, int lineno)
231 uint32_t old_itr_status;
232 struct wait_queue_elem wqe;
234 old_itr_status = thread_mask_exceptions(THREAD_EXCP_ALL);
236 /* Link this condvar to this mutex until reinitialized */
237 cpu_spin_lock(&cv->spin_lock);
238 if (cv->m && cv->m != m)
239 panic("invalid mutex");
242 cpu_spin_unlock(&cv->spin_lock);
244 cpu_spin_lock(&m->spin_lock);
246 /* Add to mutex wait queue as a condvar waiter */
247 wq_wait_init_condvar(&m->wq, &wqe, cv);
249 /* Unlock the mutex */
250 if (m->value != MUTEX_VALUE_LOCKED)
254 m->value = MUTEX_VALUE_UNLOCKED;
256 cpu_spin_unlock(&m->spin_lock);
258 thread_unmask_exceptions(old_itr_status);
260 /* Wake eventual waiters */
261 wq_wake_one(&m->wq, m, fname, lineno);
263 wq_wait_final(&m->wq, &wqe, m, fname, lineno);
268 #ifdef CFG_MUTEX_DEBUG
269 void condvar_wait_debug(struct condvar *cv, struct mutex *m,
270 const char *fname, int lineno)
272 __condvar_wait(cv, m, fname, lineno);
275 void condvar_wait(struct condvar *cv, struct mutex *m)
277 __condvar_wait(cv, m, NULL, -1);