xref: /optee_os/core/kernel/mutex.c (revision 51f49692723419b40830e2652b9773b45c9b97d4)
1*51f49692SMarouene Boubakri // SPDX-License-Identifier: BSD-2-Clause
2*51f49692SMarouene Boubakri /*
3*51f49692SMarouene Boubakri  * Copyright (c) 2015-2017, Linaro Limited
4*51f49692SMarouene Boubakri  */
5*51f49692SMarouene Boubakri 
6*51f49692SMarouene Boubakri #include <kernel/mutex.h>
7*51f49692SMarouene Boubakri #include <kernel/panic.h>
8*51f49692SMarouene Boubakri #include <kernel/refcount.h>
9*51f49692SMarouene Boubakri #include <kernel/spinlock.h>
10*51f49692SMarouene Boubakri #include <kernel/thread.h>
11*51f49692SMarouene Boubakri #include <trace.h>
12*51f49692SMarouene Boubakri 
13*51f49692SMarouene Boubakri #include "mutex_lockdep.h"
14*51f49692SMarouene Boubakri 
15*51f49692SMarouene Boubakri void mutex_init(struct mutex *m)
16*51f49692SMarouene Boubakri {
17*51f49692SMarouene Boubakri 	*m = (struct mutex)MUTEX_INITIALIZER;
18*51f49692SMarouene Boubakri }
19*51f49692SMarouene Boubakri 
20*51f49692SMarouene Boubakri void mutex_init_recursive(struct recursive_mutex *m)
21*51f49692SMarouene Boubakri {
22*51f49692SMarouene Boubakri 	*m = (struct recursive_mutex)RECURSIVE_MUTEX_INITIALIZER;
23*51f49692SMarouene Boubakri }
24*51f49692SMarouene Boubakri 
25*51f49692SMarouene Boubakri static void __mutex_lock(struct mutex *m, const char *fname, int lineno)
26*51f49692SMarouene Boubakri {
27*51f49692SMarouene Boubakri 	assert_have_no_spinlock();
28*51f49692SMarouene Boubakri 	assert(thread_get_id_may_fail() != THREAD_ID_INVALID);
29*51f49692SMarouene Boubakri 	assert(thread_is_in_normal_mode());
30*51f49692SMarouene Boubakri 
31*51f49692SMarouene Boubakri 	mutex_lock_check(m);
32*51f49692SMarouene Boubakri 
33*51f49692SMarouene Boubakri 	while (true) {
34*51f49692SMarouene Boubakri 		uint32_t old_itr_status;
35*51f49692SMarouene Boubakri 		bool can_lock;
36*51f49692SMarouene Boubakri 		struct wait_queue_elem wqe;
37*51f49692SMarouene Boubakri 
38*51f49692SMarouene Boubakri 		/*
39*51f49692SMarouene Boubakri 		 * If the mutex is locked we need to initialize the wqe
40*51f49692SMarouene Boubakri 		 * before releasing the spinlock to guarantee that we don't
41*51f49692SMarouene Boubakri 		 * miss the wakeup from mutex_unlock().
42*51f49692SMarouene Boubakri 		 *
43*51f49692SMarouene Boubakri 		 * If the mutex is unlocked we don't need to use the wqe at
44*51f49692SMarouene Boubakri 		 * all.
45*51f49692SMarouene Boubakri 		 */
46*51f49692SMarouene Boubakri 
47*51f49692SMarouene Boubakri 		old_itr_status = cpu_spin_lock_xsave(&m->spin_lock);
48*51f49692SMarouene Boubakri 
49*51f49692SMarouene Boubakri 		can_lock = !m->state;
50*51f49692SMarouene Boubakri 		if (!can_lock) {
51*51f49692SMarouene Boubakri 			wq_wait_init(&m->wq, &wqe, false /* wait_read */);
52*51f49692SMarouene Boubakri 		} else {
53*51f49692SMarouene Boubakri 			m->state = -1; /* write locked */
54*51f49692SMarouene Boubakri 		}
55*51f49692SMarouene Boubakri 
56*51f49692SMarouene Boubakri 		cpu_spin_unlock_xrestore(&m->spin_lock, old_itr_status);
57*51f49692SMarouene Boubakri 
58*51f49692SMarouene Boubakri 		if (!can_lock) {
59*51f49692SMarouene Boubakri 			/*
60*51f49692SMarouene Boubakri 			 * Someone else is holding the lock, wait in normal
61*51f49692SMarouene Boubakri 			 * world for the lock to become available.
62*51f49692SMarouene Boubakri 			 */
63*51f49692SMarouene Boubakri 			wq_wait_final(&m->wq, &wqe, m, fname, lineno);
64*51f49692SMarouene Boubakri 		} else
65*51f49692SMarouene Boubakri 			return;
66*51f49692SMarouene Boubakri 	}
67*51f49692SMarouene Boubakri }
68*51f49692SMarouene Boubakri 
69*51f49692SMarouene Boubakri static void __mutex_lock_recursive(struct recursive_mutex *m, const char *fname,
70*51f49692SMarouene Boubakri 				   int lineno)
71*51f49692SMarouene Boubakri {
72*51f49692SMarouene Boubakri 	short int ct = thread_get_id();
73*51f49692SMarouene Boubakri 
74*51f49692SMarouene Boubakri 	assert_have_no_spinlock();
75*51f49692SMarouene Boubakri 	assert(thread_is_in_normal_mode());
76*51f49692SMarouene Boubakri 
77*51f49692SMarouene Boubakri 	if (atomic_load_short(&m->owner) == ct) {
78*51f49692SMarouene Boubakri 		if (!refcount_inc(&m->lock_depth))
79*51f49692SMarouene Boubakri 			panic();
80*51f49692SMarouene Boubakri 		return;
81*51f49692SMarouene Boubakri 	}
82*51f49692SMarouene Boubakri 
83*51f49692SMarouene Boubakri 	__mutex_lock(&m->m, fname, lineno);
84*51f49692SMarouene Boubakri 
85*51f49692SMarouene Boubakri 	assert(m->owner == THREAD_ID_INVALID);
86*51f49692SMarouene Boubakri 	atomic_store_short(&m->owner, ct);
87*51f49692SMarouene Boubakri 	refcount_set(&m->lock_depth, 1);
88*51f49692SMarouene Boubakri }
89*51f49692SMarouene Boubakri 
90*51f49692SMarouene Boubakri static void __mutex_unlock(struct mutex *m, const char *fname, int lineno)
91*51f49692SMarouene Boubakri {
92*51f49692SMarouene Boubakri 	uint32_t old_itr_status;
93*51f49692SMarouene Boubakri 
94*51f49692SMarouene Boubakri 	assert_have_no_spinlock();
95*51f49692SMarouene Boubakri 	assert(thread_get_id_may_fail() != THREAD_ID_INVALID);
96*51f49692SMarouene Boubakri 
97*51f49692SMarouene Boubakri 	mutex_unlock_check(m);
98*51f49692SMarouene Boubakri 
99*51f49692SMarouene Boubakri 	old_itr_status = cpu_spin_lock_xsave(&m->spin_lock);
100*51f49692SMarouene Boubakri 
101*51f49692SMarouene Boubakri 	if (!m->state)
102*51f49692SMarouene Boubakri 		panic();
103*51f49692SMarouene Boubakri 
104*51f49692SMarouene Boubakri 	m->state = 0;
105*51f49692SMarouene Boubakri 
106*51f49692SMarouene Boubakri 	cpu_spin_unlock_xrestore(&m->spin_lock, old_itr_status);
107*51f49692SMarouene Boubakri 
108*51f49692SMarouene Boubakri 	wq_wake_next(&m->wq, m, fname, lineno);
109*51f49692SMarouene Boubakri }
110*51f49692SMarouene Boubakri 
111*51f49692SMarouene Boubakri static void __mutex_unlock_recursive(struct recursive_mutex *m,
112*51f49692SMarouene Boubakri 				     const char *fname, int lineno)
113*51f49692SMarouene Boubakri {
114*51f49692SMarouene Boubakri 	assert_have_no_spinlock();
115*51f49692SMarouene Boubakri 	assert(m->owner == thread_get_id());
116*51f49692SMarouene Boubakri 
117*51f49692SMarouene Boubakri 	if (refcount_dec(&m->lock_depth)) {
118*51f49692SMarouene Boubakri 		/*
119*51f49692SMarouene Boubakri 		 * Do an atomic store to match the atomic load in
120*51f49692SMarouene Boubakri 		 * __mutex_lock_recursive()
121*51f49692SMarouene Boubakri 		 */
122*51f49692SMarouene Boubakri 		atomic_store_short(&m->owner, THREAD_ID_INVALID);
123*51f49692SMarouene Boubakri 		__mutex_unlock(&m->m, fname, lineno);
124*51f49692SMarouene Boubakri 	}
125*51f49692SMarouene Boubakri }
126*51f49692SMarouene Boubakri 
127*51f49692SMarouene Boubakri static bool __mutex_trylock(struct mutex *m, const char *fname __unused,
128*51f49692SMarouene Boubakri 			int lineno __unused)
129*51f49692SMarouene Boubakri {
130*51f49692SMarouene Boubakri 	uint32_t old_itr_status;
131*51f49692SMarouene Boubakri 	bool can_lock_write;
132*51f49692SMarouene Boubakri 
133*51f49692SMarouene Boubakri 	assert_have_no_spinlock();
134*51f49692SMarouene Boubakri 	assert(thread_get_id_may_fail() != THREAD_ID_INVALID);
135*51f49692SMarouene Boubakri 
136*51f49692SMarouene Boubakri 	old_itr_status = cpu_spin_lock_xsave(&m->spin_lock);
137*51f49692SMarouene Boubakri 
138*51f49692SMarouene Boubakri 	can_lock_write = !m->state;
139*51f49692SMarouene Boubakri 	if (can_lock_write)
140*51f49692SMarouene Boubakri 		m->state = -1;
141*51f49692SMarouene Boubakri 
142*51f49692SMarouene Boubakri 	cpu_spin_unlock_xrestore(&m->spin_lock, old_itr_status);
143*51f49692SMarouene Boubakri 
144*51f49692SMarouene Boubakri 	if (can_lock_write)
145*51f49692SMarouene Boubakri 		mutex_trylock_check(m);
146*51f49692SMarouene Boubakri 
147*51f49692SMarouene Boubakri 	return can_lock_write;
148*51f49692SMarouene Boubakri }
149*51f49692SMarouene Boubakri 
150*51f49692SMarouene Boubakri static void __mutex_read_unlock(struct mutex *m, const char *fname, int lineno)
151*51f49692SMarouene Boubakri {
152*51f49692SMarouene Boubakri 	uint32_t old_itr_status;
153*51f49692SMarouene Boubakri 	short new_state;
154*51f49692SMarouene Boubakri 
155*51f49692SMarouene Boubakri 	assert_have_no_spinlock();
156*51f49692SMarouene Boubakri 	assert(thread_get_id_may_fail() != THREAD_ID_INVALID);
157*51f49692SMarouene Boubakri 
158*51f49692SMarouene Boubakri 	old_itr_status = cpu_spin_lock_xsave(&m->spin_lock);
159*51f49692SMarouene Boubakri 
160*51f49692SMarouene Boubakri 	if (m->state <= 0)
161*51f49692SMarouene Boubakri 		panic();
162*51f49692SMarouene Boubakri 	m->state--;
163*51f49692SMarouene Boubakri 	new_state = m->state;
164*51f49692SMarouene Boubakri 
165*51f49692SMarouene Boubakri 	cpu_spin_unlock_xrestore(&m->spin_lock, old_itr_status);
166*51f49692SMarouene Boubakri 
167*51f49692SMarouene Boubakri 	/* Wake eventual waiters if the mutex was unlocked */
168*51f49692SMarouene Boubakri 	if (!new_state)
169*51f49692SMarouene Boubakri 		wq_wake_next(&m->wq, m, fname, lineno);
170*51f49692SMarouene Boubakri }
171*51f49692SMarouene Boubakri 
172*51f49692SMarouene Boubakri static void __mutex_read_lock(struct mutex *m, const char *fname, int lineno)
173*51f49692SMarouene Boubakri {
174*51f49692SMarouene Boubakri 	assert_have_no_spinlock();
175*51f49692SMarouene Boubakri 	assert(thread_get_id_may_fail() != THREAD_ID_INVALID);
176*51f49692SMarouene Boubakri 	assert(thread_is_in_normal_mode());
177*51f49692SMarouene Boubakri 
178*51f49692SMarouene Boubakri 	while (true) {
179*51f49692SMarouene Boubakri 		uint32_t old_itr_status;
180*51f49692SMarouene Boubakri 		bool can_lock;
181*51f49692SMarouene Boubakri 		struct wait_queue_elem wqe;
182*51f49692SMarouene Boubakri 
183*51f49692SMarouene Boubakri 		/*
184*51f49692SMarouene Boubakri 		 * If the mutex is locked we need to initialize the wqe
185*51f49692SMarouene Boubakri 		 * before releasing the spinlock to guarantee that we don't
186*51f49692SMarouene Boubakri 		 * miss the wakeup from mutex_unlock().
187*51f49692SMarouene Boubakri 		 *
188*51f49692SMarouene Boubakri 		 * If the mutex is unlocked we don't need to use the wqe at
189*51f49692SMarouene Boubakri 		 * all.
190*51f49692SMarouene Boubakri 		 */
191*51f49692SMarouene Boubakri 
192*51f49692SMarouene Boubakri 		old_itr_status = cpu_spin_lock_xsave(&m->spin_lock);
193*51f49692SMarouene Boubakri 
194*51f49692SMarouene Boubakri 		can_lock = m->state != -1;
195*51f49692SMarouene Boubakri 		if (!can_lock) {
196*51f49692SMarouene Boubakri 			wq_wait_init(&m->wq, &wqe, true /* wait_read */);
197*51f49692SMarouene Boubakri 		} else {
198*51f49692SMarouene Boubakri 			m->state++; /* read_locked */
199*51f49692SMarouene Boubakri 		}
200*51f49692SMarouene Boubakri 
201*51f49692SMarouene Boubakri 		cpu_spin_unlock_xrestore(&m->spin_lock, old_itr_status);
202*51f49692SMarouene Boubakri 
203*51f49692SMarouene Boubakri 		if (!can_lock) {
204*51f49692SMarouene Boubakri 			/*
205*51f49692SMarouene Boubakri 			 * Someone else is holding the lock, wait in normal
206*51f49692SMarouene Boubakri 			 * world for the lock to become available.
207*51f49692SMarouene Boubakri 			 */
208*51f49692SMarouene Boubakri 			wq_wait_final(&m->wq, &wqe, m, fname, lineno);
209*51f49692SMarouene Boubakri 		} else
210*51f49692SMarouene Boubakri 			return;
211*51f49692SMarouene Boubakri 	}
212*51f49692SMarouene Boubakri }
213*51f49692SMarouene Boubakri 
214*51f49692SMarouene Boubakri static bool __mutex_read_trylock(struct mutex *m, const char *fname __unused,
215*51f49692SMarouene Boubakri 				 int lineno __unused)
216*51f49692SMarouene Boubakri {
217*51f49692SMarouene Boubakri 	uint32_t old_itr_status;
218*51f49692SMarouene Boubakri 	bool can_lock;
219*51f49692SMarouene Boubakri 
220*51f49692SMarouene Boubakri 	assert_have_no_spinlock();
221*51f49692SMarouene Boubakri 	assert(thread_get_id_may_fail() != THREAD_ID_INVALID);
222*51f49692SMarouene Boubakri 	assert(thread_is_in_normal_mode());
223*51f49692SMarouene Boubakri 
224*51f49692SMarouene Boubakri 	old_itr_status = cpu_spin_lock_xsave(&m->spin_lock);
225*51f49692SMarouene Boubakri 
226*51f49692SMarouene Boubakri 	can_lock = m->state != -1;
227*51f49692SMarouene Boubakri 	if (can_lock)
228*51f49692SMarouene Boubakri 		m->state++;
229*51f49692SMarouene Boubakri 
230*51f49692SMarouene Boubakri 	cpu_spin_unlock_xrestore(&m->spin_lock, old_itr_status);
231*51f49692SMarouene Boubakri 
232*51f49692SMarouene Boubakri 	return can_lock;
233*51f49692SMarouene Boubakri }
234*51f49692SMarouene Boubakri 
235*51f49692SMarouene Boubakri #ifdef CFG_MUTEX_DEBUG
236*51f49692SMarouene Boubakri void mutex_unlock_debug(struct mutex *m, const char *fname, int lineno)
237*51f49692SMarouene Boubakri {
238*51f49692SMarouene Boubakri 	__mutex_unlock(m, fname, lineno);
239*51f49692SMarouene Boubakri }
240*51f49692SMarouene Boubakri 
241*51f49692SMarouene Boubakri void mutex_lock_debug(struct mutex *m, const char *fname, int lineno)
242*51f49692SMarouene Boubakri {
243*51f49692SMarouene Boubakri 	__mutex_lock(m, fname, lineno);
244*51f49692SMarouene Boubakri }
245*51f49692SMarouene Boubakri 
246*51f49692SMarouene Boubakri bool mutex_trylock_debug(struct mutex *m, const char *fname, int lineno)
247*51f49692SMarouene Boubakri {
248*51f49692SMarouene Boubakri 	return __mutex_trylock(m, fname, lineno);
249*51f49692SMarouene Boubakri }
250*51f49692SMarouene Boubakri 
251*51f49692SMarouene Boubakri void mutex_read_unlock_debug(struct mutex *m, const char *fname, int lineno)
252*51f49692SMarouene Boubakri {
253*51f49692SMarouene Boubakri 	__mutex_read_unlock(m, fname, lineno);
254*51f49692SMarouene Boubakri }
255*51f49692SMarouene Boubakri 
256*51f49692SMarouene Boubakri void mutex_read_lock_debug(struct mutex *m, const char *fname, int lineno)
257*51f49692SMarouene Boubakri {
258*51f49692SMarouene Boubakri 	__mutex_read_lock(m, fname, lineno);
259*51f49692SMarouene Boubakri }
260*51f49692SMarouene Boubakri 
261*51f49692SMarouene Boubakri bool mutex_read_trylock_debug(struct mutex *m, const char *fname, int lineno)
262*51f49692SMarouene Boubakri {
263*51f49692SMarouene Boubakri 	return __mutex_read_trylock(m, fname, lineno);
264*51f49692SMarouene Boubakri }
265*51f49692SMarouene Boubakri 
266*51f49692SMarouene Boubakri void mutex_unlock_recursive_debug(struct recursive_mutex *m, const char *fname,
267*51f49692SMarouene Boubakri 				  int lineno)
268*51f49692SMarouene Boubakri {
269*51f49692SMarouene Boubakri 	__mutex_unlock_recursive(m, fname, lineno);
270*51f49692SMarouene Boubakri }
271*51f49692SMarouene Boubakri 
272*51f49692SMarouene Boubakri void mutex_lock_recursive_debug(struct recursive_mutex *m, const char *fname,
273*51f49692SMarouene Boubakri 				int lineno)
274*51f49692SMarouene Boubakri {
275*51f49692SMarouene Boubakri 	__mutex_lock_recursive(m, fname, lineno);
276*51f49692SMarouene Boubakri }
277*51f49692SMarouene Boubakri #else
278*51f49692SMarouene Boubakri void mutex_unlock(struct mutex *m)
279*51f49692SMarouene Boubakri {
280*51f49692SMarouene Boubakri 	__mutex_unlock(m, NULL, -1);
281*51f49692SMarouene Boubakri }
282*51f49692SMarouene Boubakri 
283*51f49692SMarouene Boubakri void mutex_unlock_recursive(struct recursive_mutex *m)
284*51f49692SMarouene Boubakri {
285*51f49692SMarouene Boubakri 	__mutex_unlock_recursive(m, NULL, -1);
286*51f49692SMarouene Boubakri }
287*51f49692SMarouene Boubakri 
288*51f49692SMarouene Boubakri void mutex_lock(struct mutex *m)
289*51f49692SMarouene Boubakri {
290*51f49692SMarouene Boubakri 	__mutex_lock(m, NULL, -1);
291*51f49692SMarouene Boubakri }
292*51f49692SMarouene Boubakri 
293*51f49692SMarouene Boubakri void mutex_lock_recursive(struct recursive_mutex *m)
294*51f49692SMarouene Boubakri {
295*51f49692SMarouene Boubakri 	__mutex_lock_recursive(m, NULL, -1);
296*51f49692SMarouene Boubakri }
297*51f49692SMarouene Boubakri 
298*51f49692SMarouene Boubakri bool mutex_trylock(struct mutex *m)
299*51f49692SMarouene Boubakri {
300*51f49692SMarouene Boubakri 	return __mutex_trylock(m, NULL, -1);
301*51f49692SMarouene Boubakri }
302*51f49692SMarouene Boubakri 
303*51f49692SMarouene Boubakri void mutex_read_unlock(struct mutex *m)
304*51f49692SMarouene Boubakri {
305*51f49692SMarouene Boubakri 	__mutex_read_unlock(m, NULL, -1);
306*51f49692SMarouene Boubakri }
307*51f49692SMarouene Boubakri 
308*51f49692SMarouene Boubakri void mutex_read_lock(struct mutex *m)
309*51f49692SMarouene Boubakri {
310*51f49692SMarouene Boubakri 	__mutex_read_lock(m, NULL, -1);
311*51f49692SMarouene Boubakri }
312*51f49692SMarouene Boubakri 
313*51f49692SMarouene Boubakri bool mutex_read_trylock(struct mutex *m)
314*51f49692SMarouene Boubakri {
315*51f49692SMarouene Boubakri 	return __mutex_read_trylock(m, NULL, -1);
316*51f49692SMarouene Boubakri }
317*51f49692SMarouene Boubakri #endif
318*51f49692SMarouene Boubakri 
319*51f49692SMarouene Boubakri void mutex_destroy(struct mutex *m)
320*51f49692SMarouene Boubakri {
321*51f49692SMarouene Boubakri 	/*
322*51f49692SMarouene Boubakri 	 * Caller guarantees that no one will try to take the mutex so
323*51f49692SMarouene Boubakri 	 * there's no need to take the spinlock before accessing it.
324*51f49692SMarouene Boubakri 	 */
325*51f49692SMarouene Boubakri 	if (m->state)
326*51f49692SMarouene Boubakri 		panic();
327*51f49692SMarouene Boubakri 	if (!wq_is_empty(&m->wq))
328*51f49692SMarouene Boubakri 		panic("waitqueue not empty");
329*51f49692SMarouene Boubakri 	mutex_destroy_check(m);
330*51f49692SMarouene Boubakri }
331*51f49692SMarouene Boubakri 
332*51f49692SMarouene Boubakri void mutex_destroy_recursive(struct recursive_mutex *m)
333*51f49692SMarouene Boubakri {
334*51f49692SMarouene Boubakri 	mutex_destroy(&m->m);
335*51f49692SMarouene Boubakri }
336*51f49692SMarouene Boubakri 
337*51f49692SMarouene Boubakri unsigned int mutex_get_recursive_lock_depth(struct recursive_mutex *m)
338*51f49692SMarouene Boubakri {
339*51f49692SMarouene Boubakri 	assert_have_no_spinlock();
340*51f49692SMarouene Boubakri 	assert(m->owner == thread_get_id());
341*51f49692SMarouene Boubakri 
342*51f49692SMarouene Boubakri 	return refcount_val(&m->lock_depth);
343*51f49692SMarouene Boubakri }
344*51f49692SMarouene Boubakri 
345*51f49692SMarouene Boubakri void condvar_init(struct condvar *cv)
346*51f49692SMarouene Boubakri {
347*51f49692SMarouene Boubakri 	*cv = (struct condvar)CONDVAR_INITIALIZER;
348*51f49692SMarouene Boubakri }
349*51f49692SMarouene Boubakri 
350*51f49692SMarouene Boubakri void condvar_destroy(struct condvar *cv)
351*51f49692SMarouene Boubakri {
352*51f49692SMarouene Boubakri 	if (cv->m && wq_have_condvar(&cv->m->wq, cv))
353*51f49692SMarouene Boubakri 		panic();
354*51f49692SMarouene Boubakri 
355*51f49692SMarouene Boubakri 	condvar_init(cv);
356*51f49692SMarouene Boubakri }
357*51f49692SMarouene Boubakri 
358*51f49692SMarouene Boubakri static void cv_signal(struct condvar *cv, bool only_one, const char *fname,
359*51f49692SMarouene Boubakri 			int lineno)
360*51f49692SMarouene Boubakri {
361*51f49692SMarouene Boubakri 	uint32_t old_itr_status;
362*51f49692SMarouene Boubakri 	struct mutex *m;
363*51f49692SMarouene Boubakri 
364*51f49692SMarouene Boubakri 	old_itr_status = cpu_spin_lock_xsave(&cv->spin_lock);
365*51f49692SMarouene Boubakri 	m = cv->m;
366*51f49692SMarouene Boubakri 	cpu_spin_unlock_xrestore(&cv->spin_lock, old_itr_status);
367*51f49692SMarouene Boubakri 
368*51f49692SMarouene Boubakri 	if (m)
369*51f49692SMarouene Boubakri 		wq_promote_condvar(&m->wq, cv, only_one, m, fname, lineno);
370*51f49692SMarouene Boubakri 
371*51f49692SMarouene Boubakri }
372*51f49692SMarouene Boubakri 
373*51f49692SMarouene Boubakri #ifdef CFG_MUTEX_DEBUG
374*51f49692SMarouene Boubakri void condvar_signal_debug(struct condvar *cv, const char *fname, int lineno)
375*51f49692SMarouene Boubakri {
376*51f49692SMarouene Boubakri 	cv_signal(cv, true /* only one */, fname, lineno);
377*51f49692SMarouene Boubakri }
378*51f49692SMarouene Boubakri 
379*51f49692SMarouene Boubakri void condvar_broadcast_debug(struct condvar *cv, const char *fname, int lineno)
380*51f49692SMarouene Boubakri {
381*51f49692SMarouene Boubakri 	cv_signal(cv, false /* all */, fname, lineno);
382*51f49692SMarouene Boubakri }
383*51f49692SMarouene Boubakri 
384*51f49692SMarouene Boubakri #else
385*51f49692SMarouene Boubakri void condvar_signal(struct condvar *cv)
386*51f49692SMarouene Boubakri {
387*51f49692SMarouene Boubakri 	cv_signal(cv, true /* only one */, NULL, -1);
388*51f49692SMarouene Boubakri }
389*51f49692SMarouene Boubakri 
390*51f49692SMarouene Boubakri void condvar_broadcast(struct condvar *cv)
391*51f49692SMarouene Boubakri {
392*51f49692SMarouene Boubakri 	cv_signal(cv, false /* all */, NULL, -1);
393*51f49692SMarouene Boubakri }
394*51f49692SMarouene Boubakri #endif /*CFG_MUTEX_DEBUG*/
395*51f49692SMarouene Boubakri 
396*51f49692SMarouene Boubakri static void __condvar_wait(struct condvar *cv, struct mutex *m,
397*51f49692SMarouene Boubakri 			const char *fname, int lineno)
398*51f49692SMarouene Boubakri {
399*51f49692SMarouene Boubakri 	uint32_t old_itr_status;
400*51f49692SMarouene Boubakri 	struct wait_queue_elem wqe;
401*51f49692SMarouene Boubakri 	short old_state;
402*51f49692SMarouene Boubakri 	short new_state;
403*51f49692SMarouene Boubakri 
404*51f49692SMarouene Boubakri 	mutex_unlock_check(m);
405*51f49692SMarouene Boubakri 
406*51f49692SMarouene Boubakri 	/* Link this condvar to this mutex until reinitialized */
407*51f49692SMarouene Boubakri 	old_itr_status = cpu_spin_lock_xsave(&cv->spin_lock);
408*51f49692SMarouene Boubakri 	if (cv->m && cv->m != m)
409*51f49692SMarouene Boubakri 		panic("invalid mutex");
410*51f49692SMarouene Boubakri 
411*51f49692SMarouene Boubakri 	cv->m = m;
412*51f49692SMarouene Boubakri 	cpu_spin_unlock(&cv->spin_lock);
413*51f49692SMarouene Boubakri 
414*51f49692SMarouene Boubakri 	cpu_spin_lock(&m->spin_lock);
415*51f49692SMarouene Boubakri 
416*51f49692SMarouene Boubakri 	if (!m->state)
417*51f49692SMarouene Boubakri 		panic();
418*51f49692SMarouene Boubakri 	old_state = m->state;
419*51f49692SMarouene Boubakri 	/* Add to mutex wait queue as a condvar waiter */
420*51f49692SMarouene Boubakri 	wq_wait_init_condvar(&m->wq, &wqe, cv, m->state > 0);
421*51f49692SMarouene Boubakri 
422*51f49692SMarouene Boubakri 	if (m->state > 1) {
423*51f49692SMarouene Boubakri 		/* Multiple read locks, remove one */
424*51f49692SMarouene Boubakri 		m->state--;
425*51f49692SMarouene Boubakri 	} else {
426*51f49692SMarouene Boubakri 		/* Only one lock (read or write), unlock the mutex */
427*51f49692SMarouene Boubakri 		m->state = 0;
428*51f49692SMarouene Boubakri 	}
429*51f49692SMarouene Boubakri 	new_state = m->state;
430*51f49692SMarouene Boubakri 
431*51f49692SMarouene Boubakri 	cpu_spin_unlock_xrestore(&m->spin_lock, old_itr_status);
432*51f49692SMarouene Boubakri 
433*51f49692SMarouene Boubakri 	/* Wake eventual waiters if the mutex was unlocked */
434*51f49692SMarouene Boubakri 	if (!new_state)
435*51f49692SMarouene Boubakri 		wq_wake_next(&m->wq, m, fname, lineno);
436*51f49692SMarouene Boubakri 
437*51f49692SMarouene Boubakri 	wq_wait_final(&m->wq, &wqe, m, fname, lineno);
438*51f49692SMarouene Boubakri 
439*51f49692SMarouene Boubakri 	if (old_state > 0)
440*51f49692SMarouene Boubakri 		mutex_read_lock(m);
441*51f49692SMarouene Boubakri 	else
442*51f49692SMarouene Boubakri 		mutex_lock(m);
443*51f49692SMarouene Boubakri }
444*51f49692SMarouene Boubakri 
445*51f49692SMarouene Boubakri #ifdef CFG_MUTEX_DEBUG
446*51f49692SMarouene Boubakri void condvar_wait_debug(struct condvar *cv, struct mutex *m,
447*51f49692SMarouene Boubakri 			const char *fname, int lineno)
448*51f49692SMarouene Boubakri {
449*51f49692SMarouene Boubakri 	__condvar_wait(cv, m, fname, lineno);
450*51f49692SMarouene Boubakri }
451*51f49692SMarouene Boubakri #else
452*51f49692SMarouene Boubakri void condvar_wait(struct condvar *cv, struct mutex *m)
453*51f49692SMarouene Boubakri {
454*51f49692SMarouene Boubakri 	__condvar_wait(cv, m, NULL, -1);
455*51f49692SMarouene Boubakri }
456*51f49692SMarouene Boubakri #endif
457