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