xref: /optee_os/core/kernel/mutex.c (revision 3a20c6612811bf408eca3316b49702b767803de2)
151f49692SMarouene Boubakri // SPDX-License-Identifier: BSD-2-Clause
251f49692SMarouene Boubakri /*
351f49692SMarouene Boubakri  * Copyright (c) 2015-2017, Linaro Limited
451f49692SMarouene Boubakri  */
551f49692SMarouene Boubakri 
651f49692SMarouene Boubakri #include <kernel/mutex.h>
7*3a20c661SEtienne Carriere #include <kernel/mutex_pm_aware.h>
851f49692SMarouene Boubakri #include <kernel/panic.h>
951f49692SMarouene Boubakri #include <kernel/refcount.h>
1051f49692SMarouene Boubakri #include <kernel/spinlock.h>
1151f49692SMarouene Boubakri #include <kernel/thread.h>
1251f49692SMarouene Boubakri #include <trace.h>
1351f49692SMarouene Boubakri 
1451f49692SMarouene Boubakri #include "mutex_lockdep.h"
1551f49692SMarouene Boubakri 
1651f49692SMarouene Boubakri void mutex_init(struct mutex *m)
1751f49692SMarouene Boubakri {
1851f49692SMarouene Boubakri 	*m = (struct mutex)MUTEX_INITIALIZER;
1951f49692SMarouene Boubakri }
2051f49692SMarouene Boubakri 
2151f49692SMarouene Boubakri void mutex_init_recursive(struct recursive_mutex *m)
2251f49692SMarouene Boubakri {
2351f49692SMarouene Boubakri 	*m = (struct recursive_mutex)RECURSIVE_MUTEX_INITIALIZER;
2451f49692SMarouene Boubakri }
2551f49692SMarouene Boubakri 
2651f49692SMarouene Boubakri static void __mutex_lock(struct mutex *m, const char *fname, int lineno)
2751f49692SMarouene Boubakri {
2851f49692SMarouene Boubakri 	assert_have_no_spinlock();
2951f49692SMarouene Boubakri 	assert(thread_get_id_may_fail() != THREAD_ID_INVALID);
3051f49692SMarouene Boubakri 	assert(thread_is_in_normal_mode());
3151f49692SMarouene Boubakri 
3251f49692SMarouene Boubakri 	mutex_lock_check(m);
3351f49692SMarouene Boubakri 
3451f49692SMarouene Boubakri 	while (true) {
3551f49692SMarouene Boubakri 		uint32_t old_itr_status;
3651f49692SMarouene Boubakri 		bool can_lock;
3751f49692SMarouene Boubakri 		struct wait_queue_elem wqe;
3851f49692SMarouene Boubakri 
3951f49692SMarouene Boubakri 		/*
4051f49692SMarouene Boubakri 		 * If the mutex is locked we need to initialize the wqe
4151f49692SMarouene Boubakri 		 * before releasing the spinlock to guarantee that we don't
4251f49692SMarouene Boubakri 		 * miss the wakeup from mutex_unlock().
4351f49692SMarouene Boubakri 		 *
4451f49692SMarouene Boubakri 		 * If the mutex is unlocked we don't need to use the wqe at
4551f49692SMarouene Boubakri 		 * all.
4651f49692SMarouene Boubakri 		 */
4751f49692SMarouene Boubakri 
4851f49692SMarouene Boubakri 		old_itr_status = cpu_spin_lock_xsave(&m->spin_lock);
4951f49692SMarouene Boubakri 
5051f49692SMarouene Boubakri 		can_lock = !m->state;
5151f49692SMarouene Boubakri 		if (!can_lock) {
5251f49692SMarouene Boubakri 			wq_wait_init(&m->wq, &wqe, false /* wait_read */);
5351f49692SMarouene Boubakri 		} else {
5451f49692SMarouene Boubakri 			m->state = -1; /* write locked */
5551f49692SMarouene Boubakri 		}
5651f49692SMarouene Boubakri 
5751f49692SMarouene Boubakri 		cpu_spin_unlock_xrestore(&m->spin_lock, old_itr_status);
5851f49692SMarouene Boubakri 
5951f49692SMarouene Boubakri 		if (!can_lock) {
6051f49692SMarouene Boubakri 			/*
6151f49692SMarouene Boubakri 			 * Someone else is holding the lock, wait in normal
6251f49692SMarouene Boubakri 			 * world for the lock to become available.
6351f49692SMarouene Boubakri 			 */
6451f49692SMarouene Boubakri 			wq_wait_final(&m->wq, &wqe, m, fname, lineno);
6551f49692SMarouene Boubakri 		} else
6651f49692SMarouene Boubakri 			return;
6751f49692SMarouene Boubakri 	}
6851f49692SMarouene Boubakri }
6951f49692SMarouene Boubakri 
7051f49692SMarouene Boubakri static void __mutex_lock_recursive(struct recursive_mutex *m, const char *fname,
7151f49692SMarouene Boubakri 				   int lineno)
7251f49692SMarouene Boubakri {
7351f49692SMarouene Boubakri 	short int ct = thread_get_id();
7451f49692SMarouene Boubakri 
7551f49692SMarouene Boubakri 	assert_have_no_spinlock();
7651f49692SMarouene Boubakri 	assert(thread_is_in_normal_mode());
7751f49692SMarouene Boubakri 
7851f49692SMarouene Boubakri 	if (atomic_load_short(&m->owner) == ct) {
7951f49692SMarouene Boubakri 		if (!refcount_inc(&m->lock_depth))
8051f49692SMarouene Boubakri 			panic();
8151f49692SMarouene Boubakri 		return;
8251f49692SMarouene Boubakri 	}
8351f49692SMarouene Boubakri 
8451f49692SMarouene Boubakri 	__mutex_lock(&m->m, fname, lineno);
8551f49692SMarouene Boubakri 
8651f49692SMarouene Boubakri 	assert(m->owner == THREAD_ID_INVALID);
8751f49692SMarouene Boubakri 	atomic_store_short(&m->owner, ct);
8851f49692SMarouene Boubakri 	refcount_set(&m->lock_depth, 1);
8951f49692SMarouene Boubakri }
9051f49692SMarouene Boubakri 
9151f49692SMarouene Boubakri static void __mutex_unlock(struct mutex *m, const char *fname, int lineno)
9251f49692SMarouene Boubakri {
9351f49692SMarouene Boubakri 	uint32_t old_itr_status;
9451f49692SMarouene Boubakri 
9551f49692SMarouene Boubakri 	assert_have_no_spinlock();
9651f49692SMarouene Boubakri 	assert(thread_get_id_may_fail() != THREAD_ID_INVALID);
9751f49692SMarouene Boubakri 
9851f49692SMarouene Boubakri 	mutex_unlock_check(m);
9951f49692SMarouene Boubakri 
10051f49692SMarouene Boubakri 	old_itr_status = cpu_spin_lock_xsave(&m->spin_lock);
10151f49692SMarouene Boubakri 
10251f49692SMarouene Boubakri 	if (!m->state)
10351f49692SMarouene Boubakri 		panic();
10451f49692SMarouene Boubakri 
10551f49692SMarouene Boubakri 	m->state = 0;
10651f49692SMarouene Boubakri 
10751f49692SMarouene Boubakri 	cpu_spin_unlock_xrestore(&m->spin_lock, old_itr_status);
10851f49692SMarouene Boubakri 
10951f49692SMarouene Boubakri 	wq_wake_next(&m->wq, m, fname, lineno);
11051f49692SMarouene Boubakri }
11151f49692SMarouene Boubakri 
11251f49692SMarouene Boubakri static void __mutex_unlock_recursive(struct recursive_mutex *m,
11351f49692SMarouene Boubakri 				     const char *fname, int lineno)
11451f49692SMarouene Boubakri {
11551f49692SMarouene Boubakri 	assert_have_no_spinlock();
11651f49692SMarouene Boubakri 	assert(m->owner == thread_get_id());
11751f49692SMarouene Boubakri 
11851f49692SMarouene Boubakri 	if (refcount_dec(&m->lock_depth)) {
11951f49692SMarouene Boubakri 		/*
12051f49692SMarouene Boubakri 		 * Do an atomic store to match the atomic load in
12151f49692SMarouene Boubakri 		 * __mutex_lock_recursive()
12251f49692SMarouene Boubakri 		 */
12351f49692SMarouene Boubakri 		atomic_store_short(&m->owner, THREAD_ID_INVALID);
12451f49692SMarouene Boubakri 		__mutex_unlock(&m->m, fname, lineno);
12551f49692SMarouene Boubakri 	}
12651f49692SMarouene Boubakri }
12751f49692SMarouene Boubakri 
12851f49692SMarouene Boubakri static bool __mutex_trylock(struct mutex *m, const char *fname __unused,
12951f49692SMarouene Boubakri 			int lineno __unused)
13051f49692SMarouene Boubakri {
13151f49692SMarouene Boubakri 	uint32_t old_itr_status;
13251f49692SMarouene Boubakri 	bool can_lock_write;
13351f49692SMarouene Boubakri 
13451f49692SMarouene Boubakri 	assert_have_no_spinlock();
13551f49692SMarouene Boubakri 	assert(thread_get_id_may_fail() != THREAD_ID_INVALID);
13651f49692SMarouene Boubakri 
13751f49692SMarouene Boubakri 	old_itr_status = cpu_spin_lock_xsave(&m->spin_lock);
13851f49692SMarouene Boubakri 
13951f49692SMarouene Boubakri 	can_lock_write = !m->state;
14051f49692SMarouene Boubakri 	if (can_lock_write)
14151f49692SMarouene Boubakri 		m->state = -1;
14251f49692SMarouene Boubakri 
14351f49692SMarouene Boubakri 	cpu_spin_unlock_xrestore(&m->spin_lock, old_itr_status);
14451f49692SMarouene Boubakri 
14551f49692SMarouene Boubakri 	if (can_lock_write)
14651f49692SMarouene Boubakri 		mutex_trylock_check(m);
14751f49692SMarouene Boubakri 
14851f49692SMarouene Boubakri 	return can_lock_write;
14951f49692SMarouene Boubakri }
15051f49692SMarouene Boubakri 
15151f49692SMarouene Boubakri static void __mutex_read_unlock(struct mutex *m, const char *fname, int lineno)
15251f49692SMarouene Boubakri {
15351f49692SMarouene Boubakri 	uint32_t old_itr_status;
15451f49692SMarouene Boubakri 	short new_state;
15551f49692SMarouene Boubakri 
15651f49692SMarouene Boubakri 	assert_have_no_spinlock();
15751f49692SMarouene Boubakri 	assert(thread_get_id_may_fail() != THREAD_ID_INVALID);
15851f49692SMarouene Boubakri 
15951f49692SMarouene Boubakri 	old_itr_status = cpu_spin_lock_xsave(&m->spin_lock);
16051f49692SMarouene Boubakri 
16151f49692SMarouene Boubakri 	if (m->state <= 0)
16251f49692SMarouene Boubakri 		panic();
16351f49692SMarouene Boubakri 	m->state--;
16451f49692SMarouene Boubakri 	new_state = m->state;
16551f49692SMarouene Boubakri 
16651f49692SMarouene Boubakri 	cpu_spin_unlock_xrestore(&m->spin_lock, old_itr_status);
16751f49692SMarouene Boubakri 
16851f49692SMarouene Boubakri 	/* Wake eventual waiters if the mutex was unlocked */
16951f49692SMarouene Boubakri 	if (!new_state)
17051f49692SMarouene Boubakri 		wq_wake_next(&m->wq, m, fname, lineno);
17151f49692SMarouene Boubakri }
17251f49692SMarouene Boubakri 
17351f49692SMarouene Boubakri static void __mutex_read_lock(struct mutex *m, const char *fname, int lineno)
17451f49692SMarouene Boubakri {
17551f49692SMarouene Boubakri 	assert_have_no_spinlock();
17651f49692SMarouene Boubakri 	assert(thread_get_id_may_fail() != THREAD_ID_INVALID);
17751f49692SMarouene Boubakri 	assert(thread_is_in_normal_mode());
17851f49692SMarouene Boubakri 
17951f49692SMarouene Boubakri 	while (true) {
18051f49692SMarouene Boubakri 		uint32_t old_itr_status;
18151f49692SMarouene Boubakri 		bool can_lock;
18251f49692SMarouene Boubakri 		struct wait_queue_elem wqe;
18351f49692SMarouene Boubakri 
18451f49692SMarouene Boubakri 		/*
18551f49692SMarouene Boubakri 		 * If the mutex is locked we need to initialize the wqe
18651f49692SMarouene Boubakri 		 * before releasing the spinlock to guarantee that we don't
18751f49692SMarouene Boubakri 		 * miss the wakeup from mutex_unlock().
18851f49692SMarouene Boubakri 		 *
18951f49692SMarouene Boubakri 		 * If the mutex is unlocked we don't need to use the wqe at
19051f49692SMarouene Boubakri 		 * all.
19151f49692SMarouene Boubakri 		 */
19251f49692SMarouene Boubakri 
19351f49692SMarouene Boubakri 		old_itr_status = cpu_spin_lock_xsave(&m->spin_lock);
19451f49692SMarouene Boubakri 
19551f49692SMarouene Boubakri 		can_lock = m->state != -1;
19651f49692SMarouene Boubakri 		if (!can_lock) {
19751f49692SMarouene Boubakri 			wq_wait_init(&m->wq, &wqe, true /* wait_read */);
19851f49692SMarouene Boubakri 		} else {
19951f49692SMarouene Boubakri 			m->state++; /* read_locked */
20051f49692SMarouene Boubakri 		}
20151f49692SMarouene Boubakri 
20251f49692SMarouene Boubakri 		cpu_spin_unlock_xrestore(&m->spin_lock, old_itr_status);
20351f49692SMarouene Boubakri 
20451f49692SMarouene Boubakri 		if (!can_lock) {
20551f49692SMarouene Boubakri 			/*
20651f49692SMarouene Boubakri 			 * Someone else is holding the lock, wait in normal
20751f49692SMarouene Boubakri 			 * world for the lock to become available.
20851f49692SMarouene Boubakri 			 */
20951f49692SMarouene Boubakri 			wq_wait_final(&m->wq, &wqe, m, fname, lineno);
21051f49692SMarouene Boubakri 		} else
21151f49692SMarouene Boubakri 			return;
21251f49692SMarouene Boubakri 	}
21351f49692SMarouene Boubakri }
21451f49692SMarouene Boubakri 
21551f49692SMarouene Boubakri static bool __mutex_read_trylock(struct mutex *m, const char *fname __unused,
21651f49692SMarouene Boubakri 				 int lineno __unused)
21751f49692SMarouene Boubakri {
21851f49692SMarouene Boubakri 	uint32_t old_itr_status;
21951f49692SMarouene Boubakri 	bool can_lock;
22051f49692SMarouene Boubakri 
22151f49692SMarouene Boubakri 	assert_have_no_spinlock();
22251f49692SMarouene Boubakri 	assert(thread_get_id_may_fail() != THREAD_ID_INVALID);
22351f49692SMarouene Boubakri 	assert(thread_is_in_normal_mode());
22451f49692SMarouene Boubakri 
22551f49692SMarouene Boubakri 	old_itr_status = cpu_spin_lock_xsave(&m->spin_lock);
22651f49692SMarouene Boubakri 
22751f49692SMarouene Boubakri 	can_lock = m->state != -1;
22851f49692SMarouene Boubakri 	if (can_lock)
22951f49692SMarouene Boubakri 		m->state++;
23051f49692SMarouene Boubakri 
23151f49692SMarouene Boubakri 	cpu_spin_unlock_xrestore(&m->spin_lock, old_itr_status);
23251f49692SMarouene Boubakri 
23351f49692SMarouene Boubakri 	return can_lock;
23451f49692SMarouene Boubakri }
23551f49692SMarouene Boubakri 
23651f49692SMarouene Boubakri #ifdef CFG_MUTEX_DEBUG
23751f49692SMarouene Boubakri void mutex_unlock_debug(struct mutex *m, const char *fname, int lineno)
23851f49692SMarouene Boubakri {
23951f49692SMarouene Boubakri 	__mutex_unlock(m, fname, lineno);
24051f49692SMarouene Boubakri }
24151f49692SMarouene Boubakri 
24251f49692SMarouene Boubakri void mutex_lock_debug(struct mutex *m, const char *fname, int lineno)
24351f49692SMarouene Boubakri {
24451f49692SMarouene Boubakri 	__mutex_lock(m, fname, lineno);
24551f49692SMarouene Boubakri }
24651f49692SMarouene Boubakri 
24751f49692SMarouene Boubakri bool mutex_trylock_debug(struct mutex *m, const char *fname, int lineno)
24851f49692SMarouene Boubakri {
24951f49692SMarouene Boubakri 	return __mutex_trylock(m, fname, lineno);
25051f49692SMarouene Boubakri }
25151f49692SMarouene Boubakri 
25251f49692SMarouene Boubakri void mutex_read_unlock_debug(struct mutex *m, const char *fname, int lineno)
25351f49692SMarouene Boubakri {
25451f49692SMarouene Boubakri 	__mutex_read_unlock(m, fname, lineno);
25551f49692SMarouene Boubakri }
25651f49692SMarouene Boubakri 
25751f49692SMarouene Boubakri void mutex_read_lock_debug(struct mutex *m, const char *fname, int lineno)
25851f49692SMarouene Boubakri {
25951f49692SMarouene Boubakri 	__mutex_read_lock(m, fname, lineno);
26051f49692SMarouene Boubakri }
26151f49692SMarouene Boubakri 
26251f49692SMarouene Boubakri bool mutex_read_trylock_debug(struct mutex *m, const char *fname, int lineno)
26351f49692SMarouene Boubakri {
26451f49692SMarouene Boubakri 	return __mutex_read_trylock(m, fname, lineno);
26551f49692SMarouene Boubakri }
26651f49692SMarouene Boubakri 
26751f49692SMarouene Boubakri void mutex_unlock_recursive_debug(struct recursive_mutex *m, const char *fname,
26851f49692SMarouene Boubakri 				  int lineno)
26951f49692SMarouene Boubakri {
27051f49692SMarouene Boubakri 	__mutex_unlock_recursive(m, fname, lineno);
27151f49692SMarouene Boubakri }
27251f49692SMarouene Boubakri 
27351f49692SMarouene Boubakri void mutex_lock_recursive_debug(struct recursive_mutex *m, const char *fname,
27451f49692SMarouene Boubakri 				int lineno)
27551f49692SMarouene Boubakri {
27651f49692SMarouene Boubakri 	__mutex_lock_recursive(m, fname, lineno);
27751f49692SMarouene Boubakri }
27851f49692SMarouene Boubakri #else
27951f49692SMarouene Boubakri void mutex_unlock(struct mutex *m)
28051f49692SMarouene Boubakri {
28151f49692SMarouene Boubakri 	__mutex_unlock(m, NULL, -1);
28251f49692SMarouene Boubakri }
28351f49692SMarouene Boubakri 
28451f49692SMarouene Boubakri void mutex_unlock_recursive(struct recursive_mutex *m)
28551f49692SMarouene Boubakri {
28651f49692SMarouene Boubakri 	__mutex_unlock_recursive(m, NULL, -1);
28751f49692SMarouene Boubakri }
28851f49692SMarouene Boubakri 
28951f49692SMarouene Boubakri void mutex_lock(struct mutex *m)
29051f49692SMarouene Boubakri {
29151f49692SMarouene Boubakri 	__mutex_lock(m, NULL, -1);
29251f49692SMarouene Boubakri }
29351f49692SMarouene Boubakri 
29451f49692SMarouene Boubakri void mutex_lock_recursive(struct recursive_mutex *m)
29551f49692SMarouene Boubakri {
29651f49692SMarouene Boubakri 	__mutex_lock_recursive(m, NULL, -1);
29751f49692SMarouene Boubakri }
29851f49692SMarouene Boubakri 
29951f49692SMarouene Boubakri bool mutex_trylock(struct mutex *m)
30051f49692SMarouene Boubakri {
30151f49692SMarouene Boubakri 	return __mutex_trylock(m, NULL, -1);
30251f49692SMarouene Boubakri }
30351f49692SMarouene Boubakri 
30451f49692SMarouene Boubakri void mutex_read_unlock(struct mutex *m)
30551f49692SMarouene Boubakri {
30651f49692SMarouene Boubakri 	__mutex_read_unlock(m, NULL, -1);
30751f49692SMarouene Boubakri }
30851f49692SMarouene Boubakri 
30951f49692SMarouene Boubakri void mutex_read_lock(struct mutex *m)
31051f49692SMarouene Boubakri {
31151f49692SMarouene Boubakri 	__mutex_read_lock(m, NULL, -1);
31251f49692SMarouene Boubakri }
31351f49692SMarouene Boubakri 
31451f49692SMarouene Boubakri bool mutex_read_trylock(struct mutex *m)
31551f49692SMarouene Boubakri {
31651f49692SMarouene Boubakri 	return __mutex_read_trylock(m, NULL, -1);
31751f49692SMarouene Boubakri }
31851f49692SMarouene Boubakri #endif
31951f49692SMarouene Boubakri 
32051f49692SMarouene Boubakri void mutex_destroy(struct mutex *m)
32151f49692SMarouene Boubakri {
32251f49692SMarouene Boubakri 	/*
32351f49692SMarouene Boubakri 	 * Caller guarantees that no one will try to take the mutex so
32451f49692SMarouene Boubakri 	 * there's no need to take the spinlock before accessing it.
32551f49692SMarouene Boubakri 	 */
32651f49692SMarouene Boubakri 	if (m->state)
32751f49692SMarouene Boubakri 		panic();
32851f49692SMarouene Boubakri 	if (!wq_is_empty(&m->wq))
32951f49692SMarouene Boubakri 		panic("waitqueue not empty");
33051f49692SMarouene Boubakri 	mutex_destroy_check(m);
33151f49692SMarouene Boubakri }
33251f49692SMarouene Boubakri 
33351f49692SMarouene Boubakri void mutex_destroy_recursive(struct recursive_mutex *m)
33451f49692SMarouene Boubakri {
33551f49692SMarouene Boubakri 	mutex_destroy(&m->m);
33651f49692SMarouene Boubakri }
33751f49692SMarouene Boubakri 
33851f49692SMarouene Boubakri unsigned int mutex_get_recursive_lock_depth(struct recursive_mutex *m)
33951f49692SMarouene Boubakri {
34051f49692SMarouene Boubakri 	assert_have_no_spinlock();
34151f49692SMarouene Boubakri 	assert(m->owner == thread_get_id());
34251f49692SMarouene Boubakri 
34351f49692SMarouene Boubakri 	return refcount_val(&m->lock_depth);
34451f49692SMarouene Boubakri }
34551f49692SMarouene Boubakri 
346*3a20c661SEtienne Carriere void mutex_pm_aware_init(struct mutex_pm_aware *m)
347*3a20c661SEtienne Carriere {
348*3a20c661SEtienne Carriere 	*m = (struct mutex_pm_aware)MUTEX_PM_AWARE_INITIALIZER;
349*3a20c661SEtienne Carriere }
350*3a20c661SEtienne Carriere 
351*3a20c661SEtienne Carriere void mutex_pm_aware_destroy(struct mutex_pm_aware *m)
352*3a20c661SEtienne Carriere {
353*3a20c661SEtienne Carriere 	mutex_destroy(&m->mutex);
354*3a20c661SEtienne Carriere }
355*3a20c661SEtienne Carriere 
356*3a20c661SEtienne Carriere void mutex_pm_aware_lock(struct mutex_pm_aware *m)
357*3a20c661SEtienne Carriere {
358*3a20c661SEtienne Carriere 	if (thread_get_id_may_fail() == THREAD_ID_INVALID) {
359*3a20c661SEtienne Carriere 		if (!cpu_spin_trylock(&m->lock) || m->mutex.state)
360*3a20c661SEtienne Carriere 			panic();
361*3a20c661SEtienne Carriere 	} else {
362*3a20c661SEtienne Carriere 		mutex_lock(&m->mutex);
363*3a20c661SEtienne Carriere 		if (!thread_spin_trylock(&m->lock))
364*3a20c661SEtienne Carriere 			panic();
365*3a20c661SEtienne Carriere 	}
366*3a20c661SEtienne Carriere }
367*3a20c661SEtienne Carriere 
368*3a20c661SEtienne Carriere void mutex_pm_aware_unlock(struct mutex_pm_aware *m)
369*3a20c661SEtienne Carriere {
370*3a20c661SEtienne Carriere 	if (thread_get_id_may_fail() == THREAD_ID_INVALID) {
371*3a20c661SEtienne Carriere 		assert(!m->mutex.state);
372*3a20c661SEtienne Carriere 		cpu_spin_unlock(&m->lock);
373*3a20c661SEtienne Carriere 	} else {
374*3a20c661SEtienne Carriere 		thread_spin_unlock(&m->lock);
375*3a20c661SEtienne Carriere 		mutex_unlock(&m->mutex);
376*3a20c661SEtienne Carriere 	}
377*3a20c661SEtienne Carriere }
378*3a20c661SEtienne Carriere 
37951f49692SMarouene Boubakri void condvar_init(struct condvar *cv)
38051f49692SMarouene Boubakri {
38151f49692SMarouene Boubakri 	*cv = (struct condvar)CONDVAR_INITIALIZER;
38251f49692SMarouene Boubakri }
38351f49692SMarouene Boubakri 
38451f49692SMarouene Boubakri void condvar_destroy(struct condvar *cv)
38551f49692SMarouene Boubakri {
38651f49692SMarouene Boubakri 	if (cv->m && wq_have_condvar(&cv->m->wq, cv))
38751f49692SMarouene Boubakri 		panic();
38851f49692SMarouene Boubakri 
38951f49692SMarouene Boubakri 	condvar_init(cv);
39051f49692SMarouene Boubakri }
39151f49692SMarouene Boubakri 
39251f49692SMarouene Boubakri static void cv_signal(struct condvar *cv, bool only_one, const char *fname,
39351f49692SMarouene Boubakri 			int lineno)
39451f49692SMarouene Boubakri {
39551f49692SMarouene Boubakri 	uint32_t old_itr_status;
39651f49692SMarouene Boubakri 	struct mutex *m;
39751f49692SMarouene Boubakri 
39851f49692SMarouene Boubakri 	old_itr_status = cpu_spin_lock_xsave(&cv->spin_lock);
39951f49692SMarouene Boubakri 	m = cv->m;
40051f49692SMarouene Boubakri 	cpu_spin_unlock_xrestore(&cv->spin_lock, old_itr_status);
40151f49692SMarouene Boubakri 
40251f49692SMarouene Boubakri 	if (m)
40351f49692SMarouene Boubakri 		wq_promote_condvar(&m->wq, cv, only_one, m, fname, lineno);
40451f49692SMarouene Boubakri 
40551f49692SMarouene Boubakri }
40651f49692SMarouene Boubakri 
40751f49692SMarouene Boubakri #ifdef CFG_MUTEX_DEBUG
40851f49692SMarouene Boubakri void condvar_signal_debug(struct condvar *cv, const char *fname, int lineno)
40951f49692SMarouene Boubakri {
41051f49692SMarouene Boubakri 	cv_signal(cv, true /* only one */, fname, lineno);
41151f49692SMarouene Boubakri }
41251f49692SMarouene Boubakri 
41351f49692SMarouene Boubakri void condvar_broadcast_debug(struct condvar *cv, const char *fname, int lineno)
41451f49692SMarouene Boubakri {
41551f49692SMarouene Boubakri 	cv_signal(cv, false /* all */, fname, lineno);
41651f49692SMarouene Boubakri }
41751f49692SMarouene Boubakri 
41851f49692SMarouene Boubakri #else
41951f49692SMarouene Boubakri void condvar_signal(struct condvar *cv)
42051f49692SMarouene Boubakri {
42151f49692SMarouene Boubakri 	cv_signal(cv, true /* only one */, NULL, -1);
42251f49692SMarouene Boubakri }
42351f49692SMarouene Boubakri 
42451f49692SMarouene Boubakri void condvar_broadcast(struct condvar *cv)
42551f49692SMarouene Boubakri {
42651f49692SMarouene Boubakri 	cv_signal(cv, false /* all */, NULL, -1);
42751f49692SMarouene Boubakri }
42851f49692SMarouene Boubakri #endif /*CFG_MUTEX_DEBUG*/
42951f49692SMarouene Boubakri 
43051f49692SMarouene Boubakri static void __condvar_wait(struct condvar *cv, struct mutex *m,
43151f49692SMarouene Boubakri 			const char *fname, int lineno)
43251f49692SMarouene Boubakri {
43351f49692SMarouene Boubakri 	uint32_t old_itr_status;
43451f49692SMarouene Boubakri 	struct wait_queue_elem wqe;
43551f49692SMarouene Boubakri 	short old_state;
43651f49692SMarouene Boubakri 	short new_state;
43751f49692SMarouene Boubakri 
43851f49692SMarouene Boubakri 	mutex_unlock_check(m);
43951f49692SMarouene Boubakri 
44051f49692SMarouene Boubakri 	/* Link this condvar to this mutex until reinitialized */
44151f49692SMarouene Boubakri 	old_itr_status = cpu_spin_lock_xsave(&cv->spin_lock);
44251f49692SMarouene Boubakri 	if (cv->m && cv->m != m)
44351f49692SMarouene Boubakri 		panic("invalid mutex");
44451f49692SMarouene Boubakri 
44551f49692SMarouene Boubakri 	cv->m = m;
44651f49692SMarouene Boubakri 	cpu_spin_unlock(&cv->spin_lock);
44751f49692SMarouene Boubakri 
44851f49692SMarouene Boubakri 	cpu_spin_lock(&m->spin_lock);
44951f49692SMarouene Boubakri 
45051f49692SMarouene Boubakri 	if (!m->state)
45151f49692SMarouene Boubakri 		panic();
45251f49692SMarouene Boubakri 	old_state = m->state;
45351f49692SMarouene Boubakri 	/* Add to mutex wait queue as a condvar waiter */
45451f49692SMarouene Boubakri 	wq_wait_init_condvar(&m->wq, &wqe, cv, m->state > 0);
45551f49692SMarouene Boubakri 
45651f49692SMarouene Boubakri 	if (m->state > 1) {
45751f49692SMarouene Boubakri 		/* Multiple read locks, remove one */
45851f49692SMarouene Boubakri 		m->state--;
45951f49692SMarouene Boubakri 	} else {
46051f49692SMarouene Boubakri 		/* Only one lock (read or write), unlock the mutex */
46151f49692SMarouene Boubakri 		m->state = 0;
46251f49692SMarouene Boubakri 	}
46351f49692SMarouene Boubakri 	new_state = m->state;
46451f49692SMarouene Boubakri 
46551f49692SMarouene Boubakri 	cpu_spin_unlock_xrestore(&m->spin_lock, old_itr_status);
46651f49692SMarouene Boubakri 
46751f49692SMarouene Boubakri 	/* Wake eventual waiters if the mutex was unlocked */
46851f49692SMarouene Boubakri 	if (!new_state)
46951f49692SMarouene Boubakri 		wq_wake_next(&m->wq, m, fname, lineno);
47051f49692SMarouene Boubakri 
47151f49692SMarouene Boubakri 	wq_wait_final(&m->wq, &wqe, m, fname, lineno);
47251f49692SMarouene Boubakri 
47351f49692SMarouene Boubakri 	if (old_state > 0)
47451f49692SMarouene Boubakri 		mutex_read_lock(m);
47551f49692SMarouene Boubakri 	else
47651f49692SMarouene Boubakri 		mutex_lock(m);
47751f49692SMarouene Boubakri }
47851f49692SMarouene Boubakri 
47951f49692SMarouene Boubakri #ifdef CFG_MUTEX_DEBUG
48051f49692SMarouene Boubakri void condvar_wait_debug(struct condvar *cv, struct mutex *m,
48151f49692SMarouene Boubakri 			const char *fname, int lineno)
48251f49692SMarouene Boubakri {
48351f49692SMarouene Boubakri 	__condvar_wait(cv, m, fname, lineno);
48451f49692SMarouene Boubakri }
48551f49692SMarouene Boubakri #else
48651f49692SMarouene Boubakri void condvar_wait(struct condvar *cv, struct mutex *m)
48751f49692SMarouene Boubakri {
48851f49692SMarouene Boubakri 	__condvar_wait(cv, m, NULL, -1);
48951f49692SMarouene Boubakri }
49051f49692SMarouene Boubakri #endif
491