10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 54570Sraf * Common Development and Distribution License (the "License"). 64570Sraf * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 214570Sraf 220Sstevel@tonic-gate /* 236247Sraf * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 240Sstevel@tonic-gate * Use is subject to license terms. 250Sstevel@tonic-gate */ 260Sstevel@tonic-gate 270Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 280Sstevel@tonic-gate 290Sstevel@tonic-gate #include "lint.h" 300Sstevel@tonic-gate #include "thr_uberdata.h" 310Sstevel@tonic-gate #include <sys/sdt.h> 320Sstevel@tonic-gate 330Sstevel@tonic-gate #define TRY_FLAG 0x10 340Sstevel@tonic-gate #define READ_LOCK 0 350Sstevel@tonic-gate #define WRITE_LOCK 1 360Sstevel@tonic-gate #define READ_LOCK_TRY (READ_LOCK | TRY_FLAG) 370Sstevel@tonic-gate #define WRITE_LOCK_TRY (WRITE_LOCK | TRY_FLAG) 380Sstevel@tonic-gate 390Sstevel@tonic-gate #define NLOCKS 4 /* initial number of readlock_t structs allocated */ 400Sstevel@tonic-gate 414570Sraf #define ASSERT_CONSISTENT_STATE(readers) \ 424570Sraf ASSERT(!((readers) & URW_WRITE_LOCKED) || \ 434570Sraf ((readers) & ~URW_HAS_WAITERS) == URW_WRITE_LOCKED) 444570Sraf 450Sstevel@tonic-gate /* 460Sstevel@tonic-gate * Find/allocate an entry for rwlp in our array of rwlocks held for reading. 474570Sraf * We must be deferring signals for this to be safe. 484574Sraf * Else if we are returning an entry with ul_rdlockcnt == 0, 494570Sraf * it could be reassigned behind our back in a signal handler. 500Sstevel@tonic-gate */ 510Sstevel@tonic-gate static readlock_t * 520Sstevel@tonic-gate rwl_entry(rwlock_t *rwlp) 530Sstevel@tonic-gate { 540Sstevel@tonic-gate ulwp_t *self = curthread; 550Sstevel@tonic-gate readlock_t *remembered = NULL; 560Sstevel@tonic-gate readlock_t *readlockp; 570Sstevel@tonic-gate uint_t nlocks; 580Sstevel@tonic-gate 594570Sraf /* we must be deferring signals */ 604570Sraf ASSERT((self->ul_critical + self->ul_sigdefer) != 0); 614570Sraf 624574Sraf if ((nlocks = self->ul_rdlockcnt) != 0) 630Sstevel@tonic-gate readlockp = self->ul_readlock.array; 640Sstevel@tonic-gate else { 650Sstevel@tonic-gate nlocks = 1; 660Sstevel@tonic-gate readlockp = &self->ul_readlock.single; 670Sstevel@tonic-gate } 680Sstevel@tonic-gate 690Sstevel@tonic-gate for (; nlocks; nlocks--, readlockp++) { 700Sstevel@tonic-gate if (readlockp->rd_rwlock == rwlp) 710Sstevel@tonic-gate return (readlockp); 720Sstevel@tonic-gate if (readlockp->rd_count == 0 && remembered == NULL) 730Sstevel@tonic-gate remembered = readlockp; 740Sstevel@tonic-gate } 750Sstevel@tonic-gate if (remembered != NULL) { 760Sstevel@tonic-gate remembered->rd_rwlock = rwlp; 770Sstevel@tonic-gate return (remembered); 780Sstevel@tonic-gate } 790Sstevel@tonic-gate 800Sstevel@tonic-gate /* 810Sstevel@tonic-gate * No entry available. Allocate more space, converting the single 820Sstevel@tonic-gate * readlock_t entry into an array of readlock_t entries if necessary. 830Sstevel@tonic-gate */ 844574Sraf if ((nlocks = self->ul_rdlockcnt) == 0) { 850Sstevel@tonic-gate /* 860Sstevel@tonic-gate * Initial allocation of the readlock_t array. 870Sstevel@tonic-gate * Convert the single entry into an array. 880Sstevel@tonic-gate */ 894574Sraf self->ul_rdlockcnt = nlocks = NLOCKS; 900Sstevel@tonic-gate readlockp = lmalloc(nlocks * sizeof (readlock_t)); 910Sstevel@tonic-gate /* 920Sstevel@tonic-gate * The single readlock_t becomes the first entry in the array. 930Sstevel@tonic-gate */ 940Sstevel@tonic-gate *readlockp = self->ul_readlock.single; 950Sstevel@tonic-gate self->ul_readlock.single.rd_count = 0; 960Sstevel@tonic-gate self->ul_readlock.array = readlockp; 970Sstevel@tonic-gate /* 980Sstevel@tonic-gate * Return the next available entry in the array. 990Sstevel@tonic-gate */ 1000Sstevel@tonic-gate (++readlockp)->rd_rwlock = rwlp; 1010Sstevel@tonic-gate return (readlockp); 1020Sstevel@tonic-gate } 1030Sstevel@tonic-gate /* 1040Sstevel@tonic-gate * Reallocate the array, double the size each time. 1050Sstevel@tonic-gate */ 1060Sstevel@tonic-gate readlockp = lmalloc(nlocks * 2 * sizeof (readlock_t)); 1076515Sraf (void) memcpy(readlockp, self->ul_readlock.array, 1086247Sraf nlocks * sizeof (readlock_t)); 1090Sstevel@tonic-gate lfree(self->ul_readlock.array, nlocks * sizeof (readlock_t)); 1100Sstevel@tonic-gate self->ul_readlock.array = readlockp; 1114574Sraf self->ul_rdlockcnt *= 2; 1120Sstevel@tonic-gate /* 1130Sstevel@tonic-gate * Return the next available entry in the newly allocated array. 1140Sstevel@tonic-gate */ 1150Sstevel@tonic-gate (readlockp += nlocks)->rd_rwlock = rwlp; 1160Sstevel@tonic-gate return (readlockp); 1170Sstevel@tonic-gate } 1180Sstevel@tonic-gate 1190Sstevel@tonic-gate /* 1200Sstevel@tonic-gate * Free the array of rwlocks held for reading. 1210Sstevel@tonic-gate */ 1220Sstevel@tonic-gate void 1230Sstevel@tonic-gate rwl_free(ulwp_t *ulwp) 1240Sstevel@tonic-gate { 1250Sstevel@tonic-gate uint_t nlocks; 1260Sstevel@tonic-gate 1274574Sraf if ((nlocks = ulwp->ul_rdlockcnt) != 0) 1280Sstevel@tonic-gate lfree(ulwp->ul_readlock.array, nlocks * sizeof (readlock_t)); 1294574Sraf ulwp->ul_rdlockcnt = 0; 1300Sstevel@tonic-gate ulwp->ul_readlock.single.rd_rwlock = NULL; 1310Sstevel@tonic-gate ulwp->ul_readlock.single.rd_count = 0; 1320Sstevel@tonic-gate } 1330Sstevel@tonic-gate 1340Sstevel@tonic-gate /* 1350Sstevel@tonic-gate * Check if a reader version of the lock is held by the current thread. 1360Sstevel@tonic-gate */ 137*6812Sraf #pragma weak _rw_read_held = rw_read_held 1380Sstevel@tonic-gate int 139*6812Sraf rw_read_held(rwlock_t *rwlp) 1400Sstevel@tonic-gate { 1414570Sraf volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers; 1424570Sraf uint32_t readers; 1434570Sraf ulwp_t *self = curthread; 1440Sstevel@tonic-gate readlock_t *readlockp; 1450Sstevel@tonic-gate uint_t nlocks; 1464570Sraf int rval = 0; 1470Sstevel@tonic-gate 1484570Sraf no_preempt(self); 1494570Sraf 1504570Sraf readers = *rwstate; 1514570Sraf ASSERT_CONSISTENT_STATE(readers); 1524570Sraf if (!(readers & URW_WRITE_LOCKED) && 1534570Sraf (readers & URW_READERS_MASK) != 0) { 1544570Sraf /* 1554570Sraf * The lock is held for reading by some thread. 1564570Sraf * Search our array of rwlocks held for reading for a match. 1574570Sraf */ 1584574Sraf if ((nlocks = self->ul_rdlockcnt) != 0) 1594570Sraf readlockp = self->ul_readlock.array; 1604570Sraf else { 1614570Sraf nlocks = 1; 1624570Sraf readlockp = &self->ul_readlock.single; 1634570Sraf } 1644570Sraf for (; nlocks; nlocks--, readlockp++) { 1654570Sraf if (readlockp->rd_rwlock == rwlp) { 1664570Sraf if (readlockp->rd_count) 1674570Sraf rval = 1; 1684570Sraf break; 1694570Sraf } 1704570Sraf } 1710Sstevel@tonic-gate } 1720Sstevel@tonic-gate 1734570Sraf preempt(self); 1744570Sraf return (rval); 1750Sstevel@tonic-gate } 1760Sstevel@tonic-gate 1770Sstevel@tonic-gate /* 1780Sstevel@tonic-gate * Check if a writer version of the lock is held by the current thread. 1790Sstevel@tonic-gate */ 180*6812Sraf #pragma weak _rw_write_held = rw_write_held 1810Sstevel@tonic-gate int 182*6812Sraf rw_write_held(rwlock_t *rwlp) 1830Sstevel@tonic-gate { 1844570Sraf volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers; 1854570Sraf uint32_t readers; 1860Sstevel@tonic-gate ulwp_t *self = curthread; 1874570Sraf int rval; 1884570Sraf 1894570Sraf no_preempt(self); 1900Sstevel@tonic-gate 1914570Sraf readers = *rwstate; 1924570Sraf ASSERT_CONSISTENT_STATE(readers); 1934570Sraf rval = ((readers & URW_WRITE_LOCKED) && 1944570Sraf rwlp->rwlock_owner == (uintptr_t)self && 1954570Sraf (rwlp->rwlock_type == USYNC_THREAD || 1964570Sraf rwlp->rwlock_ownerpid == self->ul_uberdata->pid)); 1970Sstevel@tonic-gate 1984570Sraf preempt(self); 1994570Sraf return (rval); 2000Sstevel@tonic-gate } 2010Sstevel@tonic-gate 202*6812Sraf #pragma weak _rwlock_init = rwlock_init 2030Sstevel@tonic-gate /* ARGSUSED2 */ 2040Sstevel@tonic-gate int 205*6812Sraf rwlock_init(rwlock_t *rwlp, int type, void *arg) 2060Sstevel@tonic-gate { 2070Sstevel@tonic-gate if (type != USYNC_THREAD && type != USYNC_PROCESS) 2080Sstevel@tonic-gate return (EINVAL); 2090Sstevel@tonic-gate /* 2100Sstevel@tonic-gate * Once reinitialized, we can no longer be holding a read or write lock. 2110Sstevel@tonic-gate * We can do nothing about other threads that are holding read locks. 2120Sstevel@tonic-gate */ 2134570Sraf sigoff(curthread); 2144570Sraf rwl_entry(rwlp)->rd_count = 0; 2154570Sraf sigon(curthread); 2166515Sraf (void) memset(rwlp, 0, sizeof (*rwlp)); 2170Sstevel@tonic-gate rwlp->rwlock_type = (uint16_t)type; 2180Sstevel@tonic-gate rwlp->rwlock_magic = RWL_MAGIC; 2190Sstevel@tonic-gate rwlp->mutex.mutex_type = (uint8_t)type; 2200Sstevel@tonic-gate rwlp->mutex.mutex_flag = LOCK_INITED; 2210Sstevel@tonic-gate rwlp->mutex.mutex_magic = MUTEX_MAGIC; 2220Sstevel@tonic-gate return (0); 2230Sstevel@tonic-gate } 2240Sstevel@tonic-gate 225*6812Sraf #pragma weak pthread_rwlock_destroy = rwlock_destroy 226*6812Sraf #pragma weak _rwlock_destroy = rwlock_destroy 2270Sstevel@tonic-gate int 228*6812Sraf rwlock_destroy(rwlock_t *rwlp) 2290Sstevel@tonic-gate { 2300Sstevel@tonic-gate /* 2310Sstevel@tonic-gate * Once destroyed, we can no longer be holding a read or write lock. 2320Sstevel@tonic-gate * We can do nothing about other threads that are holding read locks. 2330Sstevel@tonic-gate */ 2344570Sraf sigoff(curthread); 2354570Sraf rwl_entry(rwlp)->rd_count = 0; 2364570Sraf sigon(curthread); 2370Sstevel@tonic-gate rwlp->rwlock_magic = 0; 2380Sstevel@tonic-gate tdb_sync_obj_deregister(rwlp); 2390Sstevel@tonic-gate return (0); 2400Sstevel@tonic-gate } 2410Sstevel@tonic-gate 2420Sstevel@tonic-gate /* 2434570Sraf * Attempt to acquire a readers lock. Return true on success. 2444570Sraf */ 2454570Sraf static int 2464570Sraf read_lock_try(rwlock_t *rwlp, int ignore_waiters_flag) 2474570Sraf { 2484570Sraf volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers; 2494570Sraf uint32_t mask = ignore_waiters_flag? 2506247Sraf URW_WRITE_LOCKED : (URW_HAS_WAITERS | URW_WRITE_LOCKED); 2514570Sraf uint32_t readers; 2524570Sraf ulwp_t *self = curthread; 2534570Sraf 2544570Sraf no_preempt(self); 2554570Sraf while (((readers = *rwstate) & mask) == 0) { 2564570Sraf if (atomic_cas_32(rwstate, readers, readers + 1) == readers) { 2574570Sraf preempt(self); 2584570Sraf return (1); 2594570Sraf } 2604570Sraf } 2614570Sraf preempt(self); 2624570Sraf return (0); 2634570Sraf } 2644570Sraf 2654570Sraf /* 2664570Sraf * Attempt to release a reader lock. Return true on success. 2674570Sraf */ 2684570Sraf static int 2694570Sraf read_unlock_try(rwlock_t *rwlp) 2704570Sraf { 2714570Sraf volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers; 2724570Sraf uint32_t readers; 2734570Sraf ulwp_t *self = curthread; 2744570Sraf 2754570Sraf no_preempt(self); 2764570Sraf while (((readers = *rwstate) & URW_HAS_WAITERS) == 0) { 2774570Sraf if (atomic_cas_32(rwstate, readers, readers - 1) == readers) { 2784570Sraf preempt(self); 2794570Sraf return (1); 2804570Sraf } 2814570Sraf } 2824570Sraf preempt(self); 2834570Sraf return (0); 2844570Sraf } 2854570Sraf 2864570Sraf /* 2874570Sraf * Attempt to acquire a writer lock. Return true on success. 2884570Sraf */ 2894570Sraf static int 2904570Sraf write_lock_try(rwlock_t *rwlp, int ignore_waiters_flag) 2914570Sraf { 2924570Sraf volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers; 2934570Sraf uint32_t mask = ignore_waiters_flag? 2946247Sraf (URW_WRITE_LOCKED | URW_READERS_MASK) : 2956247Sraf (URW_HAS_WAITERS | URW_WRITE_LOCKED | URW_READERS_MASK); 2964570Sraf ulwp_t *self = curthread; 2974570Sraf uint32_t readers; 2984570Sraf 2994570Sraf no_preempt(self); 3004570Sraf while (((readers = *rwstate) & mask) == 0) { 3014570Sraf if (atomic_cas_32(rwstate, readers, readers | URW_WRITE_LOCKED) 3024570Sraf == readers) { 3034570Sraf preempt(self); 3044570Sraf return (1); 3054570Sraf } 3064570Sraf } 3074570Sraf preempt(self); 3084570Sraf return (0); 3094570Sraf } 3104570Sraf 3114570Sraf /* 3124570Sraf * Attempt to release a writer lock. Return true on success. 3134570Sraf */ 3144570Sraf static int 3154570Sraf write_unlock_try(rwlock_t *rwlp) 3164570Sraf { 3174570Sraf volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers; 3184570Sraf uint32_t readers; 3194570Sraf ulwp_t *self = curthread; 3204570Sraf 3214570Sraf no_preempt(self); 3224570Sraf while (((readers = *rwstate) & URW_HAS_WAITERS) == 0) { 3234570Sraf if (atomic_cas_32(rwstate, readers, 0) == readers) { 3244570Sraf preempt(self); 3254570Sraf return (1); 3264570Sraf } 3274570Sraf } 3284570Sraf preempt(self); 3294570Sraf return (0); 3304570Sraf } 3314570Sraf 3324570Sraf /* 3334570Sraf * Wake up thread(s) sleeping on the rwlock queue and then 3340Sstevel@tonic-gate * drop the queue lock. Return non-zero if we wake up someone. 3354570Sraf * This is called when a thread releases a lock that appears to have waiters. 3360Sstevel@tonic-gate */ 3370Sstevel@tonic-gate static int 3380Sstevel@tonic-gate rw_queue_release(queue_head_t *qp, rwlock_t *rwlp) 3390Sstevel@tonic-gate { 3404570Sraf volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers; 3414570Sraf uint32_t readers; 3424570Sraf uint32_t writers; 3434570Sraf ulwp_t **ulwpp; 3440Sstevel@tonic-gate ulwp_t *ulwp; 3456247Sraf ulwp_t *prev; 3466247Sraf int nlwpid = 0; 3476247Sraf int more; 3486247Sraf int maxlwps = MAXLWPS; 3494570Sraf lwpid_t buffer[MAXLWPS]; 3504570Sraf lwpid_t *lwpid = buffer; 3514570Sraf 3524570Sraf readers = *rwstate; 3534570Sraf ASSERT_CONSISTENT_STATE(readers); 3544570Sraf if (!(readers & URW_HAS_WAITERS)) { 3554570Sraf queue_unlock(qp); 3564570Sraf return (0); 3574570Sraf } 3584570Sraf readers &= URW_READERS_MASK; 3594570Sraf writers = 0; 3600Sstevel@tonic-gate 3614570Sraf /* 3626247Sraf * Examine the queue of waiters in priority order and prepare 3636247Sraf * to wake up as many readers as we encounter before encountering 3646247Sraf * a writer. If the highest priority thread on the queue is a 3654570Sraf * writer, stop there and wake it up. 3664570Sraf * 3674570Sraf * We keep track of lwpids that are to be unparked in lwpid[]. 3684570Sraf * __lwp_unpark_all() is called to unpark all of them after 3694570Sraf * they have been removed from the sleep queue and the sleep 3704570Sraf * queue lock has been dropped. If we run out of space in our 3714570Sraf * on-stack buffer, we need to allocate more but we can't call 3724570Sraf * lmalloc() because we are holding a queue lock when the overflow 3734570Sraf * occurs and lmalloc() acquires a lock. We can't use alloca() 3744570Sraf * either because the application may have allocated a small 3754570Sraf * stack and we don't want to overrun the stack. So we call 3764570Sraf * alloc_lwpids() to allocate a bigger buffer using the mmap() 3774570Sraf * system call directly since that path acquires no locks. 3784570Sraf */ 3796247Sraf while ((ulwpp = queue_slot(qp, &prev, &more)) != NULL) { 3806247Sraf ulwp = *ulwpp; 3816247Sraf ASSERT(ulwp->ul_wchan == rwlp); 3824570Sraf if (ulwp->ul_writer) { 3834570Sraf if (writers != 0 || readers != 0) 3844570Sraf break; 3854570Sraf /* one writer to wake */ 3864570Sraf writers++; 3874570Sraf } else { 3884570Sraf if (writers != 0) 3894570Sraf break; 3904570Sraf /* at least one reader to wake */ 3914570Sraf readers++; 3924570Sraf if (nlwpid == maxlwps) 3934570Sraf lwpid = alloc_lwpids(lwpid, &nlwpid, &maxlwps); 3944570Sraf } 3956247Sraf queue_unlink(qp, ulwpp, prev); 3966247Sraf ulwp->ul_sleepq = NULL; 3976247Sraf ulwp->ul_wchan = NULL; 3984570Sraf lwpid[nlwpid++] = ulwp->ul_lwpid; 3990Sstevel@tonic-gate } 4006247Sraf if (ulwpp == NULL) 4014570Sraf atomic_and_32(rwstate, ~URW_HAS_WAITERS); 4024570Sraf if (nlwpid == 0) { 4034570Sraf queue_unlock(qp); 4044570Sraf } else { 4056247Sraf ulwp_t *self = curthread; 4064570Sraf no_preempt(self); 4074570Sraf queue_unlock(qp); 4084570Sraf if (nlwpid == 1) 4094570Sraf (void) __lwp_unpark(lwpid[0]); 4104570Sraf else 4114570Sraf (void) __lwp_unpark_all(lwpid, nlwpid); 4124570Sraf preempt(self); 4134570Sraf } 4144570Sraf if (lwpid != buffer) 4156515Sraf (void) munmap((caddr_t)lwpid, maxlwps * sizeof (lwpid_t)); 4164570Sraf return (nlwpid != 0); 4170Sstevel@tonic-gate } 4180Sstevel@tonic-gate 4190Sstevel@tonic-gate /* 4200Sstevel@tonic-gate * Common code for rdlock, timedrdlock, wrlock, timedwrlock, tryrdlock, 4210Sstevel@tonic-gate * and trywrlock for process-shared (USYNC_PROCESS) rwlocks. 4220Sstevel@tonic-gate * 4230Sstevel@tonic-gate * Note: if the lock appears to be contended we call __lwp_rwlock_rdlock() 4240Sstevel@tonic-gate * or __lwp_rwlock_wrlock() holding the mutex. These return with the mutex 4250Sstevel@tonic-gate * released, and if they need to sleep will release the mutex first. In the 4260Sstevel@tonic-gate * event of a spurious wakeup, these will return EAGAIN (because it is much 4270Sstevel@tonic-gate * easier for us to re-acquire the mutex here). 4280Sstevel@tonic-gate */ 4290Sstevel@tonic-gate int 4300Sstevel@tonic-gate shared_rwlock_lock(rwlock_t *rwlp, timespec_t *tsp, int rd_wr) 4310Sstevel@tonic-gate { 4324570Sraf volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers; 4334570Sraf mutex_t *mp = &rwlp->mutex; 4344570Sraf uint32_t readers; 4354570Sraf int try_flag; 4364570Sraf int error; 4374570Sraf 4384570Sraf try_flag = (rd_wr & TRY_FLAG); 4394570Sraf rd_wr &= ~TRY_FLAG; 4404570Sraf ASSERT(rd_wr == READ_LOCK || rd_wr == WRITE_LOCK); 4414570Sraf 4424570Sraf if (!try_flag) { 4434570Sraf DTRACE_PROBE2(plockstat, rw__block, rwlp, rd_wr); 4444570Sraf } 4454570Sraf 4464570Sraf do { 4474570Sraf if (try_flag && (*rwstate & URW_WRITE_LOCKED)) { 4484570Sraf error = EBUSY; 4494570Sraf break; 4504570Sraf } 4516515Sraf if ((error = mutex_lock(mp)) != 0) 4524570Sraf break; 4534570Sraf if (rd_wr == READ_LOCK) { 4544570Sraf if (read_lock_try(rwlp, 0)) { 4556515Sraf (void) mutex_unlock(mp); 4564570Sraf break; 4574570Sraf } 4584570Sraf } else { 4594570Sraf if (write_lock_try(rwlp, 0)) { 4606515Sraf (void) mutex_unlock(mp); 4614570Sraf break; 4624570Sraf } 4634570Sraf } 4644570Sraf atomic_or_32(rwstate, URW_HAS_WAITERS); 4654570Sraf readers = *rwstate; 4664570Sraf ASSERT_CONSISTENT_STATE(readers); 4674570Sraf /* 4684570Sraf * The calls to __lwp_rwlock_*() below will release the mutex, 4694570Sraf * so we need a dtrace probe here. 4704570Sraf */ 4714570Sraf mp->mutex_owner = 0; 4724570Sraf DTRACE_PROBE2(plockstat, mutex__release, mp, 0); 4734570Sraf /* 4744570Sraf * The waiters bit may be inaccurate. 4754570Sraf * Only the kernel knows for sure. 4764570Sraf */ 4774570Sraf if (rd_wr == READ_LOCK) { 4784570Sraf if (try_flag) 4794570Sraf error = __lwp_rwlock_tryrdlock(rwlp); 4804570Sraf else 4814570Sraf error = __lwp_rwlock_rdlock(rwlp, tsp); 4824570Sraf } else { 4834570Sraf if (try_flag) 4844570Sraf error = __lwp_rwlock_trywrlock(rwlp); 4854570Sraf else 4864570Sraf error = __lwp_rwlock_wrlock(rwlp, tsp); 4874570Sraf } 4884570Sraf } while (error == EAGAIN || error == EINTR); 4894570Sraf 4904570Sraf if (!try_flag) { 4914570Sraf DTRACE_PROBE3(plockstat, rw__blocked, rwlp, rd_wr, error == 0); 4924570Sraf } 4934570Sraf 4944570Sraf return (error); 4954570Sraf } 4964570Sraf 4974570Sraf /* 4984570Sraf * Common code for rdlock, timedrdlock, wrlock, timedwrlock, tryrdlock, 4994570Sraf * and trywrlock for process-private (USYNC_THREAD) rwlocks. 5004570Sraf */ 5014570Sraf int 5024570Sraf rwlock_lock(rwlock_t *rwlp, timespec_t *tsp, int rd_wr) 5034570Sraf { 5044570Sraf volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers; 5054570Sraf uint32_t readers; 5060Sstevel@tonic-gate ulwp_t *self = curthread; 5074570Sraf queue_head_t *qp; 5084570Sraf ulwp_t *ulwp; 5090Sstevel@tonic-gate int try_flag; 5106247Sraf int ignore_waiters_flag; 5110Sstevel@tonic-gate int error = 0; 5120Sstevel@tonic-gate 5130Sstevel@tonic-gate try_flag = (rd_wr & TRY_FLAG); 5140Sstevel@tonic-gate rd_wr &= ~TRY_FLAG; 5150Sstevel@tonic-gate ASSERT(rd_wr == READ_LOCK || rd_wr == WRITE_LOCK); 5160Sstevel@tonic-gate 5170Sstevel@tonic-gate if (!try_flag) { 5180Sstevel@tonic-gate DTRACE_PROBE2(plockstat, rw__block, rwlp, rd_wr); 5190Sstevel@tonic-gate } 5200Sstevel@tonic-gate 5214570Sraf qp = queue_lock(rwlp, MX); 5226247Sraf /* initial attempt to acquire the lock fails if there are waiters */ 5236247Sraf ignore_waiters_flag = 0; 5244570Sraf while (error == 0) { 5250Sstevel@tonic-gate if (rd_wr == READ_LOCK) { 5266247Sraf if (read_lock_try(rwlp, ignore_waiters_flag)) 5276247Sraf break; 5280Sstevel@tonic-gate } else { 5296247Sraf if (write_lock_try(rwlp, ignore_waiters_flag)) 5306247Sraf break; 5310Sstevel@tonic-gate } 5326247Sraf /* subsequent attempts do not fail due to waiters */ 5336247Sraf ignore_waiters_flag = 1; 5344570Sraf atomic_or_32(rwstate, URW_HAS_WAITERS); 5354570Sraf readers = *rwstate; 5364570Sraf ASSERT_CONSISTENT_STATE(readers); 5374570Sraf if ((readers & URW_WRITE_LOCKED) || 5384570Sraf (rd_wr == WRITE_LOCK && 5394570Sraf (readers & URW_READERS_MASK) != 0)) 5400Sstevel@tonic-gate /* EMPTY */; /* somebody holds the lock */ 5416247Sraf else if ((ulwp = queue_waiter(qp)) == NULL) { 5424570Sraf atomic_and_32(rwstate, ~URW_HAS_WAITERS); 5436247Sraf continue; /* no queued waiters, try again */ 5440Sstevel@tonic-gate } else { 5456247Sraf /* 5466247Sraf * Do a priority check on the queued waiter (the 5476247Sraf * highest priority thread on the queue) to see 5486247Sraf * if we should defer to him or just grab the lock. 5496247Sraf */ 5500Sstevel@tonic-gate int our_pri = real_priority(self); 5510Sstevel@tonic-gate int his_pri = real_priority(ulwp); 5520Sstevel@tonic-gate 5530Sstevel@tonic-gate if (rd_wr == WRITE_LOCK) { 5540Sstevel@tonic-gate /* 5550Sstevel@tonic-gate * We defer to a queued thread that has 5560Sstevel@tonic-gate * a higher priority than ours. 5570Sstevel@tonic-gate */ 5580Sstevel@tonic-gate if (his_pri <= our_pri) 5596247Sraf continue; /* try again */ 5600Sstevel@tonic-gate } else { 5610Sstevel@tonic-gate /* 5620Sstevel@tonic-gate * We defer to a queued thread that has 5630Sstevel@tonic-gate * a higher priority than ours or that 5640Sstevel@tonic-gate * is a writer whose priority equals ours. 5650Sstevel@tonic-gate */ 5660Sstevel@tonic-gate if (his_pri < our_pri || 5670Sstevel@tonic-gate (his_pri == our_pri && !ulwp->ul_writer)) 5686247Sraf continue; /* try again */ 5690Sstevel@tonic-gate } 5700Sstevel@tonic-gate } 5710Sstevel@tonic-gate /* 5720Sstevel@tonic-gate * We are about to block. 5730Sstevel@tonic-gate * If we're doing a trylock, return EBUSY instead. 5740Sstevel@tonic-gate */ 5750Sstevel@tonic-gate if (try_flag) { 5760Sstevel@tonic-gate error = EBUSY; 5770Sstevel@tonic-gate break; 5780Sstevel@tonic-gate } 5790Sstevel@tonic-gate /* 5806247Sraf * Enqueue writers ahead of readers. 5810Sstevel@tonic-gate */ 5820Sstevel@tonic-gate self->ul_writer = rd_wr; /* *must* be 0 or 1 */ 5836247Sraf enqueue(qp, self, 0); 5840Sstevel@tonic-gate set_parking_flag(self, 1); 5850Sstevel@tonic-gate queue_unlock(qp); 5860Sstevel@tonic-gate if ((error = __lwp_park(tsp, 0)) == EINTR) 5876247Sraf error = ignore_waiters_flag = 0; 5880Sstevel@tonic-gate set_parking_flag(self, 0); 5890Sstevel@tonic-gate qp = queue_lock(rwlp, MX); 5906247Sraf if (self->ul_sleepq && dequeue_self(qp) == 0) 5914570Sraf atomic_and_32(rwstate, ~URW_HAS_WAITERS); 5926247Sraf self->ul_writer = 0; 5930Sstevel@tonic-gate } 5940Sstevel@tonic-gate 5954570Sraf queue_unlock(qp); 5964570Sraf 5974570Sraf if (!try_flag) { 5984570Sraf DTRACE_PROBE3(plockstat, rw__blocked, rwlp, rd_wr, error == 0); 5994570Sraf } 6000Sstevel@tonic-gate 6010Sstevel@tonic-gate return (error); 6020Sstevel@tonic-gate } 6030Sstevel@tonic-gate 6040Sstevel@tonic-gate int 6050Sstevel@tonic-gate rw_rdlock_impl(rwlock_t *rwlp, timespec_t *tsp) 6060Sstevel@tonic-gate { 6070Sstevel@tonic-gate ulwp_t *self = curthread; 6080Sstevel@tonic-gate uberdata_t *udp = self->ul_uberdata; 6090Sstevel@tonic-gate readlock_t *readlockp; 6100Sstevel@tonic-gate tdb_rwlock_stats_t *rwsp = RWLOCK_STATS(rwlp, udp); 6110Sstevel@tonic-gate int error; 6120Sstevel@tonic-gate 6130Sstevel@tonic-gate /* 6140Sstevel@tonic-gate * If we already hold a readers lock on this rwlock, 6150Sstevel@tonic-gate * just increment our reference count and return. 6160Sstevel@tonic-gate */ 6174570Sraf sigoff(self); 6180Sstevel@tonic-gate readlockp = rwl_entry(rwlp); 6190Sstevel@tonic-gate if (readlockp->rd_count != 0) { 6204570Sraf if (readlockp->rd_count == READ_LOCK_MAX) { 6214570Sraf sigon(self); 6224570Sraf error = EAGAIN; 6234570Sraf goto out; 6244570Sraf } 6254570Sraf sigon(self); 6264570Sraf error = 0; 6274570Sraf goto out; 6280Sstevel@tonic-gate } 6294570Sraf sigon(self); 6300Sstevel@tonic-gate 6310Sstevel@tonic-gate /* 6320Sstevel@tonic-gate * If we hold the writer lock, bail out. 6330Sstevel@tonic-gate */ 634*6812Sraf if (rw_write_held(rwlp)) { 6350Sstevel@tonic-gate if (self->ul_error_detection) 6360Sstevel@tonic-gate rwlock_error(rwlp, "rwlock_rdlock", 6370Sstevel@tonic-gate "calling thread owns the writer lock"); 6384570Sraf error = EDEADLK; 6394570Sraf goto out; 6400Sstevel@tonic-gate } 6410Sstevel@tonic-gate 6424570Sraf if (read_lock_try(rwlp, 0)) 6434570Sraf error = 0; 6444570Sraf else if (rwlp->rwlock_type == USYNC_PROCESS) /* kernel-level */ 6450Sstevel@tonic-gate error = shared_rwlock_lock(rwlp, tsp, READ_LOCK); 6460Sstevel@tonic-gate else /* user-level */ 6470Sstevel@tonic-gate error = rwlock_lock(rwlp, tsp, READ_LOCK); 6480Sstevel@tonic-gate 6494570Sraf out: 6500Sstevel@tonic-gate if (error == 0) { 6514570Sraf sigoff(self); 6524570Sraf rwl_entry(rwlp)->rd_count++; 6534570Sraf sigon(self); 6540Sstevel@tonic-gate if (rwsp) 6550Sstevel@tonic-gate tdb_incr(rwsp->rw_rdlock); 6564570Sraf DTRACE_PROBE2(plockstat, rw__acquire, rwlp, READ_LOCK); 6574570Sraf } else { 6584570Sraf DTRACE_PROBE3(plockstat, rw__error, rwlp, READ_LOCK, error); 6590Sstevel@tonic-gate } 6600Sstevel@tonic-gate 6610Sstevel@tonic-gate return (error); 6620Sstevel@tonic-gate } 6630Sstevel@tonic-gate 664*6812Sraf #pragma weak pthread_rwlock_rdlock = rw_rdlock 665*6812Sraf #pragma weak _rw_rdlock = rw_rdlock 6660Sstevel@tonic-gate int 667*6812Sraf rw_rdlock(rwlock_t *rwlp) 6680Sstevel@tonic-gate { 6690Sstevel@tonic-gate ASSERT(!curthread->ul_critical || curthread->ul_bindflags); 6700Sstevel@tonic-gate return (rw_rdlock_impl(rwlp, NULL)); 6710Sstevel@tonic-gate } 6720Sstevel@tonic-gate 6730Sstevel@tonic-gate void 6740Sstevel@tonic-gate lrw_rdlock(rwlock_t *rwlp) 6750Sstevel@tonic-gate { 6760Sstevel@tonic-gate enter_critical(curthread); 6770Sstevel@tonic-gate (void) rw_rdlock_impl(rwlp, NULL); 6780Sstevel@tonic-gate } 6790Sstevel@tonic-gate 6800Sstevel@tonic-gate int 681*6812Sraf pthread_rwlock_reltimedrdlock_np(pthread_rwlock_t *_RESTRICT_KYWD rwlp, 682*6812Sraf const struct timespec *_RESTRICT_KYWD reltime) 6830Sstevel@tonic-gate { 6840Sstevel@tonic-gate timespec_t tslocal = *reltime; 6850Sstevel@tonic-gate int error; 6860Sstevel@tonic-gate 6870Sstevel@tonic-gate ASSERT(!curthread->ul_critical || curthread->ul_bindflags); 688*6812Sraf error = rw_rdlock_impl((rwlock_t *)rwlp, &tslocal); 6890Sstevel@tonic-gate if (error == ETIME) 6900Sstevel@tonic-gate error = ETIMEDOUT; 6910Sstevel@tonic-gate return (error); 6920Sstevel@tonic-gate } 6930Sstevel@tonic-gate 6940Sstevel@tonic-gate int 695*6812Sraf pthread_rwlock_timedrdlock(pthread_rwlock_t *_RESTRICT_KYWD rwlp, 696*6812Sraf const struct timespec *_RESTRICT_KYWD abstime) 6970Sstevel@tonic-gate { 6980Sstevel@tonic-gate timespec_t tslocal; 6990Sstevel@tonic-gate int error; 7000Sstevel@tonic-gate 7010Sstevel@tonic-gate ASSERT(!curthread->ul_critical || curthread->ul_bindflags); 7020Sstevel@tonic-gate abstime_to_reltime(CLOCK_REALTIME, abstime, &tslocal); 703*6812Sraf error = rw_rdlock_impl((rwlock_t *)rwlp, &tslocal); 7040Sstevel@tonic-gate if (error == ETIME) 7050Sstevel@tonic-gate error = ETIMEDOUT; 7060Sstevel@tonic-gate return (error); 7070Sstevel@tonic-gate } 7080Sstevel@tonic-gate 7090Sstevel@tonic-gate int 7100Sstevel@tonic-gate rw_wrlock_impl(rwlock_t *rwlp, timespec_t *tsp) 7110Sstevel@tonic-gate { 7120Sstevel@tonic-gate ulwp_t *self = curthread; 7130Sstevel@tonic-gate uberdata_t *udp = self->ul_uberdata; 7140Sstevel@tonic-gate tdb_rwlock_stats_t *rwsp = RWLOCK_STATS(rwlp, udp); 7150Sstevel@tonic-gate int error; 7160Sstevel@tonic-gate 7170Sstevel@tonic-gate /* 7180Sstevel@tonic-gate * If we hold a readers lock on this rwlock, bail out. 7190Sstevel@tonic-gate */ 720*6812Sraf if (rw_read_held(rwlp)) { 7210Sstevel@tonic-gate if (self->ul_error_detection) 7220Sstevel@tonic-gate rwlock_error(rwlp, "rwlock_wrlock", 7230Sstevel@tonic-gate "calling thread owns the readers lock"); 7244570Sraf error = EDEADLK; 7254570Sraf goto out; 7260Sstevel@tonic-gate } 7270Sstevel@tonic-gate 7280Sstevel@tonic-gate /* 7290Sstevel@tonic-gate * If we hold the writer lock, bail out. 7300Sstevel@tonic-gate */ 731*6812Sraf if (rw_write_held(rwlp)) { 7320Sstevel@tonic-gate if (self->ul_error_detection) 7330Sstevel@tonic-gate rwlock_error(rwlp, "rwlock_wrlock", 7340Sstevel@tonic-gate "calling thread owns the writer lock"); 7354570Sraf error = EDEADLK; 7364570Sraf goto out; 7370Sstevel@tonic-gate } 7380Sstevel@tonic-gate 7394570Sraf if (write_lock_try(rwlp, 0)) 7404570Sraf error = 0; 7414570Sraf else if (rwlp->rwlock_type == USYNC_PROCESS) /* kernel-level */ 7420Sstevel@tonic-gate error = shared_rwlock_lock(rwlp, tsp, WRITE_LOCK); 7434570Sraf else /* user-level */ 7440Sstevel@tonic-gate error = rwlock_lock(rwlp, tsp, WRITE_LOCK); 7450Sstevel@tonic-gate 7464570Sraf out: 7474570Sraf if (error == 0) { 7484570Sraf rwlp->rwlock_owner = (uintptr_t)self; 7494570Sraf if (rwlp->rwlock_type == USYNC_PROCESS) 7504570Sraf rwlp->rwlock_ownerpid = udp->pid; 7514570Sraf if (rwsp) { 7524570Sraf tdb_incr(rwsp->rw_wrlock); 7534570Sraf rwsp->rw_wrlock_begin_hold = gethrtime(); 7544570Sraf } 7554570Sraf DTRACE_PROBE2(plockstat, rw__acquire, rwlp, WRITE_LOCK); 7564570Sraf } else { 7574570Sraf DTRACE_PROBE3(plockstat, rw__error, rwlp, WRITE_LOCK, error); 7580Sstevel@tonic-gate } 7590Sstevel@tonic-gate return (error); 7600Sstevel@tonic-gate } 7610Sstevel@tonic-gate 762*6812Sraf #pragma weak pthread_rwlock_wrlock = rw_wrlock 763*6812Sraf #pragma weak _rw_wrlock = rw_wrlock 7640Sstevel@tonic-gate int 765*6812Sraf rw_wrlock(rwlock_t *rwlp) 7660Sstevel@tonic-gate { 7670Sstevel@tonic-gate ASSERT(!curthread->ul_critical || curthread->ul_bindflags); 7680Sstevel@tonic-gate return (rw_wrlock_impl(rwlp, NULL)); 7690Sstevel@tonic-gate } 7700Sstevel@tonic-gate 7710Sstevel@tonic-gate void 7720Sstevel@tonic-gate lrw_wrlock(rwlock_t *rwlp) 7730Sstevel@tonic-gate { 7740Sstevel@tonic-gate enter_critical(curthread); 7750Sstevel@tonic-gate (void) rw_wrlock_impl(rwlp, NULL); 7760Sstevel@tonic-gate } 7770Sstevel@tonic-gate 7780Sstevel@tonic-gate int 779*6812Sraf pthread_rwlock_reltimedwrlock_np(pthread_rwlock_t *_RESTRICT_KYWD rwlp, 780*6812Sraf const struct timespec *_RESTRICT_KYWD reltime) 7810Sstevel@tonic-gate { 7820Sstevel@tonic-gate timespec_t tslocal = *reltime; 7830Sstevel@tonic-gate int error; 7840Sstevel@tonic-gate 7850Sstevel@tonic-gate ASSERT(!curthread->ul_critical || curthread->ul_bindflags); 786*6812Sraf error = rw_wrlock_impl((rwlock_t *)rwlp, &tslocal); 7870Sstevel@tonic-gate if (error == ETIME) 7880Sstevel@tonic-gate error = ETIMEDOUT; 7890Sstevel@tonic-gate return (error); 7900Sstevel@tonic-gate } 7910Sstevel@tonic-gate 7920Sstevel@tonic-gate int 793*6812Sraf pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlp, const timespec_t *abstime) 7940Sstevel@tonic-gate { 7950Sstevel@tonic-gate timespec_t tslocal; 7960Sstevel@tonic-gate int error; 7970Sstevel@tonic-gate 7980Sstevel@tonic-gate ASSERT(!curthread->ul_critical || curthread->ul_bindflags); 7990Sstevel@tonic-gate abstime_to_reltime(CLOCK_REALTIME, abstime, &tslocal); 800*6812Sraf error = rw_wrlock_impl((rwlock_t *)rwlp, &tslocal); 8010Sstevel@tonic-gate if (error == ETIME) 8020Sstevel@tonic-gate error = ETIMEDOUT; 8030Sstevel@tonic-gate return (error); 8040Sstevel@tonic-gate } 8050Sstevel@tonic-gate 806*6812Sraf #pragma weak pthread_rwlock_tryrdlock = rw_tryrdlock 8070Sstevel@tonic-gate int 808*6812Sraf rw_tryrdlock(rwlock_t *rwlp) 8090Sstevel@tonic-gate { 8100Sstevel@tonic-gate ulwp_t *self = curthread; 8110Sstevel@tonic-gate uberdata_t *udp = self->ul_uberdata; 8120Sstevel@tonic-gate tdb_rwlock_stats_t *rwsp = RWLOCK_STATS(rwlp, udp); 8130Sstevel@tonic-gate readlock_t *readlockp; 8140Sstevel@tonic-gate int error; 8150Sstevel@tonic-gate 8160Sstevel@tonic-gate ASSERT(!curthread->ul_critical || curthread->ul_bindflags); 8170Sstevel@tonic-gate 8180Sstevel@tonic-gate if (rwsp) 8190Sstevel@tonic-gate tdb_incr(rwsp->rw_rdlock_try); 8200Sstevel@tonic-gate 8210Sstevel@tonic-gate /* 8220Sstevel@tonic-gate * If we already hold a readers lock on this rwlock, 8230Sstevel@tonic-gate * just increment our reference count and return. 8240Sstevel@tonic-gate */ 8254570Sraf sigoff(self); 8260Sstevel@tonic-gate readlockp = rwl_entry(rwlp); 8270Sstevel@tonic-gate if (readlockp->rd_count != 0) { 8284570Sraf if (readlockp->rd_count == READ_LOCK_MAX) { 8294570Sraf sigon(self); 8304570Sraf error = EAGAIN; 8314570Sraf goto out; 8324570Sraf } 8334570Sraf sigon(self); 8344570Sraf error = 0; 8354570Sraf goto out; 8360Sstevel@tonic-gate } 8374570Sraf sigon(self); 8380Sstevel@tonic-gate 8394570Sraf if (read_lock_try(rwlp, 0)) 8404570Sraf error = 0; 8414570Sraf else if (rwlp->rwlock_type == USYNC_PROCESS) /* kernel-level */ 8420Sstevel@tonic-gate error = shared_rwlock_lock(rwlp, NULL, READ_LOCK_TRY); 8430Sstevel@tonic-gate else /* user-level */ 8440Sstevel@tonic-gate error = rwlock_lock(rwlp, NULL, READ_LOCK_TRY); 8450Sstevel@tonic-gate 8464570Sraf out: 8474570Sraf if (error == 0) { 8484570Sraf sigoff(self); 8494570Sraf rwl_entry(rwlp)->rd_count++; 8504570Sraf sigon(self); 8514570Sraf DTRACE_PROBE2(plockstat, rw__acquire, rwlp, READ_LOCK); 8524570Sraf } else { 8534570Sraf if (rwsp) 8544570Sraf tdb_incr(rwsp->rw_rdlock_try_fail); 8554570Sraf if (error != EBUSY) { 8564570Sraf DTRACE_PROBE3(plockstat, rw__error, rwlp, READ_LOCK, 8574570Sraf error); 8584570Sraf } 8594570Sraf } 8600Sstevel@tonic-gate 8610Sstevel@tonic-gate return (error); 8620Sstevel@tonic-gate } 8630Sstevel@tonic-gate 864*6812Sraf #pragma weak pthread_rwlock_trywrlock = rw_trywrlock 8650Sstevel@tonic-gate int 866*6812Sraf rw_trywrlock(rwlock_t *rwlp) 8670Sstevel@tonic-gate { 8680Sstevel@tonic-gate ulwp_t *self = curthread; 8690Sstevel@tonic-gate uberdata_t *udp = self->ul_uberdata; 8700Sstevel@tonic-gate tdb_rwlock_stats_t *rwsp = RWLOCK_STATS(rwlp, udp); 8710Sstevel@tonic-gate int error; 8720Sstevel@tonic-gate 8734570Sraf ASSERT(!self->ul_critical || self->ul_bindflags); 8740Sstevel@tonic-gate 8750Sstevel@tonic-gate if (rwsp) 8760Sstevel@tonic-gate tdb_incr(rwsp->rw_wrlock_try); 8770Sstevel@tonic-gate 8784570Sraf if (write_lock_try(rwlp, 0)) 8794570Sraf error = 0; 8804570Sraf else if (rwlp->rwlock_type == USYNC_PROCESS) /* kernel-level */ 8810Sstevel@tonic-gate error = shared_rwlock_lock(rwlp, NULL, WRITE_LOCK_TRY); 8824570Sraf else /* user-level */ 8830Sstevel@tonic-gate error = rwlock_lock(rwlp, NULL, WRITE_LOCK_TRY); 8844570Sraf 8854570Sraf if (error == 0) { 8864570Sraf rwlp->rwlock_owner = (uintptr_t)self; 8874570Sraf if (rwlp->rwlock_type == USYNC_PROCESS) 8884570Sraf rwlp->rwlock_ownerpid = udp->pid; 8894570Sraf if (rwsp) 8904570Sraf rwsp->rw_wrlock_begin_hold = gethrtime(); 8914570Sraf DTRACE_PROBE2(plockstat, rw__acquire, rwlp, WRITE_LOCK); 8924570Sraf } else { 8934570Sraf if (rwsp) 8940Sstevel@tonic-gate tdb_incr(rwsp->rw_wrlock_try_fail); 8954570Sraf if (error != EBUSY) { 8964570Sraf DTRACE_PROBE3(plockstat, rw__error, rwlp, WRITE_LOCK, 8974570Sraf error); 8984570Sraf } 8990Sstevel@tonic-gate } 9000Sstevel@tonic-gate return (error); 9010Sstevel@tonic-gate } 9020Sstevel@tonic-gate 903*6812Sraf #pragma weak pthread_rwlock_unlock = rw_unlock 904*6812Sraf #pragma weak _rw_unlock = rw_unlock 9050Sstevel@tonic-gate int 906*6812Sraf rw_unlock(rwlock_t *rwlp) 9070Sstevel@tonic-gate { 9084570Sraf volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers; 9094570Sraf uint32_t readers; 9100Sstevel@tonic-gate ulwp_t *self = curthread; 9110Sstevel@tonic-gate uberdata_t *udp = self->ul_uberdata; 9120Sstevel@tonic-gate tdb_rwlock_stats_t *rwsp; 9134570Sraf queue_head_t *qp; 9144570Sraf int rd_wr; 9154570Sraf int waked = 0; 9160Sstevel@tonic-gate 9174570Sraf readers = *rwstate; 9184570Sraf ASSERT_CONSISTENT_STATE(readers); 9194570Sraf if (readers & URW_WRITE_LOCKED) { 9204570Sraf rd_wr = WRITE_LOCK; 9214570Sraf readers = 0; 9224570Sraf } else { 9234570Sraf rd_wr = READ_LOCK; 9244570Sraf readers &= URW_READERS_MASK; 9250Sstevel@tonic-gate } 9260Sstevel@tonic-gate 9274570Sraf if (rd_wr == WRITE_LOCK) { 9280Sstevel@tonic-gate /* 9290Sstevel@tonic-gate * Since the writer lock is held, we'd better be 9300Sstevel@tonic-gate * holding it, else we cannot legitimately be here. 9310Sstevel@tonic-gate */ 932*6812Sraf if (!rw_write_held(rwlp)) { 9330Sstevel@tonic-gate if (self->ul_error_detection) 9340Sstevel@tonic-gate rwlock_error(rwlp, "rwlock_unlock", 9350Sstevel@tonic-gate "writer lock held, " 9360Sstevel@tonic-gate "but not by the calling thread"); 9370Sstevel@tonic-gate return (EPERM); 9380Sstevel@tonic-gate } 9390Sstevel@tonic-gate if ((rwsp = RWLOCK_STATS(rwlp, udp)) != NULL) { 9400Sstevel@tonic-gate if (rwsp->rw_wrlock_begin_hold) 9410Sstevel@tonic-gate rwsp->rw_wrlock_hold_time += 9420Sstevel@tonic-gate gethrtime() - rwsp->rw_wrlock_begin_hold; 9430Sstevel@tonic-gate rwsp->rw_wrlock_begin_hold = 0; 9440Sstevel@tonic-gate } 9454570Sraf rwlp->rwlock_owner = 0; 9464570Sraf rwlp->rwlock_ownerpid = 0; 9474570Sraf } else if (readers > 0) { 9480Sstevel@tonic-gate /* 9490Sstevel@tonic-gate * A readers lock is held; if we don't hold one, bail out. 9500Sstevel@tonic-gate */ 9514570Sraf readlock_t *readlockp; 9524570Sraf 9534570Sraf sigoff(self); 9544570Sraf readlockp = rwl_entry(rwlp); 9550Sstevel@tonic-gate if (readlockp->rd_count == 0) { 9564570Sraf sigon(self); 9570Sstevel@tonic-gate if (self->ul_error_detection) 9580Sstevel@tonic-gate rwlock_error(rwlp, "rwlock_unlock", 9590Sstevel@tonic-gate "readers lock held, " 9600Sstevel@tonic-gate "but not by the calling thread"); 9610Sstevel@tonic-gate return (EPERM); 9620Sstevel@tonic-gate } 9630Sstevel@tonic-gate /* 9640Sstevel@tonic-gate * If we hold more than one readers lock on this rwlock, 9650Sstevel@tonic-gate * just decrement our reference count and return. 9660Sstevel@tonic-gate */ 9670Sstevel@tonic-gate if (--readlockp->rd_count != 0) { 9684570Sraf sigon(self); 9694570Sraf goto out; 9700Sstevel@tonic-gate } 9714570Sraf sigon(self); 9720Sstevel@tonic-gate } else { 9730Sstevel@tonic-gate /* 9740Sstevel@tonic-gate * This is a usage error. 9750Sstevel@tonic-gate * No thread should release an unowned lock. 9760Sstevel@tonic-gate */ 9770Sstevel@tonic-gate if (self->ul_error_detection) 9780Sstevel@tonic-gate rwlock_error(rwlp, "rwlock_unlock", "lock not owned"); 9790Sstevel@tonic-gate return (EPERM); 9800Sstevel@tonic-gate } 9810Sstevel@tonic-gate 9824570Sraf if (rd_wr == WRITE_LOCK && write_unlock_try(rwlp)) { 9834570Sraf /* EMPTY */; 9844570Sraf } else if (rd_wr == READ_LOCK && read_unlock_try(rwlp)) { 9854570Sraf /* EMPTY */; 9864570Sraf } else if (rwlp->rwlock_type == USYNC_PROCESS) { 9876515Sraf (void) mutex_lock(&rwlp->mutex); 9884570Sraf (void) __lwp_rwlock_unlock(rwlp); 9896515Sraf (void) mutex_unlock(&rwlp->mutex); 9904570Sraf waked = 1; 9914570Sraf } else { 9920Sstevel@tonic-gate qp = queue_lock(rwlp, MX); 9934570Sraf if (rd_wr == READ_LOCK) 9944570Sraf atomic_dec_32(rwstate); 9954570Sraf else 9964570Sraf atomic_and_32(rwstate, ~URW_WRITE_LOCKED); 9970Sstevel@tonic-gate waked = rw_queue_release(qp, rwlp); 9980Sstevel@tonic-gate } 9990Sstevel@tonic-gate 10004570Sraf out: 10014570Sraf DTRACE_PROBE2(plockstat, rw__release, rwlp, rd_wr); 10024570Sraf 10030Sstevel@tonic-gate /* 10040Sstevel@tonic-gate * Yield to the thread we just waked up, just in case we might 10050Sstevel@tonic-gate * be about to grab the rwlock again immediately upon return. 10060Sstevel@tonic-gate * This is pretty weak but it helps on a uniprocessor and also 10070Sstevel@tonic-gate * when cpu affinity has assigned both ourself and the other 10080Sstevel@tonic-gate * thread to the same CPU. Note that lwp_yield() will yield 10090Sstevel@tonic-gate * the processor only if the writer is at the same or higher 10100Sstevel@tonic-gate * priority than ourself. This provides more balanced program 10110Sstevel@tonic-gate * behavior; it doesn't guarantee acquisition of the lock by 10120Sstevel@tonic-gate * the pending writer. 10130Sstevel@tonic-gate */ 10140Sstevel@tonic-gate if (waked) 10156515Sraf yield(); 10160Sstevel@tonic-gate return (0); 10170Sstevel@tonic-gate } 10180Sstevel@tonic-gate 10190Sstevel@tonic-gate void 10200Sstevel@tonic-gate lrw_unlock(rwlock_t *rwlp) 10210Sstevel@tonic-gate { 1022*6812Sraf (void) rw_unlock(rwlp); 10230Sstevel@tonic-gate exit_critical(curthread); 10240Sstevel@tonic-gate } 1025