xref: /optee_os/core/kernel/mutex.c (revision ea413ca5cc1e2af1ae200534480588faed975f22)
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>
73a20c661SEtienne 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 
mutex_init(struct mutex * m)1651f49692SMarouene Boubakri void mutex_init(struct mutex *m)
1751f49692SMarouene Boubakri {
1851f49692SMarouene Boubakri 	*m = (struct mutex)MUTEX_INITIALIZER;
1951f49692SMarouene Boubakri }
2051f49692SMarouene Boubakri 
mutex_init_recursive(struct recursive_mutex * m)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 
__mutex_lock(struct mutex * m,const char * fname,int lineno)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 			 */
64*ea413ca5SGavin Liu 			wq_wait_final(&m->wq, &wqe, 0, m, fname, lineno);
6551f49692SMarouene Boubakri 		} else
6651f49692SMarouene Boubakri 			return;
6751f49692SMarouene Boubakri 	}
6851f49692SMarouene Boubakri }
6951f49692SMarouene Boubakri 
__mutex_lock_recursive(struct recursive_mutex * m,const char * fname,int lineno)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 
__mutex_unlock(struct mutex * m,const char * fname,int lineno)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 
__mutex_unlock_recursive(struct recursive_mutex * m,const char * fname,int lineno)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 
__mutex_trylock(struct mutex * m,const char * fname __unused,int lineno __unused)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 
__mutex_read_unlock(struct mutex * m,const char * fname,int lineno)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 
__mutex_read_lock(struct mutex * m,const char * fname,int lineno)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 			 */
209*ea413ca5SGavin Liu 			wq_wait_final(&m->wq, &wqe, 0, m, fname, lineno);
21051f49692SMarouene Boubakri 		} else
21151f49692SMarouene Boubakri 			return;
21251f49692SMarouene Boubakri 	}
21351f49692SMarouene Boubakri }
21451f49692SMarouene Boubakri 
__mutex_read_trylock(struct mutex * m,const char * fname __unused,int lineno __unused)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
mutex_unlock_debug(struct mutex * m,const char * fname,int lineno)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 
mutex_lock_debug(struct mutex * m,const char * fname,int lineno)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 
mutex_trylock_debug(struct mutex * m,const char * fname,int lineno)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 
mutex_read_unlock_debug(struct mutex * m,const char * fname,int lineno)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 
mutex_read_lock_debug(struct mutex * m,const char * fname,int lineno)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 
mutex_read_trylock_debug(struct mutex * m,const char * fname,int lineno)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 
mutex_unlock_recursive_debug(struct recursive_mutex * m,const char * fname,int lineno)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 
mutex_lock_recursive_debug(struct recursive_mutex * m,const char * fname,int lineno)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
mutex_unlock(struct mutex * m)27951f49692SMarouene Boubakri void mutex_unlock(struct mutex *m)
28051f49692SMarouene Boubakri {
28151f49692SMarouene Boubakri 	__mutex_unlock(m, NULL, -1);
28251f49692SMarouene Boubakri }
28351f49692SMarouene Boubakri 
mutex_unlock_recursive(struct recursive_mutex * m)28451f49692SMarouene Boubakri void mutex_unlock_recursive(struct recursive_mutex *m)
28551f49692SMarouene Boubakri {
28651f49692SMarouene Boubakri 	__mutex_unlock_recursive(m, NULL, -1);
28751f49692SMarouene Boubakri }
28851f49692SMarouene Boubakri 
mutex_lock(struct mutex * m)28951f49692SMarouene Boubakri void mutex_lock(struct mutex *m)
29051f49692SMarouene Boubakri {
29151f49692SMarouene Boubakri 	__mutex_lock(m, NULL, -1);
29251f49692SMarouene Boubakri }
29351f49692SMarouene Boubakri 
mutex_lock_recursive(struct recursive_mutex * m)29451f49692SMarouene Boubakri void mutex_lock_recursive(struct recursive_mutex *m)
29551f49692SMarouene Boubakri {
29651f49692SMarouene Boubakri 	__mutex_lock_recursive(m, NULL, -1);
29751f49692SMarouene Boubakri }
29851f49692SMarouene Boubakri 
mutex_trylock(struct mutex * m)29951f49692SMarouene Boubakri bool mutex_trylock(struct mutex *m)
30051f49692SMarouene Boubakri {
30151f49692SMarouene Boubakri 	return __mutex_trylock(m, NULL, -1);
30251f49692SMarouene Boubakri }
30351f49692SMarouene Boubakri 
mutex_read_unlock(struct mutex * m)30451f49692SMarouene Boubakri void mutex_read_unlock(struct mutex *m)
30551f49692SMarouene Boubakri {
30651f49692SMarouene Boubakri 	__mutex_read_unlock(m, NULL, -1);
30751f49692SMarouene Boubakri }
30851f49692SMarouene Boubakri 
mutex_read_lock(struct mutex * m)30951f49692SMarouene Boubakri void mutex_read_lock(struct mutex *m)
31051f49692SMarouene Boubakri {
31151f49692SMarouene Boubakri 	__mutex_read_lock(m, NULL, -1);
31251f49692SMarouene Boubakri }
31351f49692SMarouene Boubakri 
mutex_read_trylock(struct mutex * m)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 
mutex_destroy(struct mutex * m)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 
mutex_destroy_recursive(struct recursive_mutex * m)33351f49692SMarouene Boubakri void mutex_destroy_recursive(struct recursive_mutex *m)
33451f49692SMarouene Boubakri {
33551f49692SMarouene Boubakri 	mutex_destroy(&m->m);
33651f49692SMarouene Boubakri }
33751f49692SMarouene Boubakri 
mutex_get_recursive_lock_depth(struct recursive_mutex * m)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 
mutex_pm_aware_init(struct mutex_pm_aware * m)3463a20c661SEtienne Carriere void mutex_pm_aware_init(struct mutex_pm_aware *m)
3473a20c661SEtienne Carriere {
3483a20c661SEtienne Carriere 	*m = (struct mutex_pm_aware)MUTEX_PM_AWARE_INITIALIZER;
3493a20c661SEtienne Carriere }
3503a20c661SEtienne Carriere 
mutex_pm_aware_destroy(struct mutex_pm_aware * m)3513a20c661SEtienne Carriere void mutex_pm_aware_destroy(struct mutex_pm_aware *m)
3523a20c661SEtienne Carriere {
3533a20c661SEtienne Carriere 	mutex_destroy(&m->mutex);
3543a20c661SEtienne Carriere }
3553a20c661SEtienne Carriere 
mutex_pm_aware_lock(struct mutex_pm_aware * m)3563a20c661SEtienne Carriere void mutex_pm_aware_lock(struct mutex_pm_aware *m)
3573a20c661SEtienne Carriere {
3583a20c661SEtienne Carriere 	if (thread_get_id_may_fail() == THREAD_ID_INVALID) {
3593a20c661SEtienne Carriere 		if (!cpu_spin_trylock(&m->lock) || m->mutex.state)
3603a20c661SEtienne Carriere 			panic();
3613a20c661SEtienne Carriere 	} else {
3623a20c661SEtienne Carriere 		mutex_lock(&m->mutex);
3633a20c661SEtienne Carriere 		if (!thread_spin_trylock(&m->lock))
3643a20c661SEtienne Carriere 			panic();
3653a20c661SEtienne Carriere 	}
3663a20c661SEtienne Carriere }
3673a20c661SEtienne Carriere 
mutex_pm_aware_unlock(struct mutex_pm_aware * m)3683a20c661SEtienne Carriere void mutex_pm_aware_unlock(struct mutex_pm_aware *m)
3693a20c661SEtienne Carriere {
3703a20c661SEtienne Carriere 	if (thread_get_id_may_fail() == THREAD_ID_INVALID) {
3713a20c661SEtienne Carriere 		assert(!m->mutex.state);
3723a20c661SEtienne Carriere 		cpu_spin_unlock(&m->lock);
3733a20c661SEtienne Carriere 	} else {
3743a20c661SEtienne Carriere 		thread_spin_unlock(&m->lock);
3753a20c661SEtienne Carriere 		mutex_unlock(&m->mutex);
3763a20c661SEtienne Carriere 	}
3773a20c661SEtienne Carriere }
3783a20c661SEtienne Carriere 
condvar_init(struct condvar * cv)37951f49692SMarouene Boubakri void condvar_init(struct condvar *cv)
38051f49692SMarouene Boubakri {
38151f49692SMarouene Boubakri 	*cv = (struct condvar)CONDVAR_INITIALIZER;
38251f49692SMarouene Boubakri }
38351f49692SMarouene Boubakri 
condvar_destroy(struct condvar * cv)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 
cv_signal(struct condvar * cv,bool only_one,const char * fname,int lineno)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
condvar_signal_debug(struct condvar * cv,const char * fname,int lineno)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 
condvar_broadcast_debug(struct condvar * cv,const char * fname,int lineno)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
condvar_signal(struct condvar * cv)41951f49692SMarouene Boubakri void condvar_signal(struct condvar *cv)
42051f49692SMarouene Boubakri {
42151f49692SMarouene Boubakri 	cv_signal(cv, true /* only one */, NULL, -1);
42251f49692SMarouene Boubakri }
42351f49692SMarouene Boubakri 
condvar_broadcast(struct condvar * cv)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 
__condvar_wait_timeout(struct condvar * cv,struct mutex * m,uint32_t timeout_ms,const char * fname,int lineno)430*ea413ca5SGavin Liu static TEE_Result __condvar_wait_timeout(struct condvar *cv, struct mutex *m,
431*ea413ca5SGavin Liu 					 uint32_t timeout_ms, const char *fname,
432*ea413ca5SGavin Liu 					 int lineno)
43351f49692SMarouene Boubakri {
434*ea413ca5SGavin Liu 	TEE_Result res = TEE_SUCCESS;
435*ea413ca5SGavin Liu 	uint32_t old_itr_status = 0;
436*ea413ca5SGavin Liu 	struct wait_queue_elem wqe = { };
437*ea413ca5SGavin Liu 	short old_state = 0;
438*ea413ca5SGavin Liu 	short new_state = 0;
43951f49692SMarouene Boubakri 
44051f49692SMarouene Boubakri 	mutex_unlock_check(m);
44151f49692SMarouene Boubakri 
44251f49692SMarouene Boubakri 	/* Link this condvar to this mutex until reinitialized */
44351f49692SMarouene Boubakri 	old_itr_status = cpu_spin_lock_xsave(&cv->spin_lock);
44451f49692SMarouene Boubakri 	if (cv->m && cv->m != m)
44551f49692SMarouene Boubakri 		panic("invalid mutex");
44651f49692SMarouene Boubakri 
44751f49692SMarouene Boubakri 	cv->m = m;
44851f49692SMarouene Boubakri 	cpu_spin_unlock(&cv->spin_lock);
44951f49692SMarouene Boubakri 
45051f49692SMarouene Boubakri 	cpu_spin_lock(&m->spin_lock);
45151f49692SMarouene Boubakri 
45251f49692SMarouene Boubakri 	if (!m->state)
45351f49692SMarouene Boubakri 		panic();
45451f49692SMarouene Boubakri 	old_state = m->state;
45551f49692SMarouene Boubakri 	/* Add to mutex wait queue as a condvar waiter */
45651f49692SMarouene Boubakri 	wq_wait_init_condvar(&m->wq, &wqe, cv, m->state > 0);
45751f49692SMarouene Boubakri 
45851f49692SMarouene Boubakri 	if (m->state > 1) {
45951f49692SMarouene Boubakri 		/* Multiple read locks, remove one */
46051f49692SMarouene Boubakri 		m->state--;
46151f49692SMarouene Boubakri 	} else {
46251f49692SMarouene Boubakri 		/* Only one lock (read or write), unlock the mutex */
46351f49692SMarouene Boubakri 		m->state = 0;
46451f49692SMarouene Boubakri 	}
46551f49692SMarouene Boubakri 	new_state = m->state;
46651f49692SMarouene Boubakri 
46751f49692SMarouene Boubakri 	cpu_spin_unlock_xrestore(&m->spin_lock, old_itr_status);
46851f49692SMarouene Boubakri 
46951f49692SMarouene Boubakri 	/* Wake eventual waiters if the mutex was unlocked */
47051f49692SMarouene Boubakri 	if (!new_state)
47151f49692SMarouene Boubakri 		wq_wake_next(&m->wq, m, fname, lineno);
47251f49692SMarouene Boubakri 
473*ea413ca5SGavin Liu 	res = wq_wait_final(&m->wq, &wqe, timeout_ms, m, fname, lineno);
47451f49692SMarouene Boubakri 
47551f49692SMarouene Boubakri 	if (old_state > 0)
47651f49692SMarouene Boubakri 		mutex_read_lock(m);
47751f49692SMarouene Boubakri 	else
47851f49692SMarouene Boubakri 		mutex_lock(m);
479*ea413ca5SGavin Liu 
480*ea413ca5SGavin Liu 	return res;
48151f49692SMarouene Boubakri }
48251f49692SMarouene Boubakri 
48351f49692SMarouene Boubakri #ifdef CFG_MUTEX_DEBUG
condvar_wait_debug(struct condvar * cv,struct mutex * m,const char * fname,int lineno)48451f49692SMarouene Boubakri void condvar_wait_debug(struct condvar *cv, struct mutex *m,
48551f49692SMarouene Boubakri 			const char *fname, int lineno)
48651f49692SMarouene Boubakri {
487*ea413ca5SGavin Liu 	__condvar_wait_timeout(cv, m, 0, fname, lineno);
488*ea413ca5SGavin Liu }
489*ea413ca5SGavin Liu 
condvar_wait_timeout_debug(struct condvar * cv,struct mutex * m,uint32_t timeout_ms,const char * fname,int lineno)490*ea413ca5SGavin Liu TEE_Result condvar_wait_timeout_debug(struct condvar *cv, struct mutex *m,
491*ea413ca5SGavin Liu 				      uint32_t timeout_ms, const char *fname,
492*ea413ca5SGavin Liu 				      int lineno)
493*ea413ca5SGavin Liu {
494*ea413ca5SGavin Liu 	return __condvar_wait_timeout(cv, m, timeout_ms, fname, lineno);
49551f49692SMarouene Boubakri }
49651f49692SMarouene Boubakri #else
condvar_wait(struct condvar * cv,struct mutex * m)49751f49692SMarouene Boubakri void condvar_wait(struct condvar *cv, struct mutex *m)
49851f49692SMarouene Boubakri {
499*ea413ca5SGavin Liu 	__condvar_wait_timeout(cv, m, 0, NULL, -1);
500*ea413ca5SGavin Liu }
501*ea413ca5SGavin Liu 
condvar_wait_timeout(struct condvar * cv,struct mutex * m,uint32_t timeout_ms)502*ea413ca5SGavin Liu TEE_Result condvar_wait_timeout(struct condvar *cv, struct mutex *m,
503*ea413ca5SGavin Liu 				uint32_t timeout_ms)
504*ea413ca5SGavin Liu {
505*ea413ca5SGavin Liu 	return __condvar_wait_timeout(cv, m, timeout_ms, NULL, -1);
50651f49692SMarouene Boubakri }
50751f49692SMarouene Boubakri #endif
508