xref: /optee_os/core/kernel/wait_queue.c (revision 450f8ada92af73d9909324da8e1c26f40233ad64)
151f49692SMarouene Boubakri // SPDX-License-Identifier: BSD-2-Clause
251f49692SMarouene Boubakri /*
32828809eSJens Wiklander  * Copyright (c) 2015-2021, Linaro Limited
451f49692SMarouene Boubakri  */
52828809eSJens Wiklander 
651f49692SMarouene Boubakri #include <compiler.h>
72828809eSJens Wiklander #include <kernel/notif.h>
851f49692SMarouene Boubakri #include <kernel/spinlock.h>
951f49692SMarouene Boubakri #include <kernel/thread.h>
1051f49692SMarouene Boubakri #include <kernel/wait_queue.h>
1151f49692SMarouene Boubakri #include <tee_api_defines.h>
1251f49692SMarouene Boubakri #include <trace.h>
1351f49692SMarouene Boubakri #include <types_ext.h>
1451f49692SMarouene Boubakri 
1551f49692SMarouene Boubakri static unsigned wq_spin_lock;
1651f49692SMarouene Boubakri 
1751f49692SMarouene Boubakri 
wq_init(struct wait_queue * wq)1851f49692SMarouene Boubakri void wq_init(struct wait_queue *wq)
1951f49692SMarouene Boubakri {
2051f49692SMarouene Boubakri 	*wq = (struct wait_queue)WAIT_QUEUE_INITIALIZER;
2151f49692SMarouene Boubakri }
2251f49692SMarouene Boubakri 
do_notif(bool wait,int id,uint32_t timeout_ms,const char * cmd_str __maybe_unused,const void * sync_obj __maybe_unused,const char * fname,int lineno __maybe_unused)23*450f8adaSGavin Liu static TEE_Result do_notif(bool wait, int id, uint32_t timeout_ms,
242828809eSJens Wiklander 			   const char *cmd_str __maybe_unused,
252828809eSJens Wiklander 			   const void *sync_obj __maybe_unused,
2651f49692SMarouene Boubakri 			   const char *fname, int lineno __maybe_unused)
2751f49692SMarouene Boubakri {
282828809eSJens Wiklander 	TEE_Result res = TEE_SUCCESS;
2951f49692SMarouene Boubakri 
3051f49692SMarouene Boubakri 	if (fname)
312828809eSJens Wiklander 		DMSG("%s thread %d %p %s:%d", cmd_str, id,
3251f49692SMarouene Boubakri 		     sync_obj, fname, lineno);
3351f49692SMarouene Boubakri 	else
342828809eSJens Wiklander 		DMSG("%s thread %d %p", cmd_str, id, sync_obj);
3551f49692SMarouene Boubakri 
36*450f8adaSGavin Liu 	if (wait)
37*450f8adaSGavin Liu 		res = notif_wait_timeout(id + NOTIF_SYNC_VALUE_BASE,
38*450f8adaSGavin Liu 					 timeout_ms);
39*450f8adaSGavin Liu 	else
40*450f8adaSGavin Liu 		res = notif_send_sync(id + NOTIF_SYNC_VALUE_BASE);
412828809eSJens Wiklander 	if (res)
422828809eSJens Wiklander 		DMSG("%s thread %d res %#"PRIx32, cmd_str, id, res);
43*450f8adaSGavin Liu 
44*450f8adaSGavin Liu 	return res;
4551f49692SMarouene Boubakri }
4651f49692SMarouene Boubakri 
slist_add_tail(struct wait_queue * wq,struct wait_queue_elem * wqe)4751f49692SMarouene Boubakri static void slist_add_tail(struct wait_queue *wq, struct wait_queue_elem *wqe)
4851f49692SMarouene Boubakri {
4951f49692SMarouene Boubakri 	struct wait_queue_elem *wqe_iter;
5051f49692SMarouene Boubakri 
5151f49692SMarouene Boubakri 	/* Add elem to end of wait queue */
5251f49692SMarouene Boubakri 	wqe_iter = SLIST_FIRST(wq);
5351f49692SMarouene Boubakri 	if (wqe_iter) {
5451f49692SMarouene Boubakri 		while (SLIST_NEXT(wqe_iter, link))
5551f49692SMarouene Boubakri 			wqe_iter = SLIST_NEXT(wqe_iter, link);
5651f49692SMarouene Boubakri 		SLIST_INSERT_AFTER(wqe_iter, wqe, link);
5751f49692SMarouene Boubakri 	} else
5851f49692SMarouene Boubakri 		SLIST_INSERT_HEAD(wq, wqe, link);
5951f49692SMarouene Boubakri }
6051f49692SMarouene Boubakri 
wq_wait_init_condvar(struct wait_queue * wq,struct wait_queue_elem * wqe,struct condvar * cv,bool wait_read)6151f49692SMarouene Boubakri void wq_wait_init_condvar(struct wait_queue *wq, struct wait_queue_elem *wqe,
6251f49692SMarouene Boubakri 		struct condvar *cv, bool wait_read)
6351f49692SMarouene Boubakri {
6451f49692SMarouene Boubakri 	uint32_t old_itr_status;
6551f49692SMarouene Boubakri 
6651f49692SMarouene Boubakri 	wqe->handle = thread_get_id();
6751f49692SMarouene Boubakri 	wqe->done = false;
6851f49692SMarouene Boubakri 	wqe->wait_read = wait_read;
6951f49692SMarouene Boubakri 	wqe->cv = cv;
7051f49692SMarouene Boubakri 
7151f49692SMarouene Boubakri 	old_itr_status = cpu_spin_lock_xsave(&wq_spin_lock);
7251f49692SMarouene Boubakri 
7351f49692SMarouene Boubakri 	slist_add_tail(wq, wqe);
7451f49692SMarouene Boubakri 
7551f49692SMarouene Boubakri 	cpu_spin_unlock_xrestore(&wq_spin_lock, old_itr_status);
7651f49692SMarouene Boubakri }
7751f49692SMarouene Boubakri 
wq_wait_final_helper(struct wait_queue * wq,struct wait_queue_elem * wqe,uint32_t timeout_ms,const void * sync_obj,const char * fname,int lineno)78*450f8adaSGavin Liu static TEE_Result wq_wait_final_helper(struct wait_queue *wq,
79*450f8adaSGavin Liu 				       struct wait_queue_elem *wqe,
80*450f8adaSGavin Liu 				       uint32_t timeout_ms,
81*450f8adaSGavin Liu 				       const void *sync_obj, const char *fname,
82*450f8adaSGavin Liu 				       int lineno)
8351f49692SMarouene Boubakri {
84*450f8adaSGavin Liu 	uint32_t old_itr_status = 0;
8551f49692SMarouene Boubakri 
86*450f8adaSGavin Liu 	do_notif(true, wqe->handle, timeout_ms, "sleep", sync_obj, fname,
87*450f8adaSGavin Liu 		 lineno);
8851f49692SMarouene Boubakri 
8951f49692SMarouene Boubakri 	old_itr_status = cpu_spin_lock_xsave(&wq_spin_lock);
9051f49692SMarouene Boubakri 	SLIST_REMOVE(wq, wqe, wait_queue_elem, link);
9151f49692SMarouene Boubakri 	cpu_spin_unlock_xrestore(&wq_spin_lock, old_itr_status);
92*450f8adaSGavin Liu 
93*450f8adaSGavin Liu 	if (wqe->done)
94*450f8adaSGavin Liu 		return TEE_SUCCESS;
95*450f8adaSGavin Liu 	else
96*450f8adaSGavin Liu 		return TEE_ERROR_TIMEOUT;
97*450f8adaSGavin Liu }
98*450f8adaSGavin Liu 
wq_wait_final(struct wait_queue * wq,struct wait_queue_elem * wqe,uint32_t timeout_ms,const void * sync_obj,const char * fname,int lineno)99*450f8adaSGavin Liu TEE_Result wq_wait_final(struct wait_queue *wq, struct wait_queue_elem *wqe,
100*450f8adaSGavin Liu 			 uint32_t timeout_ms, const void *sync_obj,
101*450f8adaSGavin Liu 			 const char *fname, int lineno)
102*450f8adaSGavin Liu {
103*450f8adaSGavin Liu 	return wq_wait_final_helper(wq, wqe, timeout_ms, sync_obj, fname,
104*450f8adaSGavin Liu 				    lineno);
10551f49692SMarouene Boubakri }
10651f49692SMarouene Boubakri 
wq_wake_next(struct wait_queue * wq,const void * sync_obj,const char * fname,int lineno)10751f49692SMarouene Boubakri void wq_wake_next(struct wait_queue *wq, const void *sync_obj,
10851f49692SMarouene Boubakri 			const char *fname, int lineno)
10951f49692SMarouene Boubakri {
11051f49692SMarouene Boubakri 	uint32_t old_itr_status;
11151f49692SMarouene Boubakri 	struct wait_queue_elem *wqe;
11251f49692SMarouene Boubakri 	int handle = -1;
11351f49692SMarouene Boubakri 	bool do_wakeup = false;
11451f49692SMarouene Boubakri 	bool wake_type_assigned = false;
11551f49692SMarouene Boubakri 	bool wake_read = false; /* avoid gcc warning */
11651f49692SMarouene Boubakri 
11751f49692SMarouene Boubakri 	/*
11851f49692SMarouene Boubakri 	 * If next type is wait_read wakeup all wqe with wait_read true.
11951f49692SMarouene Boubakri 	 * If next type isn't wait_read wakeup only the first wqe which isn't
12051f49692SMarouene Boubakri 	 * done.
12151f49692SMarouene Boubakri 	 */
12251f49692SMarouene Boubakri 
12351f49692SMarouene Boubakri 	while (true) {
12451f49692SMarouene Boubakri 		old_itr_status = cpu_spin_lock_xsave(&wq_spin_lock);
12551f49692SMarouene Boubakri 
12651f49692SMarouene Boubakri 		SLIST_FOREACH(wqe, wq, link) {
12751f49692SMarouene Boubakri 			if (wqe->cv)
12851f49692SMarouene Boubakri 				continue;
12951f49692SMarouene Boubakri 			if (wqe->done)
13051f49692SMarouene Boubakri 				continue;
13151f49692SMarouene Boubakri 			if (!wake_type_assigned) {
13251f49692SMarouene Boubakri 				wake_read = wqe->wait_read;
13351f49692SMarouene Boubakri 				wake_type_assigned = true;
13451f49692SMarouene Boubakri 			}
13551f49692SMarouene Boubakri 
13651f49692SMarouene Boubakri 			if (wqe->wait_read != wake_read)
13751f49692SMarouene Boubakri 				continue;
13851f49692SMarouene Boubakri 
13951f49692SMarouene Boubakri 			wqe->done = true;
14051f49692SMarouene Boubakri 			handle = wqe->handle;
14151f49692SMarouene Boubakri 			do_wakeup = true;
14251f49692SMarouene Boubakri 			break;
14351f49692SMarouene Boubakri 		}
14451f49692SMarouene Boubakri 
14551f49692SMarouene Boubakri 		cpu_spin_unlock_xrestore(&wq_spin_lock, old_itr_status);
14651f49692SMarouene Boubakri 
14751f49692SMarouene Boubakri 		if (do_wakeup)
148*450f8adaSGavin Liu 			do_notif(false, handle, 0,
1492828809eSJens Wiklander 				 "wake ", sync_obj, fname, lineno);
15051f49692SMarouene Boubakri 
15151f49692SMarouene Boubakri 		if (!do_wakeup || !wake_read)
15251f49692SMarouene Boubakri 			break;
15351f49692SMarouene Boubakri 		do_wakeup = false;
15451f49692SMarouene Boubakri 	}
15551f49692SMarouene Boubakri }
15651f49692SMarouene Boubakri 
wq_promote_condvar(struct wait_queue * wq,struct condvar * cv,bool only_one,const void * sync_obj __unused,const char * fname,int lineno __maybe_unused)15751f49692SMarouene Boubakri void wq_promote_condvar(struct wait_queue *wq, struct condvar *cv,
15851f49692SMarouene Boubakri 			bool only_one, const void *sync_obj __unused,
15951f49692SMarouene Boubakri 			const char *fname, int lineno __maybe_unused)
16051f49692SMarouene Boubakri {
16151f49692SMarouene Boubakri 	uint32_t old_itr_status;
16251f49692SMarouene Boubakri 	struct wait_queue_elem *wqe;
16351f49692SMarouene Boubakri 
16451f49692SMarouene Boubakri 	if (!cv)
16551f49692SMarouene Boubakri 		return;
16651f49692SMarouene Boubakri 
16751f49692SMarouene Boubakri 	old_itr_status = cpu_spin_lock_xsave(&wq_spin_lock);
16851f49692SMarouene Boubakri 
16951f49692SMarouene Boubakri 	/*
17051f49692SMarouene Boubakri 	 * Find condvar waiter(s) and promote each to an active waiter.
17151f49692SMarouene Boubakri 	 * This is a bit unfair to eventual other active waiters as a
17251f49692SMarouene Boubakri 	 * condvar waiter is added to the queue when waiting for the
17351f49692SMarouene Boubakri 	 * condvar.
17451f49692SMarouene Boubakri 	 */
17551f49692SMarouene Boubakri 	SLIST_FOREACH(wqe, wq, link) {
17651f49692SMarouene Boubakri 		if (wqe->cv == cv) {
17751f49692SMarouene Boubakri 			if (fname)
17851f49692SMarouene Boubakri 				FMSG("promote thread %u %p %s:%d",
17951f49692SMarouene Boubakri 				     wqe->handle, (void *)cv->m, fname, lineno);
18051f49692SMarouene Boubakri 			else
18151f49692SMarouene Boubakri 				FMSG("promote thread %u %p",
18251f49692SMarouene Boubakri 				     wqe->handle, (void *)cv->m);
18351f49692SMarouene Boubakri 
18451f49692SMarouene Boubakri 			wqe->cv = NULL;
18551f49692SMarouene Boubakri 			if (only_one)
18651f49692SMarouene Boubakri 				break;
18751f49692SMarouene Boubakri 		}
18851f49692SMarouene Boubakri 	}
18951f49692SMarouene Boubakri 
19051f49692SMarouene Boubakri 	cpu_spin_unlock_xrestore(&wq_spin_lock, old_itr_status);
19151f49692SMarouene Boubakri }
19251f49692SMarouene Boubakri 
wq_have_condvar(struct wait_queue * wq,struct condvar * cv)19351f49692SMarouene Boubakri bool wq_have_condvar(struct wait_queue *wq, struct condvar *cv)
19451f49692SMarouene Boubakri {
19551f49692SMarouene Boubakri 	uint32_t old_itr_status;
19651f49692SMarouene Boubakri 	struct wait_queue_elem *wqe;
19751f49692SMarouene Boubakri 	bool rc = false;
19851f49692SMarouene Boubakri 
19951f49692SMarouene Boubakri 	old_itr_status = cpu_spin_lock_xsave(&wq_spin_lock);
20051f49692SMarouene Boubakri 
20151f49692SMarouene Boubakri 	SLIST_FOREACH(wqe, wq, link) {
20251f49692SMarouene Boubakri 		if (wqe->cv == cv) {
20351f49692SMarouene Boubakri 			rc = true;
20451f49692SMarouene Boubakri 			break;
20551f49692SMarouene Boubakri 		}
20651f49692SMarouene Boubakri 	}
20751f49692SMarouene Boubakri 
20851f49692SMarouene Boubakri 	cpu_spin_unlock_xrestore(&wq_spin_lock, old_itr_status);
20951f49692SMarouene Boubakri 
21051f49692SMarouene Boubakri 	return rc;
21151f49692SMarouene Boubakri }
21251f49692SMarouene Boubakri 
wq_is_empty(struct wait_queue * wq)21351f49692SMarouene Boubakri bool wq_is_empty(struct wait_queue *wq)
21451f49692SMarouene Boubakri {
21551f49692SMarouene Boubakri 	uint32_t old_itr_status;
21651f49692SMarouene Boubakri 	bool ret;
21751f49692SMarouene Boubakri 
21851f49692SMarouene Boubakri 	old_itr_status = cpu_spin_lock_xsave(&wq_spin_lock);
21951f49692SMarouene Boubakri 
22051f49692SMarouene Boubakri 	ret = SLIST_EMPTY(wq);
22151f49692SMarouene Boubakri 
22251f49692SMarouene Boubakri 	cpu_spin_unlock_xrestore(&wq_spin_lock, old_itr_status);
22351f49692SMarouene Boubakri 
22451f49692SMarouene Boubakri 	return ret;
22551f49692SMarouene Boubakri }
226