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 50a091d823SDavid Xu int 517de1ecefSDavid Xu __thr_umutex_lock(struct umutex *mtx, uint32_t id) 52cf13ecdaSDavid Xu { 537de1ecefSDavid Xu uint32_t owner; 547de1ecefSDavid Xu 557de1ecefSDavid Xu if ((mtx->m_flags & (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) == 0) { 567de1ecefSDavid Xu for (;;) { 577de1ecefSDavid Xu /* wait in kernel */ 587de1ecefSDavid Xu _umtx_op_err(mtx, UMTX_OP_MUTEX_WAIT, 0, 0, 0); 597de1ecefSDavid Xu 607de1ecefSDavid Xu owner = mtx->m_owner; 617de1ecefSDavid Xu if ((owner & ~UMUTEX_CONTESTED) == 0 && 627de1ecefSDavid Xu atomic_cmpset_acq_32(&mtx->m_owner, owner, id|owner)) 637de1ecefSDavid Xu return (0); 647de1ecefSDavid Xu } 657de1ecefSDavid Xu } 667de1ecefSDavid Xu 67d6e0eb0aSDavid Xu return _umtx_op_err(mtx, UMTX_OP_MUTEX_LOCK, 0, 0, 0); 68cf13ecdaSDavid Xu } 69cf13ecdaSDavid Xu 70cf13ecdaSDavid Xu int 717de1ecefSDavid Xu __thr_umutex_timedlock(struct umutex *mtx, uint32_t id, 727de1ecefSDavid Xu const struct timespec *ets) 73cf13ecdaSDavid Xu { 747de1ecefSDavid Xu struct timespec timo, cts; 757de1ecefSDavid Xu uint32_t owner; 767de1ecefSDavid Xu int ret; 777de1ecefSDavid Xu 787de1ecefSDavid Xu clock_gettime(CLOCK_REALTIME, &cts); 797de1ecefSDavid Xu TIMESPEC_SUB(&timo, ets, &cts); 807de1ecefSDavid Xu 817de1ecefSDavid Xu if (timo.tv_sec < 0) 82cf13ecdaSDavid Xu return (ETIMEDOUT); 837de1ecefSDavid Xu 847de1ecefSDavid Xu for (;;) { 857de1ecefSDavid Xu if ((mtx->m_flags & (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) == 0) { 867de1ecefSDavid Xu 877de1ecefSDavid Xu /* wait in kernel */ 887de1ecefSDavid Xu ret = _umtx_op_err(mtx, UMTX_OP_MUTEX_WAIT, 0, 0, &timo); 897de1ecefSDavid Xu 907de1ecefSDavid Xu /* now try to lock it */ 917de1ecefSDavid Xu owner = mtx->m_owner; 927de1ecefSDavid Xu if ((owner & ~UMUTEX_CONTESTED) == 0 && 937de1ecefSDavid Xu atomic_cmpset_acq_32(&mtx->m_owner, owner, id|owner)) 947de1ecefSDavid Xu return (0); 957de1ecefSDavid Xu } else { 967de1ecefSDavid Xu ret = _umtx_op_err(mtx, UMTX_OP_MUTEX_LOCK, 0, 0, &timo); 977de1ecefSDavid Xu if (ret == 0) 987de1ecefSDavid Xu break; 997de1ecefSDavid Xu } 1007de1ecefSDavid Xu if (ret == ETIMEDOUT) 1017de1ecefSDavid Xu break; 1027de1ecefSDavid Xu clock_gettime(CLOCK_REALTIME, &cts); 1037de1ecefSDavid Xu TIMESPEC_SUB(&timo, ets, &cts); 1047de1ecefSDavid Xu if (timo.tv_sec < 0 || (timo.tv_sec == 0 && timo.tv_nsec == 0)) { 1057de1ecefSDavid Xu ret = ETIMEDOUT; 1067de1ecefSDavid Xu break; 1077de1ecefSDavid Xu } 1087de1ecefSDavid Xu } 1097de1ecefSDavid Xu return (ret); 110cf13ecdaSDavid Xu } 111cf13ecdaSDavid Xu 112cf13ecdaSDavid Xu int 1137de1ecefSDavid Xu __thr_umutex_unlock(struct umutex *mtx, uint32_t id) 114cf13ecdaSDavid Xu { 115e4f141b5SMarcel Moolenaar #ifndef __ia64__ 116e4f141b5SMarcel Moolenaar /* XXX this logic has a race-condition on ia64. */ 1177de1ecefSDavid Xu if ((mtx->m_flags & (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) == 0) { 1187de1ecefSDavid Xu atomic_cmpset_rel_32(&mtx->m_owner, id | UMUTEX_CONTESTED, UMUTEX_CONTESTED); 1197de1ecefSDavid Xu return _umtx_op_err(mtx, UMTX_OP_MUTEX_WAKE, 0, 0, 0); 1207de1ecefSDavid Xu } 121e4f141b5SMarcel Moolenaar #endif /* __ia64__ */ 122d6e0eb0aSDavid Xu return _umtx_op_err(mtx, UMTX_OP_MUTEX_UNLOCK, 0, 0, 0); 123cf13ecdaSDavid Xu } 124cf13ecdaSDavid Xu 125cf13ecdaSDavid Xu int 1268042f26dSDavid Xu __thr_umutex_trylock(struct umutex *mtx) 127cf13ecdaSDavid Xu { 128d6e0eb0aSDavid Xu return _umtx_op_err(mtx, UMTX_OP_MUTEX_TRYLOCK, 0, 0, 0); 129cf13ecdaSDavid Xu } 130cf13ecdaSDavid Xu 131cf13ecdaSDavid Xu int 132cf13ecdaSDavid Xu __thr_umutex_set_ceiling(struct umutex *mtx, uint32_t ceiling, 133cf13ecdaSDavid Xu uint32_t *oldceiling) 134cf13ecdaSDavid Xu { 135d6e0eb0aSDavid Xu return _umtx_op_err(mtx, UMTX_OP_SET_CEILING, ceiling, oldceiling, 0); 136cf13ecdaSDavid Xu } 137cf13ecdaSDavid Xu 138cf13ecdaSDavid Xu int 1396fdfcacbSDavid Xu _thr_umtx_wait(volatile long *mtx, long id, const struct timespec *timeout) 140a091d823SDavid Xu { 141a091d823SDavid Xu if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 && 142a091d823SDavid Xu timeout->tv_nsec <= 0))) 143a091d823SDavid Xu return (ETIMEDOUT); 144d6e0eb0aSDavid Xu return _umtx_op_err(__DEVOLATILE(void *, mtx), UMTX_OP_WAIT, id, 0, 145d6e0eb0aSDavid Xu __DECONST(void*, timeout)); 146a091d823SDavid Xu } 147a091d823SDavid Xu 148a091d823SDavid Xu int 1498d6a11a0SDavid Xu _thr_umtx_wait_uint(volatile u_int *mtx, u_int id, const struct timespec *timeout, int shared) 1506fdfcacbSDavid Xu { 1516fdfcacbSDavid Xu if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 && 1526fdfcacbSDavid Xu timeout->tv_nsec <= 0))) 1536fdfcacbSDavid Xu return (ETIMEDOUT); 1548d6a11a0SDavid Xu return _umtx_op_err(__DEVOLATILE(void *, mtx), 1558d6a11a0SDavid Xu shared ? UMTX_OP_WAIT_UINT : UMTX_OP_WAIT_UINT_PRIVATE, id, 0, 156d6e0eb0aSDavid Xu __DECONST(void*, timeout)); 1576fdfcacbSDavid Xu } 1586fdfcacbSDavid Xu 1596fdfcacbSDavid Xu int 1608d6a11a0SDavid Xu _thr_umtx_wake(volatile void *mtx, int nr_wakeup, int shared) 161a091d823SDavid Xu { 1628d6a11a0SDavid Xu return _umtx_op_err(__DEVOLATILE(void *, mtx), shared ? UMTX_OP_WAKE : UMTX_OP_WAKE_PRIVATE, 163d6e0eb0aSDavid Xu nr_wakeup, 0, 0); 164a091d823SDavid Xu } 1652bd2c907SDavid Xu 166670b44d6SDavid Xu void 167670b44d6SDavid Xu _thr_ucond_init(struct ucond *cv) 168670b44d6SDavid Xu { 169670b44d6SDavid Xu bzero(cv, sizeof(struct ucond)); 170670b44d6SDavid Xu } 171670b44d6SDavid Xu 1722bd2c907SDavid Xu int 1732bd2c907SDavid Xu _thr_ucond_wait(struct ucond *cv, struct umutex *m, 1742bd2c907SDavid Xu const struct timespec *timeout, int check_unparking) 1752bd2c907SDavid Xu { 1762bd2c907SDavid Xu if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 && 1772bd2c907SDavid Xu timeout->tv_nsec <= 0))) { 1787de1ecefSDavid Xu struct pthread *curthread = _get_curthread(); 1797de1ecefSDavid Xu _thr_umutex_unlock(m, TID(curthread)); 1802bd2c907SDavid Xu return (ETIMEDOUT); 1812bd2c907SDavid Xu } 182d6e0eb0aSDavid Xu return _umtx_op_err(cv, UMTX_OP_CV_WAIT, 1833c61d00aSDavid Xu check_unparking ? UMTX_CHECK_UNPARKING : 0, 184d6e0eb0aSDavid Xu m, __DECONST(void*, timeout)); 1852bd2c907SDavid Xu } 1862bd2c907SDavid Xu 1872bd2c907SDavid Xu int 1882bd2c907SDavid Xu _thr_ucond_signal(struct ucond *cv) 1892bd2c907SDavid Xu { 190347126a2SDavid Xu if (!cv->c_has_waiters) 191347126a2SDavid Xu return (0); 192d6e0eb0aSDavid Xu return _umtx_op_err(cv, UMTX_OP_CV_SIGNAL, 0, NULL, NULL); 1932bd2c907SDavid Xu } 1942bd2c907SDavid Xu 1952bd2c907SDavid Xu int 1962bd2c907SDavid Xu _thr_ucond_broadcast(struct ucond *cv) 1972bd2c907SDavid Xu { 198347126a2SDavid Xu if (!cv->c_has_waiters) 199347126a2SDavid Xu return (0); 200d6e0eb0aSDavid Xu return _umtx_op_err(cv, UMTX_OP_CV_BROADCAST, 0, NULL, NULL); 2012bd2c907SDavid Xu } 2028bf1a48cSDavid Xu 2038bf1a48cSDavid Xu int 2048bf1a48cSDavid Xu __thr_rwlock_rdlock(struct urwlock *rwlock, int flags, struct timespec *tsp) 2058bf1a48cSDavid Xu { 206d6e0eb0aSDavid Xu return _umtx_op_err(rwlock, UMTX_OP_RW_RDLOCK, flags, NULL, tsp); 2078bf1a48cSDavid Xu } 2088bf1a48cSDavid Xu 2098bf1a48cSDavid Xu int 2108bf1a48cSDavid Xu __thr_rwlock_wrlock(struct urwlock *rwlock, struct timespec *tsp) 2118bf1a48cSDavid Xu { 212d6e0eb0aSDavid Xu return _umtx_op_err(rwlock, UMTX_OP_RW_WRLOCK, 0, NULL, tsp); 2138bf1a48cSDavid Xu } 2148bf1a48cSDavid Xu 2158bf1a48cSDavid Xu int 2168bf1a48cSDavid Xu __thr_rwlock_unlock(struct urwlock *rwlock) 2178bf1a48cSDavid Xu { 218d6e0eb0aSDavid Xu return _umtx_op_err(rwlock, UMTX_OP_RW_UNLOCK, 0, NULL, NULL); 2198bf1a48cSDavid Xu } 220*02c3c858SDavid Xu 221*02c3c858SDavid Xu void 222*02c3c858SDavid Xu _thr_rwl_rdlock(struct urwlock *rwlock) 223*02c3c858SDavid Xu { 224*02c3c858SDavid Xu int ret; 225*02c3c858SDavid Xu 226*02c3c858SDavid Xu for (;;) { 227*02c3c858SDavid Xu if (_thr_rwlock_tryrdlock(rwlock, URWLOCK_PREFER_READER) == 0) 228*02c3c858SDavid Xu return; 229*02c3c858SDavid Xu ret = __thr_rwlock_rdlock(rwlock, URWLOCK_PREFER_READER, NULL); 230*02c3c858SDavid Xu if (ret == 0) 231*02c3c858SDavid Xu return; 232*02c3c858SDavid Xu if (ret != EINTR) 233*02c3c858SDavid Xu PANIC("rdlock error"); 234*02c3c858SDavid Xu } 235*02c3c858SDavid Xu } 236*02c3c858SDavid Xu 237*02c3c858SDavid Xu void 238*02c3c858SDavid Xu _thr_rwl_wrlock(struct urwlock *rwlock) 239*02c3c858SDavid Xu { 240*02c3c858SDavid Xu int ret; 241*02c3c858SDavid Xu 242*02c3c858SDavid Xu for (;;) { 243*02c3c858SDavid Xu if (_thr_rwlock_trywrlock(rwlock) == 0) 244*02c3c858SDavid Xu return; 245*02c3c858SDavid Xu ret = __thr_rwlock_wrlock(rwlock, NULL); 246*02c3c858SDavid Xu if (ret == 0) 247*02c3c858SDavid Xu return; 248*02c3c858SDavid Xu if (ret != EINTR) 249*02c3c858SDavid Xu PANIC("wrlock error"); 250*02c3c858SDavid Xu } 251*02c3c858SDavid Xu } 252*02c3c858SDavid Xu 253*02c3c858SDavid Xu void 254*02c3c858SDavid Xu _thr_rwl_unlock(struct urwlock *rwlock) 255*02c3c858SDavid Xu { 256*02c3c858SDavid Xu if (_thr_rwlock_unlock(rwlock)) 257*02c3c858SDavid Xu PANIC("unlock error"); 258*02c3c858SDavid Xu } 259