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> 7*3a20c661SEtienne 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 1651f49692SMarouene Boubakri void mutex_init(struct mutex *m) 1751f49692SMarouene Boubakri { 1851f49692SMarouene Boubakri *m = (struct mutex)MUTEX_INITIALIZER; 1951f49692SMarouene Boubakri } 2051f49692SMarouene Boubakri 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 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 */ 6451f49692SMarouene Boubakri wq_wait_final(&m->wq, &wqe, m, fname, lineno); 6551f49692SMarouene Boubakri } else 6651f49692SMarouene Boubakri return; 6751f49692SMarouene Boubakri } 6851f49692SMarouene Boubakri } 6951f49692SMarouene Boubakri 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 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 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 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 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 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 */ 20951f49692SMarouene Boubakri wq_wait_final(&m->wq, &wqe, m, fname, lineno); 21051f49692SMarouene Boubakri } else 21151f49692SMarouene Boubakri return; 21251f49692SMarouene Boubakri } 21351f49692SMarouene Boubakri } 21451f49692SMarouene Boubakri 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 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 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 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 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 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 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 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 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 27951f49692SMarouene Boubakri void mutex_unlock(struct mutex *m) 28051f49692SMarouene Boubakri { 28151f49692SMarouene Boubakri __mutex_unlock(m, NULL, -1); 28251f49692SMarouene Boubakri } 28351f49692SMarouene Boubakri 28451f49692SMarouene Boubakri void mutex_unlock_recursive(struct recursive_mutex *m) 28551f49692SMarouene Boubakri { 28651f49692SMarouene Boubakri __mutex_unlock_recursive(m, NULL, -1); 28751f49692SMarouene Boubakri } 28851f49692SMarouene Boubakri 28951f49692SMarouene Boubakri void mutex_lock(struct mutex *m) 29051f49692SMarouene Boubakri { 29151f49692SMarouene Boubakri __mutex_lock(m, NULL, -1); 29251f49692SMarouene Boubakri } 29351f49692SMarouene Boubakri 29451f49692SMarouene Boubakri void mutex_lock_recursive(struct recursive_mutex *m) 29551f49692SMarouene Boubakri { 29651f49692SMarouene Boubakri __mutex_lock_recursive(m, NULL, -1); 29751f49692SMarouene Boubakri } 29851f49692SMarouene Boubakri 29951f49692SMarouene Boubakri bool mutex_trylock(struct mutex *m) 30051f49692SMarouene Boubakri { 30151f49692SMarouene Boubakri return __mutex_trylock(m, NULL, -1); 30251f49692SMarouene Boubakri } 30351f49692SMarouene Boubakri 30451f49692SMarouene Boubakri void mutex_read_unlock(struct mutex *m) 30551f49692SMarouene Boubakri { 30651f49692SMarouene Boubakri __mutex_read_unlock(m, NULL, -1); 30751f49692SMarouene Boubakri } 30851f49692SMarouene Boubakri 30951f49692SMarouene Boubakri void mutex_read_lock(struct mutex *m) 31051f49692SMarouene Boubakri { 31151f49692SMarouene Boubakri __mutex_read_lock(m, NULL, -1); 31251f49692SMarouene Boubakri } 31351f49692SMarouene Boubakri 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 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 33351f49692SMarouene Boubakri void mutex_destroy_recursive(struct recursive_mutex *m) 33451f49692SMarouene Boubakri { 33551f49692SMarouene Boubakri mutex_destroy(&m->m); 33651f49692SMarouene Boubakri } 33751f49692SMarouene Boubakri 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 346*3a20c661SEtienne Carriere void mutex_pm_aware_init(struct mutex_pm_aware *m) 347*3a20c661SEtienne Carriere { 348*3a20c661SEtienne Carriere *m = (struct mutex_pm_aware)MUTEX_PM_AWARE_INITIALIZER; 349*3a20c661SEtienne Carriere } 350*3a20c661SEtienne Carriere 351*3a20c661SEtienne Carriere void mutex_pm_aware_destroy(struct mutex_pm_aware *m) 352*3a20c661SEtienne Carriere { 353*3a20c661SEtienne Carriere mutex_destroy(&m->mutex); 354*3a20c661SEtienne Carriere } 355*3a20c661SEtienne Carriere 356*3a20c661SEtienne Carriere void mutex_pm_aware_lock(struct mutex_pm_aware *m) 357*3a20c661SEtienne Carriere { 358*3a20c661SEtienne Carriere if (thread_get_id_may_fail() == THREAD_ID_INVALID) { 359*3a20c661SEtienne Carriere if (!cpu_spin_trylock(&m->lock) || m->mutex.state) 360*3a20c661SEtienne Carriere panic(); 361*3a20c661SEtienne Carriere } else { 362*3a20c661SEtienne Carriere mutex_lock(&m->mutex); 363*3a20c661SEtienne Carriere if (!thread_spin_trylock(&m->lock)) 364*3a20c661SEtienne Carriere panic(); 365*3a20c661SEtienne Carriere } 366*3a20c661SEtienne Carriere } 367*3a20c661SEtienne Carriere 368*3a20c661SEtienne Carriere void mutex_pm_aware_unlock(struct mutex_pm_aware *m) 369*3a20c661SEtienne Carriere { 370*3a20c661SEtienne Carriere if (thread_get_id_may_fail() == THREAD_ID_INVALID) { 371*3a20c661SEtienne Carriere assert(!m->mutex.state); 372*3a20c661SEtienne Carriere cpu_spin_unlock(&m->lock); 373*3a20c661SEtienne Carriere } else { 374*3a20c661SEtienne Carriere thread_spin_unlock(&m->lock); 375*3a20c661SEtienne Carriere mutex_unlock(&m->mutex); 376*3a20c661SEtienne Carriere } 377*3a20c661SEtienne Carriere } 378*3a20c661SEtienne Carriere 37951f49692SMarouene Boubakri void condvar_init(struct condvar *cv) 38051f49692SMarouene Boubakri { 38151f49692SMarouene Boubakri *cv = (struct condvar)CONDVAR_INITIALIZER; 38251f49692SMarouene Boubakri } 38351f49692SMarouene Boubakri 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 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 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 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 41951f49692SMarouene Boubakri void condvar_signal(struct condvar *cv) 42051f49692SMarouene Boubakri { 42151f49692SMarouene Boubakri cv_signal(cv, true /* only one */, NULL, -1); 42251f49692SMarouene Boubakri } 42351f49692SMarouene Boubakri 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 43051f49692SMarouene Boubakri static void __condvar_wait(struct condvar *cv, struct mutex *m, 43151f49692SMarouene Boubakri const char *fname, int lineno) 43251f49692SMarouene Boubakri { 43351f49692SMarouene Boubakri uint32_t old_itr_status; 43451f49692SMarouene Boubakri struct wait_queue_elem wqe; 43551f49692SMarouene Boubakri short old_state; 43651f49692SMarouene Boubakri short new_state; 43751f49692SMarouene Boubakri 43851f49692SMarouene Boubakri mutex_unlock_check(m); 43951f49692SMarouene Boubakri 44051f49692SMarouene Boubakri /* Link this condvar to this mutex until reinitialized */ 44151f49692SMarouene Boubakri old_itr_status = cpu_spin_lock_xsave(&cv->spin_lock); 44251f49692SMarouene Boubakri if (cv->m && cv->m != m) 44351f49692SMarouene Boubakri panic("invalid mutex"); 44451f49692SMarouene Boubakri 44551f49692SMarouene Boubakri cv->m = m; 44651f49692SMarouene Boubakri cpu_spin_unlock(&cv->spin_lock); 44751f49692SMarouene Boubakri 44851f49692SMarouene Boubakri cpu_spin_lock(&m->spin_lock); 44951f49692SMarouene Boubakri 45051f49692SMarouene Boubakri if (!m->state) 45151f49692SMarouene Boubakri panic(); 45251f49692SMarouene Boubakri old_state = m->state; 45351f49692SMarouene Boubakri /* Add to mutex wait queue as a condvar waiter */ 45451f49692SMarouene Boubakri wq_wait_init_condvar(&m->wq, &wqe, cv, m->state > 0); 45551f49692SMarouene Boubakri 45651f49692SMarouene Boubakri if (m->state > 1) { 45751f49692SMarouene Boubakri /* Multiple read locks, remove one */ 45851f49692SMarouene Boubakri m->state--; 45951f49692SMarouene Boubakri } else { 46051f49692SMarouene Boubakri /* Only one lock (read or write), unlock the mutex */ 46151f49692SMarouene Boubakri m->state = 0; 46251f49692SMarouene Boubakri } 46351f49692SMarouene Boubakri new_state = m->state; 46451f49692SMarouene Boubakri 46551f49692SMarouene Boubakri cpu_spin_unlock_xrestore(&m->spin_lock, old_itr_status); 46651f49692SMarouene Boubakri 46751f49692SMarouene Boubakri /* Wake eventual waiters if the mutex was unlocked */ 46851f49692SMarouene Boubakri if (!new_state) 46951f49692SMarouene Boubakri wq_wake_next(&m->wq, m, fname, lineno); 47051f49692SMarouene Boubakri 47151f49692SMarouene Boubakri wq_wait_final(&m->wq, &wqe, m, fname, lineno); 47251f49692SMarouene Boubakri 47351f49692SMarouene Boubakri if (old_state > 0) 47451f49692SMarouene Boubakri mutex_read_lock(m); 47551f49692SMarouene Boubakri else 47651f49692SMarouene Boubakri mutex_lock(m); 47751f49692SMarouene Boubakri } 47851f49692SMarouene Boubakri 47951f49692SMarouene Boubakri #ifdef CFG_MUTEX_DEBUG 48051f49692SMarouene Boubakri void condvar_wait_debug(struct condvar *cv, struct mutex *m, 48151f49692SMarouene Boubakri const char *fname, int lineno) 48251f49692SMarouene Boubakri { 48351f49692SMarouene Boubakri __condvar_wait(cv, m, fname, lineno); 48451f49692SMarouene Boubakri } 48551f49692SMarouene Boubakri #else 48651f49692SMarouene Boubakri void condvar_wait(struct condvar *cv, struct mutex *m) 48751f49692SMarouene Boubakri { 48851f49692SMarouene Boubakri __condvar_wait(cv, m, NULL, -1); 48951f49692SMarouene Boubakri } 49051f49692SMarouene Boubakri #endif 491