1a091d823SDavid Xu /* 2a091d823SDavid Xu * Copyright (c) 2005 David Xu <davidxu@freebsd.org> 3a091d823SDavid Xu * All rights reserved. 4a091d823SDavid Xu * 5a091d823SDavid Xu * Redistribution and use in source and binary forms, with or without 6a091d823SDavid Xu * modification, are permitted provided that the following conditions 7a091d823SDavid Xu * are met: 8a091d823SDavid Xu * 1. Redistributions of source code must retain the above copyright 9a091d823SDavid Xu * notice unmodified, this list of conditions, and the following 10a091d823SDavid Xu * disclaimer. 11a091d823SDavid Xu * 2. Redistributions in binary form must reproduce the above copyright 12a091d823SDavid Xu * notice, this list of conditions and the following disclaimer in the 13a091d823SDavid Xu * documentation and/or other materials provided with the distribution. 14a091d823SDavid Xu * 15a091d823SDavid Xu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16a091d823SDavid Xu * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17a091d823SDavid Xu * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18a091d823SDavid Xu * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19a091d823SDavid Xu * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20a091d823SDavid Xu * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21a091d823SDavid Xu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22a091d823SDavid Xu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23a091d823SDavid Xu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24a091d823SDavid Xu * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25a091d823SDavid Xu * 26a091d823SDavid Xu * $FreeBSD$ 27a091d823SDavid Xu * 28a091d823SDavid Xu */ 29a091d823SDavid Xu 30a091d823SDavid Xu #include "thr_private.h" 31a091d823SDavid Xu #include "thr_umtx.h" 32a091d823SDavid Xu 33d6e0eb0aSDavid Xu #ifndef HAS__UMTX_OP_ERR 34d6e0eb0aSDavid Xu int _umtx_op_err(void *obj, int op, u_long val, void *uaddr, void *uaddr2) 35d6e0eb0aSDavid Xu { 36d6e0eb0aSDavid Xu if (_umtx_op(obj, op, val, uaddr, uaddr2) == -1) 37d6e0eb0aSDavid Xu return (errno); 38d6e0eb0aSDavid Xu return (0); 39d6e0eb0aSDavid Xu } 40d6e0eb0aSDavid Xu #endif 41d6e0eb0aSDavid Xu 428042f26dSDavid Xu void 438042f26dSDavid Xu _thr_umutex_init(struct umutex *mtx) 448042f26dSDavid Xu { 458042f26dSDavid Xu static struct umutex default_mtx = DEFAULT_UMUTEX; 468042f26dSDavid Xu 478042f26dSDavid Xu *mtx = default_mtx; 488042f26dSDavid Xu } 498042f26dSDavid Xu 50ada33a6eSDavid Xu void 51ada33a6eSDavid Xu _thr_urwlock_init(struct urwlock *rwl) 52ada33a6eSDavid Xu { 53ada33a6eSDavid Xu static struct urwlock default_rwl = DEFAULT_URWLOCK; 54ada33a6eSDavid Xu *rwl = default_rwl; 55ada33a6eSDavid Xu } 56ada33a6eSDavid Xu 57a091d823SDavid Xu int 587de1ecefSDavid Xu __thr_umutex_lock(struct umutex *mtx, uint32_t id) 59cf13ecdaSDavid Xu { 607de1ecefSDavid Xu uint32_t owner; 617de1ecefSDavid Xu 627de1ecefSDavid Xu if ((mtx->m_flags & (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) == 0) { 637de1ecefSDavid Xu for (;;) { 647de1ecefSDavid Xu /* wait in kernel */ 657de1ecefSDavid Xu _umtx_op_err(mtx, UMTX_OP_MUTEX_WAIT, 0, 0, 0); 667de1ecefSDavid Xu 677de1ecefSDavid Xu owner = mtx->m_owner; 687de1ecefSDavid Xu if ((owner & ~UMUTEX_CONTESTED) == 0 && 697de1ecefSDavid Xu atomic_cmpset_acq_32(&mtx->m_owner, owner, id|owner)) 707de1ecefSDavid Xu return (0); 717de1ecefSDavid Xu } 727de1ecefSDavid Xu } 737de1ecefSDavid Xu 74d6e0eb0aSDavid Xu return _umtx_op_err(mtx, UMTX_OP_MUTEX_LOCK, 0, 0, 0); 75cf13ecdaSDavid Xu } 76cf13ecdaSDavid Xu 77d1078b0bSDavid Xu #define SPINLOOPS 1000 78d1078b0bSDavid Xu 79d1078b0bSDavid Xu int 80d1078b0bSDavid Xu __thr_umutex_lock_spin(struct umutex *mtx, uint32_t id) 81d1078b0bSDavid Xu { 82d1078b0bSDavid Xu uint32_t owner; 83d1078b0bSDavid Xu 84d1078b0bSDavid Xu if (!_thr_is_smp) 85d1078b0bSDavid Xu return __thr_umutex_lock(mtx, id); 86d1078b0bSDavid Xu 87d1078b0bSDavid Xu if ((mtx->m_flags & (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) == 0) { 88d1078b0bSDavid Xu for (;;) { 89d1078b0bSDavid Xu int count = SPINLOOPS; 90d1078b0bSDavid Xu while (count--) { 91d1078b0bSDavid Xu owner = mtx->m_owner; 92d1078b0bSDavid Xu if ((owner & ~UMUTEX_CONTESTED) == 0) { 93d1078b0bSDavid Xu if (atomic_cmpset_acq_32( 94d1078b0bSDavid Xu &mtx->m_owner, 95d1078b0bSDavid Xu owner, id|owner)) { 96d1078b0bSDavid Xu return (0); 97d1078b0bSDavid Xu } 98d1078b0bSDavid Xu } 99d1078b0bSDavid Xu CPU_SPINWAIT; 100d1078b0bSDavid Xu } 101d1078b0bSDavid Xu 102d1078b0bSDavid Xu /* wait in kernel */ 103d1078b0bSDavid Xu _umtx_op_err(mtx, UMTX_OP_MUTEX_WAIT, 0, 0, 0); 104d1078b0bSDavid Xu } 105d1078b0bSDavid Xu } 106d1078b0bSDavid Xu 107d1078b0bSDavid Xu return _umtx_op_err(mtx, UMTX_OP_MUTEX_LOCK, 0, 0, 0); 108d1078b0bSDavid Xu } 109d1078b0bSDavid Xu 110cf13ecdaSDavid Xu int 1117de1ecefSDavid Xu __thr_umutex_timedlock(struct umutex *mtx, uint32_t id, 112*df1f1baeSDavid Xu const struct timespec *abstime) 113cf13ecdaSDavid Xu { 114*df1f1baeSDavid Xu struct _umtx_time *tm_p, timeout; 115*df1f1baeSDavid Xu size_t tm_size; 1167de1ecefSDavid Xu uint32_t owner; 1177de1ecefSDavid Xu int ret; 1187de1ecefSDavid Xu 119*df1f1baeSDavid Xu if (abstime == NULL) { 120*df1f1baeSDavid Xu tm_p = NULL; 121*df1f1baeSDavid Xu tm_size = 0; 122*df1f1baeSDavid Xu } else { 123*df1f1baeSDavid Xu timeout._clockid = CLOCK_REALTIME; 124*df1f1baeSDavid Xu timeout._flags = UMTX_ABSTIME; 125*df1f1baeSDavid Xu timeout._timeout = *abstime; 126*df1f1baeSDavid Xu tm_p = &timeout; 127*df1f1baeSDavid Xu tm_size = sizeof(timeout); 128*df1f1baeSDavid Xu } 1297de1ecefSDavid Xu 1307de1ecefSDavid Xu for (;;) { 1317de1ecefSDavid Xu if ((mtx->m_flags & (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) == 0) { 1327de1ecefSDavid Xu 1337de1ecefSDavid Xu /* wait in kernel */ 134*df1f1baeSDavid Xu ret = _umtx_op_err(mtx, UMTX_OP_MUTEX_WAIT, 0, 135*df1f1baeSDavid Xu (void *)tm_size, __DECONST(void *, tm_p)); 1367de1ecefSDavid Xu 1377de1ecefSDavid Xu /* now try to lock it */ 1387de1ecefSDavid Xu owner = mtx->m_owner; 1397de1ecefSDavid Xu if ((owner & ~UMUTEX_CONTESTED) == 0 && 1407de1ecefSDavid Xu atomic_cmpset_acq_32(&mtx->m_owner, owner, id|owner)) 1417de1ecefSDavid Xu return (0); 1427de1ecefSDavid Xu } else { 143*df1f1baeSDavid Xu ret = _umtx_op_err(mtx, UMTX_OP_MUTEX_LOCK, 0, 144*df1f1baeSDavid Xu (void *)tm_size, __DECONST(void *, tm_p)); 1457de1ecefSDavid Xu if (ret == 0) 1467de1ecefSDavid Xu break; 1477de1ecefSDavid Xu } 1487de1ecefSDavid Xu if (ret == ETIMEDOUT) 1497de1ecefSDavid Xu break; 1507de1ecefSDavid Xu } 1517de1ecefSDavid Xu return (ret); 152cf13ecdaSDavid Xu } 153cf13ecdaSDavid Xu 154cf13ecdaSDavid Xu int 1557de1ecefSDavid Xu __thr_umutex_unlock(struct umutex *mtx, uint32_t id) 156cf13ecdaSDavid Xu { 157e4f141b5SMarcel Moolenaar #ifndef __ia64__ 158e4f141b5SMarcel Moolenaar /* XXX this logic has a race-condition on ia64. */ 1597de1ecefSDavid Xu if ((mtx->m_flags & (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) == 0) { 1607de1ecefSDavid Xu atomic_cmpset_rel_32(&mtx->m_owner, id | UMUTEX_CONTESTED, UMUTEX_CONTESTED); 1617de1ecefSDavid Xu return _umtx_op_err(mtx, UMTX_OP_MUTEX_WAKE, 0, 0, 0); 1627de1ecefSDavid Xu } 163e4f141b5SMarcel Moolenaar #endif /* __ia64__ */ 164d6e0eb0aSDavid Xu return _umtx_op_err(mtx, UMTX_OP_MUTEX_UNLOCK, 0, 0, 0); 165cf13ecdaSDavid Xu } 166cf13ecdaSDavid Xu 167cf13ecdaSDavid Xu int 1688042f26dSDavid Xu __thr_umutex_trylock(struct umutex *mtx) 169cf13ecdaSDavid Xu { 170d6e0eb0aSDavid Xu return _umtx_op_err(mtx, UMTX_OP_MUTEX_TRYLOCK, 0, 0, 0); 171cf13ecdaSDavid Xu } 172cf13ecdaSDavid Xu 173cf13ecdaSDavid Xu int 174cf13ecdaSDavid Xu __thr_umutex_set_ceiling(struct umutex *mtx, uint32_t ceiling, 175cf13ecdaSDavid Xu uint32_t *oldceiling) 176cf13ecdaSDavid Xu { 177d6e0eb0aSDavid Xu return _umtx_op_err(mtx, UMTX_OP_SET_CEILING, ceiling, oldceiling, 0); 178cf13ecdaSDavid Xu } 179cf13ecdaSDavid Xu 180cf13ecdaSDavid Xu int 1816fdfcacbSDavid Xu _thr_umtx_wait(volatile long *mtx, long id, const struct timespec *timeout) 182a091d823SDavid Xu { 183a091d823SDavid Xu if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 && 184a091d823SDavid Xu timeout->tv_nsec <= 0))) 185a091d823SDavid Xu return (ETIMEDOUT); 186d6e0eb0aSDavid Xu return _umtx_op_err(__DEVOLATILE(void *, mtx), UMTX_OP_WAIT, id, 0, 187d6e0eb0aSDavid Xu __DECONST(void*, timeout)); 188a091d823SDavid Xu } 189a091d823SDavid Xu 190a091d823SDavid Xu int 1918d6a11a0SDavid Xu _thr_umtx_wait_uint(volatile u_int *mtx, u_int id, const struct timespec *timeout, int shared) 1926fdfcacbSDavid Xu { 1936fdfcacbSDavid Xu if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 && 1946fdfcacbSDavid Xu timeout->tv_nsec <= 0))) 1956fdfcacbSDavid Xu return (ETIMEDOUT); 1968d6a11a0SDavid Xu return _umtx_op_err(__DEVOLATILE(void *, mtx), 1978d6a11a0SDavid Xu shared ? UMTX_OP_WAIT_UINT : UMTX_OP_WAIT_UINT_PRIVATE, id, 0, 198d6e0eb0aSDavid Xu __DECONST(void*, timeout)); 1996fdfcacbSDavid Xu } 2006fdfcacbSDavid Xu 2016fdfcacbSDavid Xu int 202d1078b0bSDavid Xu _thr_umtx_timedwait_uint(volatile u_int *mtx, u_int id, int clockid, 203d1078b0bSDavid Xu const struct timespec *abstime, int shared) 204d1078b0bSDavid Xu { 205*df1f1baeSDavid Xu struct _umtx_time *tm_p, timeout; 206*df1f1baeSDavid Xu size_t tm_size; 207*df1f1baeSDavid Xu 208*df1f1baeSDavid Xu if (abstime == NULL) { 209*df1f1baeSDavid Xu tm_p = NULL; 210*df1f1baeSDavid Xu tm_size = 0; 211*df1f1baeSDavid Xu } else { 212*df1f1baeSDavid Xu timeout._clockid = CLOCK_REALTIME; 213*df1f1baeSDavid Xu timeout._flags = UMTX_ABSTIME; 214*df1f1baeSDavid Xu timeout._timeout = *abstime; 215*df1f1baeSDavid Xu tm_p = &timeout; 216*df1f1baeSDavid Xu tm_size = sizeof(timeout); 217*df1f1baeSDavid Xu } 218*df1f1baeSDavid Xu 219d1078b0bSDavid Xu return _umtx_op_err(__DEVOLATILE(void *, mtx), 220b13a8fa7SDavid Xu shared ? UMTX_OP_WAIT_UINT : UMTX_OP_WAIT_UINT_PRIVATE, id, 221*df1f1baeSDavid Xu (void *)tm_size, __DECONST(void *, tm_p)); 222d1078b0bSDavid Xu } 223d1078b0bSDavid Xu 224d1078b0bSDavid Xu int 2258d6a11a0SDavid Xu _thr_umtx_wake(volatile void *mtx, int nr_wakeup, int shared) 226a091d823SDavid Xu { 2278d6a11a0SDavid Xu return _umtx_op_err(__DEVOLATILE(void *, mtx), shared ? UMTX_OP_WAKE : UMTX_OP_WAKE_PRIVATE, 228d6e0eb0aSDavid Xu nr_wakeup, 0, 0); 229a091d823SDavid Xu } 2302bd2c907SDavid Xu 231670b44d6SDavid Xu void 232670b44d6SDavid Xu _thr_ucond_init(struct ucond *cv) 233670b44d6SDavid Xu { 234670b44d6SDavid Xu bzero(cv, sizeof(struct ucond)); 235670b44d6SDavid Xu } 236670b44d6SDavid Xu 2372bd2c907SDavid Xu int 2382bd2c907SDavid Xu _thr_ucond_wait(struct ucond *cv, struct umutex *m, 2397859df8eSDavid Xu const struct timespec *timeout, int flags) 2402bd2c907SDavid Xu { 2412bd2c907SDavid Xu if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 && 2422bd2c907SDavid Xu timeout->tv_nsec <= 0))) { 2437de1ecefSDavid Xu struct pthread *curthread = _get_curthread(); 2447de1ecefSDavid Xu _thr_umutex_unlock(m, TID(curthread)); 2452bd2c907SDavid Xu return (ETIMEDOUT); 2462bd2c907SDavid Xu } 2477859df8eSDavid Xu return _umtx_op_err(cv, UMTX_OP_CV_WAIT, flags, 248d6e0eb0aSDavid Xu m, __DECONST(void*, timeout)); 2492bd2c907SDavid Xu } 2502bd2c907SDavid Xu 2512bd2c907SDavid Xu int 2522bd2c907SDavid Xu _thr_ucond_signal(struct ucond *cv) 2532bd2c907SDavid Xu { 254347126a2SDavid Xu if (!cv->c_has_waiters) 255347126a2SDavid Xu return (0); 256d6e0eb0aSDavid Xu return _umtx_op_err(cv, UMTX_OP_CV_SIGNAL, 0, NULL, NULL); 2572bd2c907SDavid Xu } 2582bd2c907SDavid Xu 2592bd2c907SDavid Xu int 2602bd2c907SDavid Xu _thr_ucond_broadcast(struct ucond *cv) 2612bd2c907SDavid Xu { 262347126a2SDavid Xu if (!cv->c_has_waiters) 263347126a2SDavid Xu return (0); 264d6e0eb0aSDavid Xu return _umtx_op_err(cv, UMTX_OP_CV_BROADCAST, 0, NULL, NULL); 2652bd2c907SDavid Xu } 2668bf1a48cSDavid Xu 2678bf1a48cSDavid Xu int 2688bf1a48cSDavid Xu __thr_rwlock_rdlock(struct urwlock *rwlock, int flags, struct timespec *tsp) 2698bf1a48cSDavid Xu { 270d6e0eb0aSDavid Xu return _umtx_op_err(rwlock, UMTX_OP_RW_RDLOCK, flags, NULL, tsp); 2718bf1a48cSDavid Xu } 2728bf1a48cSDavid Xu 2738bf1a48cSDavid Xu int 2748bf1a48cSDavid Xu __thr_rwlock_wrlock(struct urwlock *rwlock, struct timespec *tsp) 2758bf1a48cSDavid Xu { 276d6e0eb0aSDavid Xu return _umtx_op_err(rwlock, UMTX_OP_RW_WRLOCK, 0, NULL, tsp); 2778bf1a48cSDavid Xu } 2788bf1a48cSDavid Xu 2798bf1a48cSDavid Xu int 2808bf1a48cSDavid Xu __thr_rwlock_unlock(struct urwlock *rwlock) 2818bf1a48cSDavid Xu { 282d6e0eb0aSDavid Xu return _umtx_op_err(rwlock, UMTX_OP_RW_UNLOCK, 0, NULL, NULL); 2838bf1a48cSDavid Xu } 28402c3c858SDavid Xu 28502c3c858SDavid Xu void 28602c3c858SDavid Xu _thr_rwl_rdlock(struct urwlock *rwlock) 28702c3c858SDavid Xu { 28802c3c858SDavid Xu int ret; 28902c3c858SDavid Xu 29002c3c858SDavid Xu for (;;) { 29102c3c858SDavid Xu if (_thr_rwlock_tryrdlock(rwlock, URWLOCK_PREFER_READER) == 0) 29202c3c858SDavid Xu return; 29302c3c858SDavid Xu ret = __thr_rwlock_rdlock(rwlock, URWLOCK_PREFER_READER, NULL); 29402c3c858SDavid Xu if (ret == 0) 29502c3c858SDavid Xu return; 29602c3c858SDavid Xu if (ret != EINTR) 29702c3c858SDavid Xu PANIC("rdlock error"); 29802c3c858SDavid Xu } 29902c3c858SDavid Xu } 30002c3c858SDavid Xu 30102c3c858SDavid Xu void 30202c3c858SDavid Xu _thr_rwl_wrlock(struct urwlock *rwlock) 30302c3c858SDavid Xu { 30402c3c858SDavid Xu int ret; 30502c3c858SDavid Xu 30602c3c858SDavid Xu for (;;) { 30702c3c858SDavid Xu if (_thr_rwlock_trywrlock(rwlock) == 0) 30802c3c858SDavid Xu return; 30902c3c858SDavid Xu ret = __thr_rwlock_wrlock(rwlock, NULL); 31002c3c858SDavid Xu if (ret == 0) 31102c3c858SDavid Xu return; 31202c3c858SDavid Xu if (ret != EINTR) 31302c3c858SDavid Xu PANIC("wrlock error"); 31402c3c858SDavid Xu } 31502c3c858SDavid Xu } 31602c3c858SDavid Xu 31702c3c858SDavid Xu void 31802c3c858SDavid Xu _thr_rwl_unlock(struct urwlock *rwlock) 31902c3c858SDavid Xu { 32002c3c858SDavid Xu if (_thr_rwlock_unlock(rwlock)) 32102c3c858SDavid Xu PANIC("unlock error"); 32202c3c858SDavid Xu } 323