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 77*d1078b0bSDavid Xu #define SPINLOOPS 1000 78*d1078b0bSDavid Xu 79*d1078b0bSDavid Xu int 80*d1078b0bSDavid Xu __thr_umutex_lock_spin(struct umutex *mtx, uint32_t id) 81*d1078b0bSDavid Xu { 82*d1078b0bSDavid Xu uint32_t owner; 83*d1078b0bSDavid Xu 84*d1078b0bSDavid Xu if (!_thr_is_smp) 85*d1078b0bSDavid Xu return __thr_umutex_lock(mtx, id); 86*d1078b0bSDavid Xu 87*d1078b0bSDavid Xu if ((mtx->m_flags & (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) == 0) { 88*d1078b0bSDavid Xu for (;;) { 89*d1078b0bSDavid Xu int count = SPINLOOPS; 90*d1078b0bSDavid Xu while (count--) { 91*d1078b0bSDavid Xu owner = mtx->m_owner; 92*d1078b0bSDavid Xu if ((owner & ~UMUTEX_CONTESTED) == 0) { 93*d1078b0bSDavid Xu if (atomic_cmpset_acq_32( 94*d1078b0bSDavid Xu &mtx->m_owner, 95*d1078b0bSDavid Xu owner, id|owner)) { 96*d1078b0bSDavid Xu return (0); 97*d1078b0bSDavid Xu } 98*d1078b0bSDavid Xu } 99*d1078b0bSDavid Xu CPU_SPINWAIT; 100*d1078b0bSDavid Xu } 101*d1078b0bSDavid Xu 102*d1078b0bSDavid Xu /* wait in kernel */ 103*d1078b0bSDavid Xu _umtx_op_err(mtx, UMTX_OP_MUTEX_WAIT, 0, 0, 0); 104*d1078b0bSDavid Xu } 105*d1078b0bSDavid Xu } 106*d1078b0bSDavid Xu 107*d1078b0bSDavid Xu return _umtx_op_err(mtx, UMTX_OP_MUTEX_LOCK, 0, 0, 0); 108*d1078b0bSDavid Xu } 109*d1078b0bSDavid Xu 110cf13ecdaSDavid Xu int 1117de1ecefSDavid Xu __thr_umutex_timedlock(struct umutex *mtx, uint32_t id, 1127de1ecefSDavid Xu const struct timespec *ets) 113cf13ecdaSDavid Xu { 1147de1ecefSDavid Xu struct timespec timo, cts; 1157de1ecefSDavid Xu uint32_t owner; 1167de1ecefSDavid Xu int ret; 1177de1ecefSDavid Xu 1187de1ecefSDavid Xu clock_gettime(CLOCK_REALTIME, &cts); 1197de1ecefSDavid Xu TIMESPEC_SUB(&timo, ets, &cts); 1207de1ecefSDavid Xu 1217de1ecefSDavid Xu if (timo.tv_sec < 0) 122cf13ecdaSDavid Xu return (ETIMEDOUT); 1237de1ecefSDavid Xu 1247de1ecefSDavid Xu for (;;) { 1257de1ecefSDavid Xu if ((mtx->m_flags & (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) == 0) { 1267de1ecefSDavid Xu 1277de1ecefSDavid Xu /* wait in kernel */ 1287de1ecefSDavid Xu ret = _umtx_op_err(mtx, UMTX_OP_MUTEX_WAIT, 0, 0, &timo); 1297de1ecefSDavid Xu 1307de1ecefSDavid Xu /* now try to lock it */ 1317de1ecefSDavid Xu owner = mtx->m_owner; 1327de1ecefSDavid Xu if ((owner & ~UMUTEX_CONTESTED) == 0 && 1337de1ecefSDavid Xu atomic_cmpset_acq_32(&mtx->m_owner, owner, id|owner)) 1347de1ecefSDavid Xu return (0); 1357de1ecefSDavid Xu } else { 1367de1ecefSDavid Xu ret = _umtx_op_err(mtx, UMTX_OP_MUTEX_LOCK, 0, 0, &timo); 1377de1ecefSDavid Xu if (ret == 0) 1387de1ecefSDavid Xu break; 1397de1ecefSDavid Xu } 1407de1ecefSDavid Xu if (ret == ETIMEDOUT) 1417de1ecefSDavid Xu break; 1427de1ecefSDavid Xu clock_gettime(CLOCK_REALTIME, &cts); 1437de1ecefSDavid Xu TIMESPEC_SUB(&timo, ets, &cts); 1447de1ecefSDavid Xu if (timo.tv_sec < 0 || (timo.tv_sec == 0 && timo.tv_nsec == 0)) { 1457de1ecefSDavid Xu ret = ETIMEDOUT; 1467de1ecefSDavid Xu break; 1477de1ecefSDavid Xu } 1487de1ecefSDavid Xu } 1497de1ecefSDavid Xu return (ret); 150cf13ecdaSDavid Xu } 151cf13ecdaSDavid Xu 152cf13ecdaSDavid Xu int 1537de1ecefSDavid Xu __thr_umutex_unlock(struct umutex *mtx, uint32_t id) 154cf13ecdaSDavid Xu { 155e4f141b5SMarcel Moolenaar #ifndef __ia64__ 156e4f141b5SMarcel Moolenaar /* XXX this logic has a race-condition on ia64. */ 1577de1ecefSDavid Xu if ((mtx->m_flags & (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) == 0) { 1587de1ecefSDavid Xu atomic_cmpset_rel_32(&mtx->m_owner, id | UMUTEX_CONTESTED, UMUTEX_CONTESTED); 1597de1ecefSDavid Xu return _umtx_op_err(mtx, UMTX_OP_MUTEX_WAKE, 0, 0, 0); 1607de1ecefSDavid Xu } 161e4f141b5SMarcel Moolenaar #endif /* __ia64__ */ 162d6e0eb0aSDavid Xu return _umtx_op_err(mtx, UMTX_OP_MUTEX_UNLOCK, 0, 0, 0); 163cf13ecdaSDavid Xu } 164cf13ecdaSDavid Xu 165cf13ecdaSDavid Xu int 1668042f26dSDavid Xu __thr_umutex_trylock(struct umutex *mtx) 167cf13ecdaSDavid Xu { 168d6e0eb0aSDavid Xu return _umtx_op_err(mtx, UMTX_OP_MUTEX_TRYLOCK, 0, 0, 0); 169cf13ecdaSDavid Xu } 170cf13ecdaSDavid Xu 171cf13ecdaSDavid Xu int 172cf13ecdaSDavid Xu __thr_umutex_set_ceiling(struct umutex *mtx, uint32_t ceiling, 173cf13ecdaSDavid Xu uint32_t *oldceiling) 174cf13ecdaSDavid Xu { 175d6e0eb0aSDavid Xu return _umtx_op_err(mtx, UMTX_OP_SET_CEILING, ceiling, oldceiling, 0); 176cf13ecdaSDavid Xu } 177cf13ecdaSDavid Xu 178cf13ecdaSDavid Xu int 1796fdfcacbSDavid Xu _thr_umtx_wait(volatile long *mtx, long id, const struct timespec *timeout) 180a091d823SDavid Xu { 181a091d823SDavid Xu if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 && 182a091d823SDavid Xu timeout->tv_nsec <= 0))) 183a091d823SDavid Xu return (ETIMEDOUT); 184d6e0eb0aSDavid Xu return _umtx_op_err(__DEVOLATILE(void *, mtx), UMTX_OP_WAIT, id, 0, 185d6e0eb0aSDavid Xu __DECONST(void*, timeout)); 186a091d823SDavid Xu } 187a091d823SDavid Xu 188a091d823SDavid Xu int 1898d6a11a0SDavid Xu _thr_umtx_wait_uint(volatile u_int *mtx, u_int id, const struct timespec *timeout, int shared) 1906fdfcacbSDavid Xu { 1916fdfcacbSDavid Xu if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 && 1926fdfcacbSDavid Xu timeout->tv_nsec <= 0))) 1936fdfcacbSDavid Xu return (ETIMEDOUT); 1948d6a11a0SDavid Xu return _umtx_op_err(__DEVOLATILE(void *, mtx), 1958d6a11a0SDavid Xu shared ? UMTX_OP_WAIT_UINT : UMTX_OP_WAIT_UINT_PRIVATE, id, 0, 196d6e0eb0aSDavid Xu __DECONST(void*, timeout)); 1976fdfcacbSDavid Xu } 1986fdfcacbSDavid Xu 1996fdfcacbSDavid Xu int 200*d1078b0bSDavid Xu _thr_umtx_timedwait_uint(volatile u_int *mtx, u_int id, int clockid, 201*d1078b0bSDavid Xu const struct timespec *abstime, int shared) 202*d1078b0bSDavid Xu { 203*d1078b0bSDavid Xu struct timespec ts, ts2, *tsp; 204*d1078b0bSDavid Xu 205*d1078b0bSDavid Xu if (abstime != NULL) { 206*d1078b0bSDavid Xu clock_gettime(clockid, &ts); 207*d1078b0bSDavid Xu TIMESPEC_SUB(&ts2, abstime, &ts); 208*d1078b0bSDavid Xu if (ts2.tv_sec < 0 || ts2.tv_nsec <= 0) 209*d1078b0bSDavid Xu return (ETIMEDOUT); 210*d1078b0bSDavid Xu tsp = &ts2; 211*d1078b0bSDavid Xu } else { 212*d1078b0bSDavid Xu tsp = NULL; 213*d1078b0bSDavid Xu } 214*d1078b0bSDavid Xu return _umtx_op_err(__DEVOLATILE(void *, mtx), 215*d1078b0bSDavid Xu shared ? UMTX_OP_WAIT_UINT : UMTX_OP_WAIT_UINT_PRIVATE, id, NULL, 216*d1078b0bSDavid Xu tsp); 217*d1078b0bSDavid Xu } 218*d1078b0bSDavid Xu 219*d1078b0bSDavid Xu int 2208d6a11a0SDavid Xu _thr_umtx_wake(volatile void *mtx, int nr_wakeup, int shared) 221a091d823SDavid Xu { 2228d6a11a0SDavid Xu return _umtx_op_err(__DEVOLATILE(void *, mtx), shared ? UMTX_OP_WAKE : UMTX_OP_WAKE_PRIVATE, 223d6e0eb0aSDavid Xu nr_wakeup, 0, 0); 224a091d823SDavid Xu } 2252bd2c907SDavid Xu 226670b44d6SDavid Xu void 227670b44d6SDavid Xu _thr_ucond_init(struct ucond *cv) 228670b44d6SDavid Xu { 229670b44d6SDavid Xu bzero(cv, sizeof(struct ucond)); 230670b44d6SDavid Xu } 231670b44d6SDavid Xu 2322bd2c907SDavid Xu int 2332bd2c907SDavid Xu _thr_ucond_wait(struct ucond *cv, struct umutex *m, 2342bd2c907SDavid Xu const struct timespec *timeout, int check_unparking) 2352bd2c907SDavid Xu { 2362bd2c907SDavid Xu if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 && 2372bd2c907SDavid Xu timeout->tv_nsec <= 0))) { 2387de1ecefSDavid Xu struct pthread *curthread = _get_curthread(); 2397de1ecefSDavid Xu _thr_umutex_unlock(m, TID(curthread)); 2402bd2c907SDavid Xu return (ETIMEDOUT); 2412bd2c907SDavid Xu } 242d6e0eb0aSDavid Xu return _umtx_op_err(cv, UMTX_OP_CV_WAIT, 2433c61d00aSDavid Xu check_unparking ? UMTX_CHECK_UNPARKING : 0, 244d6e0eb0aSDavid Xu m, __DECONST(void*, timeout)); 2452bd2c907SDavid Xu } 2462bd2c907SDavid Xu 2472bd2c907SDavid Xu int 2482bd2c907SDavid Xu _thr_ucond_signal(struct ucond *cv) 2492bd2c907SDavid Xu { 250347126a2SDavid Xu if (!cv->c_has_waiters) 251347126a2SDavid Xu return (0); 252d6e0eb0aSDavid Xu return _umtx_op_err(cv, UMTX_OP_CV_SIGNAL, 0, NULL, NULL); 2532bd2c907SDavid Xu } 2542bd2c907SDavid Xu 2552bd2c907SDavid Xu int 2562bd2c907SDavid Xu _thr_ucond_broadcast(struct ucond *cv) 2572bd2c907SDavid Xu { 258347126a2SDavid Xu if (!cv->c_has_waiters) 259347126a2SDavid Xu return (0); 260d6e0eb0aSDavid Xu return _umtx_op_err(cv, UMTX_OP_CV_BROADCAST, 0, NULL, NULL); 2612bd2c907SDavid Xu } 2628bf1a48cSDavid Xu 2638bf1a48cSDavid Xu int 2648bf1a48cSDavid Xu __thr_rwlock_rdlock(struct urwlock *rwlock, int flags, struct timespec *tsp) 2658bf1a48cSDavid Xu { 266d6e0eb0aSDavid Xu return _umtx_op_err(rwlock, UMTX_OP_RW_RDLOCK, flags, NULL, tsp); 2678bf1a48cSDavid Xu } 2688bf1a48cSDavid Xu 2698bf1a48cSDavid Xu int 2708bf1a48cSDavid Xu __thr_rwlock_wrlock(struct urwlock *rwlock, struct timespec *tsp) 2718bf1a48cSDavid Xu { 272d6e0eb0aSDavid Xu return _umtx_op_err(rwlock, UMTX_OP_RW_WRLOCK, 0, NULL, tsp); 2738bf1a48cSDavid Xu } 2748bf1a48cSDavid Xu 2758bf1a48cSDavid Xu int 2768bf1a48cSDavid Xu __thr_rwlock_unlock(struct urwlock *rwlock) 2778bf1a48cSDavid Xu { 278d6e0eb0aSDavid Xu return _umtx_op_err(rwlock, UMTX_OP_RW_UNLOCK, 0, NULL, NULL); 2798bf1a48cSDavid Xu } 28002c3c858SDavid Xu 28102c3c858SDavid Xu void 28202c3c858SDavid Xu _thr_rwl_rdlock(struct urwlock *rwlock) 28302c3c858SDavid Xu { 28402c3c858SDavid Xu int ret; 28502c3c858SDavid Xu 28602c3c858SDavid Xu for (;;) { 28702c3c858SDavid Xu if (_thr_rwlock_tryrdlock(rwlock, URWLOCK_PREFER_READER) == 0) 28802c3c858SDavid Xu return; 28902c3c858SDavid Xu ret = __thr_rwlock_rdlock(rwlock, URWLOCK_PREFER_READER, NULL); 29002c3c858SDavid Xu if (ret == 0) 29102c3c858SDavid Xu return; 29202c3c858SDavid Xu if (ret != EINTR) 29302c3c858SDavid Xu PANIC("rdlock error"); 29402c3c858SDavid Xu } 29502c3c858SDavid Xu } 29602c3c858SDavid Xu 29702c3c858SDavid Xu void 29802c3c858SDavid Xu _thr_rwl_wrlock(struct urwlock *rwlock) 29902c3c858SDavid Xu { 30002c3c858SDavid Xu int ret; 30102c3c858SDavid Xu 30202c3c858SDavid Xu for (;;) { 30302c3c858SDavid Xu if (_thr_rwlock_trywrlock(rwlock) == 0) 30402c3c858SDavid Xu return; 30502c3c858SDavid Xu ret = __thr_rwlock_wrlock(rwlock, NULL); 30602c3c858SDavid Xu if (ret == 0) 30702c3c858SDavid Xu return; 30802c3c858SDavid Xu if (ret != EINTR) 30902c3c858SDavid Xu PANIC("wrlock error"); 31002c3c858SDavid Xu } 31102c3c858SDavid Xu } 31202c3c858SDavid Xu 31302c3c858SDavid Xu void 31402c3c858SDavid Xu _thr_rwl_unlock(struct urwlock *rwlock) 31502c3c858SDavid Xu { 31602c3c858SDavid Xu if (_thr_rwlock_unlock(rwlock)) 31702c3c858SDavid Xu PANIC("unlock error"); 31802c3c858SDavid Xu } 319