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, ¶ms); 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