xref: /optee_os/core/kernel/wait_queue.c (revision 51f49692723419b40830e2652b9773b45c9b97d4)
1*51f49692SMarouene Boubakri // SPDX-License-Identifier: BSD-2-Clause
2*51f49692SMarouene Boubakri /*
3*51f49692SMarouene Boubakri  * Copyright (c) 2015-2016, Linaro Limited
4*51f49692SMarouene Boubakri  */
5*51f49692SMarouene Boubakri #include <compiler.h>
6*51f49692SMarouene Boubakri #include <kernel/spinlock.h>
7*51f49692SMarouene Boubakri #include <kernel/thread.h>
8*51f49692SMarouene Boubakri #include <kernel/wait_queue.h>
9*51f49692SMarouene Boubakri #include <optee_rpc_cmd.h>
10*51f49692SMarouene Boubakri #include <string.h>
11*51f49692SMarouene Boubakri #include <tee_api_defines.h>
12*51f49692SMarouene Boubakri #include <trace.h>
13*51f49692SMarouene Boubakri #include <types_ext.h>
14*51f49692SMarouene Boubakri 
15*51f49692SMarouene Boubakri static unsigned wq_spin_lock;
16*51f49692SMarouene Boubakri 
17*51f49692SMarouene Boubakri 
18*51f49692SMarouene Boubakri void wq_init(struct wait_queue *wq)
19*51f49692SMarouene Boubakri {
20*51f49692SMarouene Boubakri 	*wq = (struct wait_queue)WAIT_QUEUE_INITIALIZER;
21*51f49692SMarouene Boubakri }
22*51f49692SMarouene Boubakri 
23*51f49692SMarouene Boubakri /*
24*51f49692SMarouene Boubakri  * Note: this function is weak just to make it possible to exclude it from
25*51f49692SMarouene Boubakri  * the unpaged area.
26*51f49692SMarouene Boubakri  */
27*51f49692SMarouene Boubakri void __weak __wq_rpc(uint32_t func, int id, const void *sync_obj __maybe_unused,
28*51f49692SMarouene Boubakri 		     const char *fname, int lineno __maybe_unused)
29*51f49692SMarouene Boubakri {
30*51f49692SMarouene Boubakri 	uint32_t ret;
31*51f49692SMarouene Boubakri 	const char *cmd_str __maybe_unused =
32*51f49692SMarouene Boubakri 	     func == OPTEE_RPC_WAIT_QUEUE_SLEEP ? "sleep" : "wake ";
33*51f49692SMarouene Boubakri 
34*51f49692SMarouene Boubakri 	if (fname)
35*51f49692SMarouene Boubakri 		DMSG("%s thread %u %p %s:%d", cmd_str, id,
36*51f49692SMarouene Boubakri 		     sync_obj, fname, lineno);
37*51f49692SMarouene Boubakri 	else
38*51f49692SMarouene Boubakri 		DMSG("%s thread %u %p", cmd_str, id, sync_obj);
39*51f49692SMarouene Boubakri 
40*51f49692SMarouene Boubakri 	struct thread_param params = THREAD_PARAM_VALUE(IN, func, id, 0);
41*51f49692SMarouene Boubakri 
42*51f49692SMarouene Boubakri 	ret = thread_rpc_cmd(OPTEE_RPC_CMD_WAIT_QUEUE, 1, &params);
43*51f49692SMarouene Boubakri 	if (ret != TEE_SUCCESS)
44*51f49692SMarouene Boubakri 		DMSG("%s thread %u ret 0x%x", cmd_str, id, ret);
45*51f49692SMarouene Boubakri }
46*51f49692SMarouene Boubakri 
47*51f49692SMarouene Boubakri static void slist_add_tail(struct wait_queue *wq, struct wait_queue_elem *wqe)
48*51f49692SMarouene Boubakri {
49*51f49692SMarouene Boubakri 	struct wait_queue_elem *wqe_iter;
50*51f49692SMarouene Boubakri 
51*51f49692SMarouene Boubakri 	/* Add elem to end of wait queue */
52*51f49692SMarouene Boubakri 	wqe_iter = SLIST_FIRST(wq);
53*51f49692SMarouene Boubakri 	if (wqe_iter) {
54*51f49692SMarouene Boubakri 		while (SLIST_NEXT(wqe_iter, link))
55*51f49692SMarouene Boubakri 			wqe_iter = SLIST_NEXT(wqe_iter, link);
56*51f49692SMarouene Boubakri 		SLIST_INSERT_AFTER(wqe_iter, wqe, link);
57*51f49692SMarouene Boubakri 	} else
58*51f49692SMarouene Boubakri 		SLIST_INSERT_HEAD(wq, wqe, link);
59*51f49692SMarouene Boubakri }
60*51f49692SMarouene Boubakri 
61*51f49692SMarouene Boubakri void wq_wait_init_condvar(struct wait_queue *wq, struct wait_queue_elem *wqe,
62*51f49692SMarouene Boubakri 		struct condvar *cv, bool wait_read)
63*51f49692SMarouene Boubakri {
64*51f49692SMarouene Boubakri 	uint32_t old_itr_status;
65*51f49692SMarouene Boubakri 
66*51f49692SMarouene Boubakri 	wqe->handle = thread_get_id();
67*51f49692SMarouene Boubakri 	wqe->done = false;
68*51f49692SMarouene Boubakri 	wqe->wait_read = wait_read;
69*51f49692SMarouene Boubakri 	wqe->cv = cv;
70*51f49692SMarouene Boubakri 
71*51f49692SMarouene Boubakri 	old_itr_status = cpu_spin_lock_xsave(&wq_spin_lock);
72*51f49692SMarouene Boubakri 
73*51f49692SMarouene Boubakri 	slist_add_tail(wq, wqe);
74*51f49692SMarouene Boubakri 
75*51f49692SMarouene Boubakri 	cpu_spin_unlock_xrestore(&wq_spin_lock, old_itr_status);
76*51f49692SMarouene Boubakri }
77*51f49692SMarouene Boubakri 
78*51f49692SMarouene Boubakri void wq_wait_final(struct wait_queue *wq, struct wait_queue_elem *wqe,
79*51f49692SMarouene Boubakri 		   const void *sync_obj, const char *fname, int lineno)
80*51f49692SMarouene Boubakri {
81*51f49692SMarouene Boubakri 	uint32_t old_itr_status;
82*51f49692SMarouene Boubakri 	unsigned done;
83*51f49692SMarouene Boubakri 
84*51f49692SMarouene Boubakri 	do {
85*51f49692SMarouene Boubakri 		__wq_rpc(OPTEE_RPC_WAIT_QUEUE_SLEEP, wqe->handle,
86*51f49692SMarouene Boubakri 			 sync_obj, fname, lineno);
87*51f49692SMarouene Boubakri 
88*51f49692SMarouene Boubakri 		old_itr_status = cpu_spin_lock_xsave(&wq_spin_lock);
89*51f49692SMarouene Boubakri 
90*51f49692SMarouene Boubakri 		done = wqe->done;
91*51f49692SMarouene Boubakri 		if (done)
92*51f49692SMarouene Boubakri 			SLIST_REMOVE(wq, wqe, wait_queue_elem, link);
93*51f49692SMarouene Boubakri 
94*51f49692SMarouene Boubakri 		cpu_spin_unlock_xrestore(&wq_spin_lock, old_itr_status);
95*51f49692SMarouene Boubakri 	} while (!done);
96*51f49692SMarouene Boubakri }
97*51f49692SMarouene Boubakri 
98*51f49692SMarouene Boubakri void wq_wake_next(struct wait_queue *wq, const void *sync_obj,
99*51f49692SMarouene Boubakri 			const char *fname, int lineno)
100*51f49692SMarouene Boubakri {
101*51f49692SMarouene Boubakri 	uint32_t old_itr_status;
102*51f49692SMarouene Boubakri 	struct wait_queue_elem *wqe;
103*51f49692SMarouene Boubakri 	int handle = -1;
104*51f49692SMarouene Boubakri 	bool do_wakeup = false;
105*51f49692SMarouene Boubakri 	bool wake_type_assigned = false;
106*51f49692SMarouene Boubakri 	bool wake_read = false; /* avoid gcc warning */
107*51f49692SMarouene Boubakri 
108*51f49692SMarouene Boubakri 	/*
109*51f49692SMarouene Boubakri 	 * If next type is wait_read wakeup all wqe with wait_read true.
110*51f49692SMarouene Boubakri 	 * If next type isn't wait_read wakeup only the first wqe which isn't
111*51f49692SMarouene Boubakri 	 * done.
112*51f49692SMarouene Boubakri 	 */
113*51f49692SMarouene Boubakri 
114*51f49692SMarouene Boubakri 	while (true) {
115*51f49692SMarouene Boubakri 		old_itr_status = cpu_spin_lock_xsave(&wq_spin_lock);
116*51f49692SMarouene Boubakri 
117*51f49692SMarouene Boubakri 		SLIST_FOREACH(wqe, wq, link) {
118*51f49692SMarouene Boubakri 			if (wqe->cv)
119*51f49692SMarouene Boubakri 				continue;
120*51f49692SMarouene Boubakri 			if (wqe->done)
121*51f49692SMarouene Boubakri 				continue;
122*51f49692SMarouene Boubakri 			if (!wake_type_assigned) {
123*51f49692SMarouene Boubakri 				wake_read = wqe->wait_read;
124*51f49692SMarouene Boubakri 				wake_type_assigned = true;
125*51f49692SMarouene Boubakri 			}
126*51f49692SMarouene Boubakri 
127*51f49692SMarouene Boubakri 			if (wqe->wait_read != wake_read)
128*51f49692SMarouene Boubakri 				continue;
129*51f49692SMarouene Boubakri 
130*51f49692SMarouene Boubakri 			wqe->done = true;
131*51f49692SMarouene Boubakri 			handle = wqe->handle;
132*51f49692SMarouene Boubakri 			do_wakeup = true;
133*51f49692SMarouene Boubakri 			break;
134*51f49692SMarouene Boubakri 		}
135*51f49692SMarouene Boubakri 
136*51f49692SMarouene Boubakri 		cpu_spin_unlock_xrestore(&wq_spin_lock, old_itr_status);
137*51f49692SMarouene Boubakri 
138*51f49692SMarouene Boubakri 		if (do_wakeup)
139*51f49692SMarouene Boubakri 			__wq_rpc(OPTEE_RPC_WAIT_QUEUE_WAKEUP, handle,
140*51f49692SMarouene Boubakri 				 sync_obj, fname, lineno);
141*51f49692SMarouene Boubakri 
142*51f49692SMarouene Boubakri 		if (!do_wakeup || !wake_read)
143*51f49692SMarouene Boubakri 			break;
144*51f49692SMarouene Boubakri 		do_wakeup = false;
145*51f49692SMarouene Boubakri 	}
146*51f49692SMarouene Boubakri }
147*51f49692SMarouene Boubakri 
148*51f49692SMarouene Boubakri void wq_promote_condvar(struct wait_queue *wq, struct condvar *cv,
149*51f49692SMarouene Boubakri 			bool only_one, const void *sync_obj __unused,
150*51f49692SMarouene Boubakri 			const char *fname, int lineno __maybe_unused)
151*51f49692SMarouene Boubakri {
152*51f49692SMarouene Boubakri 	uint32_t old_itr_status;
153*51f49692SMarouene Boubakri 	struct wait_queue_elem *wqe;
154*51f49692SMarouene Boubakri 
155*51f49692SMarouene Boubakri 	if (!cv)
156*51f49692SMarouene Boubakri 		return;
157*51f49692SMarouene Boubakri 
158*51f49692SMarouene Boubakri 	old_itr_status = cpu_spin_lock_xsave(&wq_spin_lock);
159*51f49692SMarouene Boubakri 
160*51f49692SMarouene Boubakri 	/*
161*51f49692SMarouene Boubakri 	 * Find condvar waiter(s) and promote each to an active waiter.
162*51f49692SMarouene Boubakri 	 * This is a bit unfair to eventual other active waiters as a
163*51f49692SMarouene Boubakri 	 * condvar waiter is added to the queue when waiting for the
164*51f49692SMarouene Boubakri 	 * condvar.
165*51f49692SMarouene Boubakri 	 */
166*51f49692SMarouene Boubakri 	SLIST_FOREACH(wqe, wq, link) {
167*51f49692SMarouene Boubakri 		if (wqe->cv == cv) {
168*51f49692SMarouene Boubakri 			if (fname)
169*51f49692SMarouene Boubakri 				FMSG("promote thread %u %p %s:%d",
170*51f49692SMarouene Boubakri 				     wqe->handle, (void *)cv->m, fname, lineno);
171*51f49692SMarouene Boubakri 			else
172*51f49692SMarouene Boubakri 				FMSG("promote thread %u %p",
173*51f49692SMarouene Boubakri 				     wqe->handle, (void *)cv->m);
174*51f49692SMarouene Boubakri 
175*51f49692SMarouene Boubakri 			wqe->cv = NULL;
176*51f49692SMarouene Boubakri 			if (only_one)
177*51f49692SMarouene Boubakri 				break;
178*51f49692SMarouene Boubakri 		}
179*51f49692SMarouene Boubakri 	}
180*51f49692SMarouene Boubakri 
181*51f49692SMarouene Boubakri 	cpu_spin_unlock_xrestore(&wq_spin_lock, old_itr_status);
182*51f49692SMarouene Boubakri }
183*51f49692SMarouene Boubakri 
184*51f49692SMarouene Boubakri bool wq_have_condvar(struct wait_queue *wq, struct condvar *cv)
185*51f49692SMarouene Boubakri {
186*51f49692SMarouene Boubakri 	uint32_t old_itr_status;
187*51f49692SMarouene Boubakri 	struct wait_queue_elem *wqe;
188*51f49692SMarouene Boubakri 	bool rc = false;
189*51f49692SMarouene Boubakri 
190*51f49692SMarouene Boubakri 	old_itr_status = cpu_spin_lock_xsave(&wq_spin_lock);
191*51f49692SMarouene Boubakri 
192*51f49692SMarouene Boubakri 	SLIST_FOREACH(wqe, wq, link) {
193*51f49692SMarouene Boubakri 		if (wqe->cv == cv) {
194*51f49692SMarouene Boubakri 			rc = true;
195*51f49692SMarouene Boubakri 			break;
196*51f49692SMarouene Boubakri 		}
197*51f49692SMarouene Boubakri 	}
198*51f49692SMarouene Boubakri 
199*51f49692SMarouene Boubakri 	cpu_spin_unlock_xrestore(&wq_spin_lock, old_itr_status);
200*51f49692SMarouene Boubakri 
201*51f49692SMarouene Boubakri 	return rc;
202*51f49692SMarouene Boubakri }
203*51f49692SMarouene Boubakri 
204*51f49692SMarouene Boubakri bool wq_is_empty(struct wait_queue *wq)
205*51f49692SMarouene Boubakri {
206*51f49692SMarouene Boubakri 	uint32_t old_itr_status;
207*51f49692SMarouene Boubakri 	bool ret;
208*51f49692SMarouene Boubakri 
209*51f49692SMarouene Boubakri 	old_itr_status = cpu_spin_lock_xsave(&wq_spin_lock);
210*51f49692SMarouene Boubakri 
211*51f49692SMarouene Boubakri 	ret = SLIST_EMPTY(wq);
212*51f49692SMarouene Boubakri 
213*51f49692SMarouene Boubakri 	cpu_spin_unlock_xrestore(&wq_spin_lock, old_itr_status);
214*51f49692SMarouene Boubakri 
215*51f49692SMarouene Boubakri 	return ret;
216*51f49692SMarouene Boubakri }
217