17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
541efec22Sraf * Common Development and Distribution License (the "License").
641efec22Sraf * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
2141efec22Sraf
227c478bd9Sstevel@tonic-gate /*
23bbbbacb4SRoger A. Faulkner * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
2448bbca81SDaniel Hoffman * Copyright (c) 2016 by Delphix. All rights reserved.
25*8b6b46dcSRobert Mustacchi * Copyright 2024 Oxide Computer Company
267c478bd9Sstevel@tonic-gate */
277c478bd9Sstevel@tonic-gate
287c478bd9Sstevel@tonic-gate #include "lint.h"
297c478bd9Sstevel@tonic-gate #include "thr_uberdata.h"
307c478bd9Sstevel@tonic-gate #include <sys/sdt.h>
317c478bd9Sstevel@tonic-gate
327c478bd9Sstevel@tonic-gate #define TRY_FLAG 0x10
337c478bd9Sstevel@tonic-gate #define READ_LOCK 0
347c478bd9Sstevel@tonic-gate #define WRITE_LOCK 1
357c478bd9Sstevel@tonic-gate #define READ_LOCK_TRY (READ_LOCK | TRY_FLAG)
367c478bd9Sstevel@tonic-gate #define WRITE_LOCK_TRY (WRITE_LOCK | TRY_FLAG)
377c478bd9Sstevel@tonic-gate
387c478bd9Sstevel@tonic-gate #define NLOCKS 4 /* initial number of readlock_t structs allocated */
397c478bd9Sstevel@tonic-gate
4041efec22Sraf #define ASSERT_CONSISTENT_STATE(readers) \
4141efec22Sraf ASSERT(!((readers) & URW_WRITE_LOCKED) || \
4241efec22Sraf ((readers) & ~URW_HAS_WAITERS) == URW_WRITE_LOCKED)
4341efec22Sraf
447c478bd9Sstevel@tonic-gate /*
457c478bd9Sstevel@tonic-gate * Find/allocate an entry for rwlp in our array of rwlocks held for reading.
4641efec22Sraf * We must be deferring signals for this to be safe.
47883492d5Sraf * Else if we are returning an entry with ul_rdlockcnt == 0,
4841efec22Sraf * it could be reassigned behind our back in a signal handler.
497c478bd9Sstevel@tonic-gate */
507c478bd9Sstevel@tonic-gate static readlock_t *
rwl_entry(rwlock_t * rwlp)517c478bd9Sstevel@tonic-gate rwl_entry(rwlock_t *rwlp)
527c478bd9Sstevel@tonic-gate {
537c478bd9Sstevel@tonic-gate ulwp_t *self = curthread;
547c478bd9Sstevel@tonic-gate readlock_t *remembered = NULL;
557c478bd9Sstevel@tonic-gate readlock_t *readlockp;
567c478bd9Sstevel@tonic-gate uint_t nlocks;
577c478bd9Sstevel@tonic-gate
5841efec22Sraf /* we must be deferring signals */
5941efec22Sraf ASSERT((self->ul_critical + self->ul_sigdefer) != 0);
6041efec22Sraf
61883492d5Sraf if ((nlocks = self->ul_rdlockcnt) != 0)
627c478bd9Sstevel@tonic-gate readlockp = self->ul_readlock.array;
637c478bd9Sstevel@tonic-gate else {
647c478bd9Sstevel@tonic-gate nlocks = 1;
657c478bd9Sstevel@tonic-gate readlockp = &self->ul_readlock.single;
667c478bd9Sstevel@tonic-gate }
677c478bd9Sstevel@tonic-gate
687c478bd9Sstevel@tonic-gate for (; nlocks; nlocks--, readlockp++) {
697c478bd9Sstevel@tonic-gate if (readlockp->rd_rwlock == rwlp)
707c478bd9Sstevel@tonic-gate return (readlockp);
717c478bd9Sstevel@tonic-gate if (readlockp->rd_count == 0 && remembered == NULL)
727c478bd9Sstevel@tonic-gate remembered = readlockp;
737c478bd9Sstevel@tonic-gate }
747c478bd9Sstevel@tonic-gate if (remembered != NULL) {
757c478bd9Sstevel@tonic-gate remembered->rd_rwlock = rwlp;
767c478bd9Sstevel@tonic-gate return (remembered);
777c478bd9Sstevel@tonic-gate }
787c478bd9Sstevel@tonic-gate
797c478bd9Sstevel@tonic-gate /*
807c478bd9Sstevel@tonic-gate * No entry available. Allocate more space, converting the single
817c478bd9Sstevel@tonic-gate * readlock_t entry into an array of readlock_t entries if necessary.
827c478bd9Sstevel@tonic-gate */
83883492d5Sraf if ((nlocks = self->ul_rdlockcnt) == 0) {
847c478bd9Sstevel@tonic-gate /*
857c478bd9Sstevel@tonic-gate * Initial allocation of the readlock_t array.
867c478bd9Sstevel@tonic-gate * Convert the single entry into an array.
877c478bd9Sstevel@tonic-gate */
88883492d5Sraf self->ul_rdlockcnt = nlocks = NLOCKS;
897c478bd9Sstevel@tonic-gate readlockp = lmalloc(nlocks * sizeof (readlock_t));
907c478bd9Sstevel@tonic-gate /*
917c478bd9Sstevel@tonic-gate * The single readlock_t becomes the first entry in the array.
927c478bd9Sstevel@tonic-gate */
937c478bd9Sstevel@tonic-gate *readlockp = self->ul_readlock.single;
947c478bd9Sstevel@tonic-gate self->ul_readlock.single.rd_count = 0;
957c478bd9Sstevel@tonic-gate self->ul_readlock.array = readlockp;
967c478bd9Sstevel@tonic-gate /*
977c478bd9Sstevel@tonic-gate * Return the next available entry in the array.
987c478bd9Sstevel@tonic-gate */
997c478bd9Sstevel@tonic-gate (++readlockp)->rd_rwlock = rwlp;
1007c478bd9Sstevel@tonic-gate return (readlockp);
1017c478bd9Sstevel@tonic-gate }
1027c478bd9Sstevel@tonic-gate /*
1037c478bd9Sstevel@tonic-gate * Reallocate the array, double the size each time.
1047c478bd9Sstevel@tonic-gate */
1057c478bd9Sstevel@tonic-gate readlockp = lmalloc(nlocks * 2 * sizeof (readlock_t));
1068cd45542Sraf (void) memcpy(readlockp, self->ul_readlock.array,
1077c478bd9Sstevel@tonic-gate nlocks * sizeof (readlock_t));
1087c478bd9Sstevel@tonic-gate lfree(self->ul_readlock.array, nlocks * sizeof (readlock_t));
1097c478bd9Sstevel@tonic-gate self->ul_readlock.array = readlockp;
110883492d5Sraf self->ul_rdlockcnt *= 2;
1117c478bd9Sstevel@tonic-gate /*
1127c478bd9Sstevel@tonic-gate * Return the next available entry in the newly allocated array.
1137c478bd9Sstevel@tonic-gate */
1147c478bd9Sstevel@tonic-gate (readlockp += nlocks)->rd_rwlock = rwlp;
1157c478bd9Sstevel@tonic-gate return (readlockp);
1167c478bd9Sstevel@tonic-gate }
1177c478bd9Sstevel@tonic-gate
1187c478bd9Sstevel@tonic-gate /*
1197c478bd9Sstevel@tonic-gate * Free the array of rwlocks held for reading.
1207c478bd9Sstevel@tonic-gate */
1217c478bd9Sstevel@tonic-gate void
rwl_free(ulwp_t * ulwp)1227c478bd9Sstevel@tonic-gate rwl_free(ulwp_t *ulwp)
1237c478bd9Sstevel@tonic-gate {
1247c478bd9Sstevel@tonic-gate uint_t nlocks;
1257c478bd9Sstevel@tonic-gate
126883492d5Sraf if ((nlocks = ulwp->ul_rdlockcnt) != 0)
1277c478bd9Sstevel@tonic-gate lfree(ulwp->ul_readlock.array, nlocks * sizeof (readlock_t));
128883492d5Sraf ulwp->ul_rdlockcnt = 0;
1297c478bd9Sstevel@tonic-gate ulwp->ul_readlock.single.rd_rwlock = NULL;
1307c478bd9Sstevel@tonic-gate ulwp->ul_readlock.single.rd_count = 0;
1317c478bd9Sstevel@tonic-gate }
1327c478bd9Sstevel@tonic-gate
1337c478bd9Sstevel@tonic-gate /*
1347c478bd9Sstevel@tonic-gate * Check if a reader version of the lock is held by the current thread.
1357c478bd9Sstevel@tonic-gate */
1367257d1b4Sraf #pragma weak _rw_read_held = rw_read_held
1377c478bd9Sstevel@tonic-gate int
rw_read_held(rwlock_t * rwlp)1387257d1b4Sraf rw_read_held(rwlock_t *rwlp)
1397c478bd9Sstevel@tonic-gate {
14041efec22Sraf volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
14141efec22Sraf uint32_t readers;
14241efec22Sraf ulwp_t *self = curthread;
1437c478bd9Sstevel@tonic-gate readlock_t *readlockp;
1447c478bd9Sstevel@tonic-gate uint_t nlocks;
14541efec22Sraf int rval = 0;
1467c478bd9Sstevel@tonic-gate
14741efec22Sraf no_preempt(self);
1487c478bd9Sstevel@tonic-gate
14941efec22Sraf readers = *rwstate;
15041efec22Sraf ASSERT_CONSISTENT_STATE(readers);
15141efec22Sraf if (!(readers & URW_WRITE_LOCKED) &&
15241efec22Sraf (readers & URW_READERS_MASK) != 0) {
1537c478bd9Sstevel@tonic-gate /*
1547c478bd9Sstevel@tonic-gate * The lock is held for reading by some thread.
1557c478bd9Sstevel@tonic-gate * Search our array of rwlocks held for reading for a match.
1567c478bd9Sstevel@tonic-gate */
157883492d5Sraf if ((nlocks = self->ul_rdlockcnt) != 0)
1587c478bd9Sstevel@tonic-gate readlockp = self->ul_readlock.array;
1597c478bd9Sstevel@tonic-gate else {
1607c478bd9Sstevel@tonic-gate nlocks = 1;
1617c478bd9Sstevel@tonic-gate readlockp = &self->ul_readlock.single;
1627c478bd9Sstevel@tonic-gate }
16341efec22Sraf for (; nlocks; nlocks--, readlockp++) {
16441efec22Sraf if (readlockp->rd_rwlock == rwlp) {
16541efec22Sraf if (readlockp->rd_count)
16641efec22Sraf rval = 1;
16741efec22Sraf break;
16841efec22Sraf }
16941efec22Sraf }
17041efec22Sraf }
1717c478bd9Sstevel@tonic-gate
17241efec22Sraf preempt(self);
17341efec22Sraf return (rval);
1747c478bd9Sstevel@tonic-gate }
1757c478bd9Sstevel@tonic-gate
1767c478bd9Sstevel@tonic-gate /*
1777c478bd9Sstevel@tonic-gate * Check if a writer version of the lock is held by the current thread.
1787c478bd9Sstevel@tonic-gate */
1797257d1b4Sraf #pragma weak _rw_write_held = rw_write_held
1807c478bd9Sstevel@tonic-gate int
rw_write_held(rwlock_t * rwlp)1817257d1b4Sraf rw_write_held(rwlock_t *rwlp)
1827c478bd9Sstevel@tonic-gate {
18341efec22Sraf volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
18441efec22Sraf uint32_t readers;
1857c478bd9Sstevel@tonic-gate ulwp_t *self = curthread;
18641efec22Sraf int rval;
1877c478bd9Sstevel@tonic-gate
18841efec22Sraf no_preempt(self);
1897c478bd9Sstevel@tonic-gate
19041efec22Sraf readers = *rwstate;
19141efec22Sraf ASSERT_CONSISTENT_STATE(readers);
19241efec22Sraf rval = ((readers & URW_WRITE_LOCKED) &&
19341efec22Sraf rwlp->rwlock_owner == (uintptr_t)self &&
19441efec22Sraf (rwlp->rwlock_type == USYNC_THREAD ||
19541efec22Sraf rwlp->rwlock_ownerpid == self->ul_uberdata->pid));
19641efec22Sraf
19741efec22Sraf preempt(self);
19841efec22Sraf return (rval);
1997c478bd9Sstevel@tonic-gate }
2007c478bd9Sstevel@tonic-gate
2017257d1b4Sraf #pragma weak _rwlock_init = rwlock_init
2027c478bd9Sstevel@tonic-gate int
rwlock_init(rwlock_t * rwlp,int type,void * arg __unused)2034a38094cSToomas Soome rwlock_init(rwlock_t *rwlp, int type, void *arg __unused)
2047c478bd9Sstevel@tonic-gate {
2057c5714f6Sraf ulwp_t *self = curthread;
2067c5714f6Sraf
2077c478bd9Sstevel@tonic-gate if (type != USYNC_THREAD && type != USYNC_PROCESS)
2087c478bd9Sstevel@tonic-gate return (EINVAL);
2097c478bd9Sstevel@tonic-gate /*
2107c478bd9Sstevel@tonic-gate * Once reinitialized, we can no longer be holding a read or write lock.
2117c478bd9Sstevel@tonic-gate * We can do nothing about other threads that are holding read locks.
2127c478bd9Sstevel@tonic-gate */
2137c5714f6Sraf sigoff(self);
2147c478bd9Sstevel@tonic-gate rwl_entry(rwlp)->rd_count = 0;
2157c5714f6Sraf sigon(self);
2168cd45542Sraf (void) memset(rwlp, 0, sizeof (*rwlp));
2177c478bd9Sstevel@tonic-gate rwlp->rwlock_type = (uint16_t)type;
2187c478bd9Sstevel@tonic-gate rwlp->rwlock_magic = RWL_MAGIC;
2197c478bd9Sstevel@tonic-gate rwlp->mutex.mutex_type = (uint8_t)type;
2207c478bd9Sstevel@tonic-gate rwlp->mutex.mutex_flag = LOCK_INITED;
2217c478bd9Sstevel@tonic-gate rwlp->mutex.mutex_magic = MUTEX_MAGIC;
2227c5714f6Sraf
2237c5714f6Sraf /*
2247c5714f6Sraf * This should be at the beginning of the function,
2257c5714f6Sraf * but for the sake of old broken applications that
2267c5714f6Sraf * do not have proper alignment for their rwlocks
2277c5714f6Sraf * (and don't check the return code from rwlock_init),
2287c5714f6Sraf * we put it here, after initializing the rwlock regardless.
2297c5714f6Sraf */
2307c5714f6Sraf if (((uintptr_t)rwlp & (_LONG_LONG_ALIGNMENT - 1)) &&
2317c5714f6Sraf self->ul_misaligned == 0)
2327c5714f6Sraf return (EINVAL);
2337c5714f6Sraf
2347c478bd9Sstevel@tonic-gate return (0);
2357c478bd9Sstevel@tonic-gate }
2367c478bd9Sstevel@tonic-gate
2377257d1b4Sraf #pragma weak pthread_rwlock_destroy = rwlock_destroy
2387257d1b4Sraf #pragma weak _rwlock_destroy = rwlock_destroy
2397c478bd9Sstevel@tonic-gate int
rwlock_destroy(rwlock_t * rwlp)2407257d1b4Sraf rwlock_destroy(rwlock_t *rwlp)
2417c478bd9Sstevel@tonic-gate {
242e54ab87fSRoger A. Faulkner ulwp_t *self = curthread;
243e54ab87fSRoger A. Faulkner
2447c478bd9Sstevel@tonic-gate /*
2457c478bd9Sstevel@tonic-gate * Once destroyed, we can no longer be holding a read or write lock.
2467c478bd9Sstevel@tonic-gate * We can do nothing about other threads that are holding read locks.
2477c478bd9Sstevel@tonic-gate */
248e54ab87fSRoger A. Faulkner sigoff(self);
2497c478bd9Sstevel@tonic-gate rwl_entry(rwlp)->rd_count = 0;
250e54ab87fSRoger A. Faulkner sigon(self);
2517c478bd9Sstevel@tonic-gate rwlp->rwlock_magic = 0;
2527c478bd9Sstevel@tonic-gate tdb_sync_obj_deregister(rwlp);
2537c478bd9Sstevel@tonic-gate return (0);
2547c478bd9Sstevel@tonic-gate }
2557c478bd9Sstevel@tonic-gate
2567c478bd9Sstevel@tonic-gate /*
257bbbbacb4SRoger A. Faulkner * The following four functions:
258bbbbacb4SRoger A. Faulkner * read_lock_try()
259bbbbacb4SRoger A. Faulkner * read_unlock_try()
260bbbbacb4SRoger A. Faulkner * write_lock_try()
261bbbbacb4SRoger A. Faulkner * write_unlock_try()
262bbbbacb4SRoger A. Faulkner * lie at the heart of the fast-path code for rwlocks,
263bbbbacb4SRoger A. Faulkner * both process-private and process-shared.
264bbbbacb4SRoger A. Faulkner *
265bbbbacb4SRoger A. Faulkner * They are called once without recourse to any other locking primitives.
266bbbbacb4SRoger A. Faulkner * If they succeed, we are done and the fast-path code was successful.
267bbbbacb4SRoger A. Faulkner * If they fail, we have to deal with lock queues, either to enqueue
268bbbbacb4SRoger A. Faulkner * ourself and sleep or to dequeue and wake up someone else (slow paths).
269bbbbacb4SRoger A. Faulkner *
270bbbbacb4SRoger A. Faulkner * Unless 'ignore_waiters_flag' is true (a condition that applies only
271bbbbacb4SRoger A. Faulkner * when read_lock_try() or write_lock_try() is called from code that
272bbbbacb4SRoger A. Faulkner * is already in the slow path and has already acquired the queue lock),
273bbbbacb4SRoger A. Faulkner * these functions will always fail if the waiters flag, URW_HAS_WAITERS,
274bbbbacb4SRoger A. Faulkner * is set in the 'rwstate' word. Thus, setting the waiters flag on the
275bbbbacb4SRoger A. Faulkner * rwlock and acquiring the queue lock guarantees exclusive access to
276bbbbacb4SRoger A. Faulkner * the rwlock (and is the only way to guarantee exclusive access).
277bbbbacb4SRoger A. Faulkner */
278bbbbacb4SRoger A. Faulkner
279bbbbacb4SRoger A. Faulkner /*
28041efec22Sraf * Attempt to acquire a readers lock. Return true on success.
2817c478bd9Sstevel@tonic-gate */
2827c478bd9Sstevel@tonic-gate static int
read_lock_try(rwlock_t * rwlp,int ignore_waiters_flag)28341efec22Sraf read_lock_try(rwlock_t *rwlp, int ignore_waiters_flag)
2847c478bd9Sstevel@tonic-gate {
28541efec22Sraf volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
28641efec22Sraf uint32_t mask = ignore_waiters_flag?
28741efec22Sraf URW_WRITE_LOCKED : (URW_HAS_WAITERS | URW_WRITE_LOCKED);
28841efec22Sraf uint32_t readers;
2897c478bd9Sstevel@tonic-gate ulwp_t *self = curthread;
2907c478bd9Sstevel@tonic-gate
2917c478bd9Sstevel@tonic-gate no_preempt(self);
29241efec22Sraf while (((readers = *rwstate) & mask) == 0) {
29341efec22Sraf if (atomic_cas_32(rwstate, readers, readers + 1) == readers) {
2947c478bd9Sstevel@tonic-gate preempt(self);
2957c478bd9Sstevel@tonic-gate return (1);
2967c478bd9Sstevel@tonic-gate }
2977c478bd9Sstevel@tonic-gate }
29841efec22Sraf preempt(self);
29941efec22Sraf return (0);
3007c478bd9Sstevel@tonic-gate }
30141efec22Sraf
30241efec22Sraf /*
30341efec22Sraf * Attempt to release a reader lock. Return true on success.
30441efec22Sraf */
30541efec22Sraf static int
read_unlock_try(rwlock_t * rwlp)30641efec22Sraf read_unlock_try(rwlock_t *rwlp)
30741efec22Sraf {
30841efec22Sraf volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
30941efec22Sraf uint32_t readers;
31041efec22Sraf ulwp_t *self = curthread;
31141efec22Sraf
31241efec22Sraf no_preempt(self);
31341efec22Sraf while (((readers = *rwstate) & URW_HAS_WAITERS) == 0) {
31441efec22Sraf if (atomic_cas_32(rwstate, readers, readers - 1) == readers) {
31541efec22Sraf preempt(self);
31641efec22Sraf return (1);
31741efec22Sraf }
31841efec22Sraf }
31941efec22Sraf preempt(self);
32041efec22Sraf return (0);
32141efec22Sraf }
32241efec22Sraf
32341efec22Sraf /*
32441efec22Sraf * Attempt to acquire a writer lock. Return true on success.
32541efec22Sraf */
32641efec22Sraf static int
write_lock_try(rwlock_t * rwlp,int ignore_waiters_flag)32741efec22Sraf write_lock_try(rwlock_t *rwlp, int ignore_waiters_flag)
32841efec22Sraf {
32941efec22Sraf volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
33041efec22Sraf uint32_t mask = ignore_waiters_flag?
33141efec22Sraf (URW_WRITE_LOCKED | URW_READERS_MASK) :
33241efec22Sraf (URW_HAS_WAITERS | URW_WRITE_LOCKED | URW_READERS_MASK);
33341efec22Sraf ulwp_t *self = curthread;
33441efec22Sraf uint32_t readers;
33541efec22Sraf
33641efec22Sraf no_preempt(self);
33741efec22Sraf while (((readers = *rwstate) & mask) == 0) {
33841efec22Sraf if (atomic_cas_32(rwstate, readers, readers | URW_WRITE_LOCKED)
33941efec22Sraf == readers) {
34041efec22Sraf preempt(self);
34141efec22Sraf return (1);
34241efec22Sraf }
34341efec22Sraf }
34441efec22Sraf preempt(self);
34541efec22Sraf return (0);
34641efec22Sraf }
34741efec22Sraf
34841efec22Sraf /*
34941efec22Sraf * Attempt to release a writer lock. Return true on success.
35041efec22Sraf */
35141efec22Sraf static int
write_unlock_try(rwlock_t * rwlp)35241efec22Sraf write_unlock_try(rwlock_t *rwlp)
35341efec22Sraf {
35441efec22Sraf volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
35541efec22Sraf uint32_t readers;
35641efec22Sraf ulwp_t *self = curthread;
35741efec22Sraf
35841efec22Sraf no_preempt(self);
35941efec22Sraf while (((readers = *rwstate) & URW_HAS_WAITERS) == 0) {
36041efec22Sraf if (atomic_cas_32(rwstate, readers, 0) == readers) {
36141efec22Sraf preempt(self);
36241efec22Sraf return (1);
36341efec22Sraf }
36441efec22Sraf }
36541efec22Sraf preempt(self);
36641efec22Sraf return (0);
36741efec22Sraf }
36841efec22Sraf
36941efec22Sraf /*
370bbbbacb4SRoger A. Faulkner * Release a process-private rwlock and wake up any thread(s) sleeping on it.
37141efec22Sraf * This is called when a thread releases a lock that appears to have waiters.
37241efec22Sraf */
373bbbbacb4SRoger A. Faulkner static void
rw_queue_release(rwlock_t * rwlp)374bbbbacb4SRoger A. Faulkner rw_queue_release(rwlock_t *rwlp)
37541efec22Sraf {
37641efec22Sraf volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
377bbbbacb4SRoger A. Faulkner queue_head_t *qp;
37841efec22Sraf uint32_t readers;
379bbbbacb4SRoger A. Faulkner uint32_t writer;
38041efec22Sraf ulwp_t **ulwpp;
38141efec22Sraf ulwp_t *ulwp;
382d4204c85Sraf ulwp_t *prev;
383d4204c85Sraf int nlwpid = 0;
384d4204c85Sraf int more;
385d4204c85Sraf int maxlwps = MAXLWPS;
38641efec22Sraf lwpid_t buffer[MAXLWPS];
38741efec22Sraf lwpid_t *lwpid = buffer;
38841efec22Sraf
389bbbbacb4SRoger A. Faulkner qp = queue_lock(rwlp, MX);
390bbbbacb4SRoger A. Faulkner
391bbbbacb4SRoger A. Faulkner /*
392bbbbacb4SRoger A. Faulkner * Here is where we actually drop the lock,
393bbbbacb4SRoger A. Faulkner * but we retain the URW_HAS_WAITERS flag, if it is already set.
394bbbbacb4SRoger A. Faulkner */
39541efec22Sraf readers = *rwstate;
39641efec22Sraf ASSERT_CONSISTENT_STATE(readers);
397bbbbacb4SRoger A. Faulkner if (readers & URW_WRITE_LOCKED) /* drop the writer lock */
398bbbbacb4SRoger A. Faulkner atomic_and_32(rwstate, ~URW_WRITE_LOCKED);
399bbbbacb4SRoger A. Faulkner else /* drop the readers lock */
400bbbbacb4SRoger A. Faulkner atomic_dec_32(rwstate);
401bbbbacb4SRoger A. Faulkner if (!(readers & URW_HAS_WAITERS)) { /* no waiters */
4027c478bd9Sstevel@tonic-gate queue_unlock(qp);
403bbbbacb4SRoger A. Faulkner return;
4047c478bd9Sstevel@tonic-gate }
405bbbbacb4SRoger A. Faulkner
406bbbbacb4SRoger A. Faulkner /*
407bbbbacb4SRoger A. Faulkner * The presence of the URW_HAS_WAITERS flag causes all rwlock
408bbbbacb4SRoger A. Faulkner * code to go through the slow path, acquiring queue_lock(qp).
409bbbbacb4SRoger A. Faulkner * Therefore, the rest of this code is safe because we are
410bbbbacb4SRoger A. Faulkner * holding the queue lock and the URW_HAS_WAITERS flag is set.
411bbbbacb4SRoger A. Faulkner */
412bbbbacb4SRoger A. Faulkner
413bbbbacb4SRoger A. Faulkner readers = *rwstate; /* must fetch the value again */
414bbbbacb4SRoger A. Faulkner ASSERT_CONSISTENT_STATE(readers);
415bbbbacb4SRoger A. Faulkner ASSERT(readers & URW_HAS_WAITERS);
416bbbbacb4SRoger A. Faulkner readers &= URW_READERS_MASK; /* count of current readers */
417bbbbacb4SRoger A. Faulkner writer = 0; /* no current writer */
41841efec22Sraf
41941efec22Sraf /*
420d4204c85Sraf * Examine the queue of waiters in priority order and prepare
421d4204c85Sraf * to wake up as many readers as we encounter before encountering
422d4204c85Sraf * a writer. If the highest priority thread on the queue is a
42341efec22Sraf * writer, stop there and wake it up.
42441efec22Sraf *
42541efec22Sraf * We keep track of lwpids that are to be unparked in lwpid[].
42641efec22Sraf * __lwp_unpark_all() is called to unpark all of them after
42741efec22Sraf * they have been removed from the sleep queue and the sleep
42841efec22Sraf * queue lock has been dropped. If we run out of space in our
42941efec22Sraf * on-stack buffer, we need to allocate more but we can't call
43041efec22Sraf * lmalloc() because we are holding a queue lock when the overflow
43141efec22Sraf * occurs and lmalloc() acquires a lock. We can't use alloca()
43241efec22Sraf * either because the application may have allocated a small
43341efec22Sraf * stack and we don't want to overrun the stack. So we call
43441efec22Sraf * alloc_lwpids() to allocate a bigger buffer using the mmap()
43541efec22Sraf * system call directly since that path acquires no locks.
43641efec22Sraf */
437d4204c85Sraf while ((ulwpp = queue_slot(qp, &prev, &more)) != NULL) {
438d4204c85Sraf ulwp = *ulwpp;
439d4204c85Sraf ASSERT(ulwp->ul_wchan == rwlp);
44041efec22Sraf if (ulwp->ul_writer) {
441bbbbacb4SRoger A. Faulkner if (writer != 0 || readers != 0)
44241efec22Sraf break;
44341efec22Sraf /* one writer to wake */
444bbbbacb4SRoger A. Faulkner writer++;
44541efec22Sraf } else {
446bbbbacb4SRoger A. Faulkner if (writer != 0)
44741efec22Sraf break;
44841efec22Sraf /* at least one reader to wake */
44941efec22Sraf readers++;
45041efec22Sraf if (nlwpid == maxlwps)
45141efec22Sraf lwpid = alloc_lwpids(lwpid, &nlwpid, &maxlwps);
45241efec22Sraf }
453d4204c85Sraf queue_unlink(qp, ulwpp, prev);
454d4204c85Sraf ulwp->ul_sleepq = NULL;
455d4204c85Sraf ulwp->ul_wchan = NULL;
456bbbbacb4SRoger A. Faulkner if (writer) {
457bbbbacb4SRoger A. Faulkner /*
458bbbbacb4SRoger A. Faulkner * Hand off the lock to the writer we will be waking.
459bbbbacb4SRoger A. Faulkner */
460bbbbacb4SRoger A. Faulkner ASSERT((*rwstate & ~URW_HAS_WAITERS) == 0);
461bbbbacb4SRoger A. Faulkner atomic_or_32(rwstate, URW_WRITE_LOCKED);
462bbbbacb4SRoger A. Faulkner rwlp->rwlock_owner = (uintptr_t)ulwp;
463bbbbacb4SRoger A. Faulkner }
46441efec22Sraf lwpid[nlwpid++] = ulwp->ul_lwpid;
46541efec22Sraf }
466bbbbacb4SRoger A. Faulkner
467bbbbacb4SRoger A. Faulkner /*
468bbbbacb4SRoger A. Faulkner * This modification of rwstate must be done last.
469bbbbacb4SRoger A. Faulkner * The presence of the URW_HAS_WAITERS flag causes all rwlock
470bbbbacb4SRoger A. Faulkner * code to go through the slow path, acquiring queue_lock(qp).
471bbbbacb4SRoger A. Faulkner * Otherwise the read_lock_try() and write_lock_try() fast paths
472bbbbacb4SRoger A. Faulkner * are effective.
473bbbbacb4SRoger A. Faulkner */
474d4204c85Sraf if (ulwpp == NULL)
47541efec22Sraf atomic_and_32(rwstate, ~URW_HAS_WAITERS);
476bbbbacb4SRoger A. Faulkner
47741efec22Sraf if (nlwpid == 0) {
47841efec22Sraf queue_unlock(qp);
47941efec22Sraf } else {
480d4204c85Sraf ulwp_t *self = curthread;
48141efec22Sraf no_preempt(self);
48241efec22Sraf queue_unlock(qp);
48341efec22Sraf if (nlwpid == 1)
48441efec22Sraf (void) __lwp_unpark(lwpid[0]);
48541efec22Sraf else
48641efec22Sraf (void) __lwp_unpark_all(lwpid, nlwpid);
48741efec22Sraf preempt(self);
48841efec22Sraf }
48941efec22Sraf if (lwpid != buffer)
4908cd45542Sraf (void) munmap((caddr_t)lwpid, maxlwps * sizeof (lwpid_t));
49141efec22Sraf }
4927c478bd9Sstevel@tonic-gate
4937c478bd9Sstevel@tonic-gate /*
4947c478bd9Sstevel@tonic-gate * Common code for rdlock, timedrdlock, wrlock, timedwrlock, tryrdlock,
4957c478bd9Sstevel@tonic-gate * and trywrlock for process-shared (USYNC_PROCESS) rwlocks.
4967c478bd9Sstevel@tonic-gate *
4977c478bd9Sstevel@tonic-gate * Note: if the lock appears to be contended we call __lwp_rwlock_rdlock()
4987c478bd9Sstevel@tonic-gate * or __lwp_rwlock_wrlock() holding the mutex. These return with the mutex
4997c478bd9Sstevel@tonic-gate * released, and if they need to sleep will release the mutex first. In the
5007c478bd9Sstevel@tonic-gate * event of a spurious wakeup, these will return EAGAIN (because it is much
5017c478bd9Sstevel@tonic-gate * easier for us to re-acquire the mutex here).
5027c478bd9Sstevel@tonic-gate */
5037c478bd9Sstevel@tonic-gate int
shared_rwlock_lock(rwlock_t * rwlp,timespec_t * tsp,int rd_wr)5047c478bd9Sstevel@tonic-gate shared_rwlock_lock(rwlock_t *rwlp, timespec_t *tsp, int rd_wr)
5057c478bd9Sstevel@tonic-gate {
50641efec22Sraf volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
50741efec22Sraf mutex_t *mp = &rwlp->mutex;
5087c478bd9Sstevel@tonic-gate int try_flag;
50941efec22Sraf int error;
5107c478bd9Sstevel@tonic-gate
5117c478bd9Sstevel@tonic-gate try_flag = (rd_wr & TRY_FLAG);
5127c478bd9Sstevel@tonic-gate rd_wr &= ~TRY_FLAG;
5137c478bd9Sstevel@tonic-gate ASSERT(rd_wr == READ_LOCK || rd_wr == WRITE_LOCK);
5147c478bd9Sstevel@tonic-gate
5157c478bd9Sstevel@tonic-gate if (!try_flag) {
5167c478bd9Sstevel@tonic-gate DTRACE_PROBE2(plockstat, rw__block, rwlp, rd_wr);
5177c478bd9Sstevel@tonic-gate }
5187c478bd9Sstevel@tonic-gate
5197c478bd9Sstevel@tonic-gate do {
52041efec22Sraf if (try_flag && (*rwstate & URW_WRITE_LOCKED)) {
52141efec22Sraf error = EBUSY;
5227c478bd9Sstevel@tonic-gate break;
52341efec22Sraf }
5248cd45542Sraf if ((error = mutex_lock(mp)) != 0)
52541efec22Sraf break;
5267c478bd9Sstevel@tonic-gate if (rd_wr == READ_LOCK) {
52741efec22Sraf if (read_lock_try(rwlp, 0)) {
5288cd45542Sraf (void) mutex_unlock(mp);
52941efec22Sraf break;
5307c478bd9Sstevel@tonic-gate }
5317c478bd9Sstevel@tonic-gate } else {
53241efec22Sraf if (write_lock_try(rwlp, 0)) {
5338cd45542Sraf (void) mutex_unlock(mp);
53441efec22Sraf break;
5357c478bd9Sstevel@tonic-gate }
53641efec22Sraf }
53741efec22Sraf atomic_or_32(rwstate, URW_HAS_WAITERS);
5385d2ed727SToomas Soome
5397f9dc0eeSBill Sommerfeld #ifdef DEBUG
5405d2ed727SToomas Soome uint32_t readers;
54141efec22Sraf readers = *rwstate;
54241efec22Sraf ASSERT_CONSISTENT_STATE(readers);
5435d2ed727SToomas Soome #endif
5447c478bd9Sstevel@tonic-gate /*
54541efec22Sraf * The calls to __lwp_rwlock_*() below will release the mutex,
546328cc3e9SRoger A. Faulkner * so we need a dtrace probe here. The owner field of the
547328cc3e9SRoger A. Faulkner * mutex is cleared in the kernel when the mutex is released,
548328cc3e9SRoger A. Faulkner * so we should not clear it here.
5497c478bd9Sstevel@tonic-gate */
55041efec22Sraf DTRACE_PROBE2(plockstat, mutex__release, mp, 0);
5517c478bd9Sstevel@tonic-gate /*
5527c478bd9Sstevel@tonic-gate * The waiters bit may be inaccurate.
5537c478bd9Sstevel@tonic-gate * Only the kernel knows for sure.
5547c478bd9Sstevel@tonic-gate */
55541efec22Sraf if (rd_wr == READ_LOCK) {
55641efec22Sraf if (try_flag)
55741efec22Sraf error = __lwp_rwlock_tryrdlock(rwlp);
55841efec22Sraf else
55941efec22Sraf error = __lwp_rwlock_rdlock(rwlp, tsp);
5607c478bd9Sstevel@tonic-gate } else {
56141efec22Sraf if (try_flag)
56241efec22Sraf error = __lwp_rwlock_trywrlock(rwlp);
56341efec22Sraf else
5647c478bd9Sstevel@tonic-gate error = __lwp_rwlock_wrlock(rwlp, tsp);
5657c478bd9Sstevel@tonic-gate }
56641efec22Sraf } while (error == EAGAIN || error == EINTR);
5677c478bd9Sstevel@tonic-gate
5687c478bd9Sstevel@tonic-gate if (!try_flag) {
56941efec22Sraf DTRACE_PROBE3(plockstat, rw__blocked, rwlp, rd_wr, error == 0);
5707c478bd9Sstevel@tonic-gate }
5717c478bd9Sstevel@tonic-gate
5727c478bd9Sstevel@tonic-gate return (error);
5737c478bd9Sstevel@tonic-gate }
5747c478bd9Sstevel@tonic-gate
5757c478bd9Sstevel@tonic-gate /*
5767c478bd9Sstevel@tonic-gate * Common code for rdlock, timedrdlock, wrlock, timedwrlock, tryrdlock,
5777c478bd9Sstevel@tonic-gate * and trywrlock for process-private (USYNC_THREAD) rwlocks.
5787c478bd9Sstevel@tonic-gate */
5797c478bd9Sstevel@tonic-gate int
rwlock_lock(rwlock_t * rwlp,timespec_t * tsp,int rd_wr)5807c478bd9Sstevel@tonic-gate rwlock_lock(rwlock_t *rwlp, timespec_t *tsp, int rd_wr)
5817c478bd9Sstevel@tonic-gate {
58241efec22Sraf volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
58341efec22Sraf uint32_t readers;
5847c478bd9Sstevel@tonic-gate ulwp_t *self = curthread;
5857c478bd9Sstevel@tonic-gate queue_head_t *qp;
5867c478bd9Sstevel@tonic-gate ulwp_t *ulwp;
5877c478bd9Sstevel@tonic-gate int try_flag;
588d4204c85Sraf int ignore_waiters_flag;
5897c478bd9Sstevel@tonic-gate int error = 0;
5907c478bd9Sstevel@tonic-gate
5917c478bd9Sstevel@tonic-gate try_flag = (rd_wr & TRY_FLAG);
5927c478bd9Sstevel@tonic-gate rd_wr &= ~TRY_FLAG;
5937c478bd9Sstevel@tonic-gate ASSERT(rd_wr == READ_LOCK || rd_wr == WRITE_LOCK);
5947c478bd9Sstevel@tonic-gate
5957c478bd9Sstevel@tonic-gate if (!try_flag) {
5967c478bd9Sstevel@tonic-gate DTRACE_PROBE2(plockstat, rw__block, rwlp, rd_wr);
5977c478bd9Sstevel@tonic-gate }
5987c478bd9Sstevel@tonic-gate
5997c478bd9Sstevel@tonic-gate qp = queue_lock(rwlp, MX);
600d4204c85Sraf /* initial attempt to acquire the lock fails if there are waiters */
601d4204c85Sraf ignore_waiters_flag = 0;
6027c478bd9Sstevel@tonic-gate while (error == 0) {
60341efec22Sraf if (rd_wr == READ_LOCK) {
604d4204c85Sraf if (read_lock_try(rwlp, ignore_waiters_flag))
605d4204c85Sraf break;
60641efec22Sraf } else {
607d4204c85Sraf if (write_lock_try(rwlp, ignore_waiters_flag))
608d4204c85Sraf break;
60941efec22Sraf }
610d4204c85Sraf /* subsequent attempts do not fail due to waiters */
611d4204c85Sraf ignore_waiters_flag = 1;
61241efec22Sraf atomic_or_32(rwstate, URW_HAS_WAITERS);
61341efec22Sraf readers = *rwstate;
61441efec22Sraf ASSERT_CONSISTENT_STATE(readers);
61541efec22Sraf if ((readers & URW_WRITE_LOCKED) ||
61641efec22Sraf (rd_wr == WRITE_LOCK &&
61741efec22Sraf (readers & URW_READERS_MASK) != 0))
6187c478bd9Sstevel@tonic-gate /* EMPTY */; /* somebody holds the lock */
619d4204c85Sraf else if ((ulwp = queue_waiter(qp)) == NULL) {
62041efec22Sraf atomic_and_32(rwstate, ~URW_HAS_WAITERS);
621bbbbacb4SRoger A. Faulkner ignore_waiters_flag = 0;
622bbbbacb4SRoger A. Faulkner continue; /* no queued waiters, start over */
6237c478bd9Sstevel@tonic-gate } else {
624d4204c85Sraf /*
625d4204c85Sraf * Do a priority check on the queued waiter (the
626d4204c85Sraf * highest priority thread on the queue) to see
62748bbca81SDaniel Hoffman * if we should defer to it or just grab the lock.
628d4204c85Sraf */
6297c478bd9Sstevel@tonic-gate int our_pri = real_priority(self);
6307c478bd9Sstevel@tonic-gate int his_pri = real_priority(ulwp);
6317c478bd9Sstevel@tonic-gate
6327c478bd9Sstevel@tonic-gate if (rd_wr == WRITE_LOCK) {
6337c478bd9Sstevel@tonic-gate /*
6347c478bd9Sstevel@tonic-gate * We defer to a queued thread that has
6357c478bd9Sstevel@tonic-gate * a higher priority than ours.
6367c478bd9Sstevel@tonic-gate */
637bbbbacb4SRoger A. Faulkner if (his_pri <= our_pri) {
638bbbbacb4SRoger A. Faulkner /*
639bbbbacb4SRoger A. Faulkner * Don't defer, just grab the lock.
640bbbbacb4SRoger A. Faulkner */
641bbbbacb4SRoger A. Faulkner continue;
642bbbbacb4SRoger A. Faulkner }
6437c478bd9Sstevel@tonic-gate } else {
6447c478bd9Sstevel@tonic-gate /*
6457c478bd9Sstevel@tonic-gate * We defer to a queued thread that has
6467c478bd9Sstevel@tonic-gate * a higher priority than ours or that
6477c478bd9Sstevel@tonic-gate * is a writer whose priority equals ours.
6487c478bd9Sstevel@tonic-gate */
6497c478bd9Sstevel@tonic-gate if (his_pri < our_pri ||
650bbbbacb4SRoger A. Faulkner (his_pri == our_pri && !ulwp->ul_writer)) {
651bbbbacb4SRoger A. Faulkner /*
652bbbbacb4SRoger A. Faulkner * Don't defer, just grab the lock.
653bbbbacb4SRoger A. Faulkner */
654bbbbacb4SRoger A. Faulkner continue;
655bbbbacb4SRoger A. Faulkner }
6567c478bd9Sstevel@tonic-gate }
6577c478bd9Sstevel@tonic-gate }
6587c478bd9Sstevel@tonic-gate /*
6597c478bd9Sstevel@tonic-gate * We are about to block.
6607c478bd9Sstevel@tonic-gate * If we're doing a trylock, return EBUSY instead.
6617c478bd9Sstevel@tonic-gate */
6627c478bd9Sstevel@tonic-gate if (try_flag) {
6637c478bd9Sstevel@tonic-gate error = EBUSY;
6647c478bd9Sstevel@tonic-gate break;
6657c478bd9Sstevel@tonic-gate }
6667c478bd9Sstevel@tonic-gate /*
667d4204c85Sraf * Enqueue writers ahead of readers.
6687c478bd9Sstevel@tonic-gate */
6697c478bd9Sstevel@tonic-gate self->ul_writer = rd_wr; /* *must* be 0 or 1 */
670d4204c85Sraf enqueue(qp, self, 0);
6717c478bd9Sstevel@tonic-gate set_parking_flag(self, 1);
6727c478bd9Sstevel@tonic-gate queue_unlock(qp);
6737c478bd9Sstevel@tonic-gate if ((error = __lwp_park(tsp, 0)) == EINTR)
674bbbbacb4SRoger A. Faulkner error = 0;
6757c478bd9Sstevel@tonic-gate set_parking_flag(self, 0);
6767c478bd9Sstevel@tonic-gate qp = queue_lock(rwlp, MX);
677bbbbacb4SRoger A. Faulkner if (self->ul_sleepq && dequeue_self(qp) == 0) {
67841efec22Sraf atomic_and_32(rwstate, ~URW_HAS_WAITERS);
679bbbbacb4SRoger A. Faulkner ignore_waiters_flag = 0;
6807c478bd9Sstevel@tonic-gate }
681bbbbacb4SRoger A. Faulkner self->ul_writer = 0;
682bbbbacb4SRoger A. Faulkner if (rd_wr == WRITE_LOCK &&
683bbbbacb4SRoger A. Faulkner (*rwstate & URW_WRITE_LOCKED) &&
684bbbbacb4SRoger A. Faulkner rwlp->rwlock_owner == (uintptr_t)self) {
685bbbbacb4SRoger A. Faulkner /*
686bbbbacb4SRoger A. Faulkner * We acquired the lock by hand-off
687bbbbacb4SRoger A. Faulkner * from the previous owner,
688bbbbacb4SRoger A. Faulkner */
689bbbbacb4SRoger A. Faulkner error = 0; /* timedlock did not fail */
690bbbbacb4SRoger A. Faulkner break;
691bbbbacb4SRoger A. Faulkner }
692bbbbacb4SRoger A. Faulkner }
693bbbbacb4SRoger A. Faulkner
694bbbbacb4SRoger A. Faulkner /*
695bbbbacb4SRoger A. Faulkner * Make one final check to see if there are any threads left
696bbbbacb4SRoger A. Faulkner * on the rwlock queue. Clear the URW_HAS_WAITERS flag if not.
697bbbbacb4SRoger A. Faulkner */
698bbbbacb4SRoger A. Faulkner if (qp->qh_root == NULL || qp->qh_root->qr_head == NULL)
699bbbbacb4SRoger A. Faulkner atomic_and_32(rwstate, ~URW_HAS_WAITERS);
7007c478bd9Sstevel@tonic-gate
70141efec22Sraf queue_unlock(qp);
70241efec22Sraf
70341efec22Sraf if (!try_flag) {
70441efec22Sraf DTRACE_PROBE3(plockstat, rw__blocked, rwlp, rd_wr, error == 0);
70541efec22Sraf }
7067c478bd9Sstevel@tonic-gate
7077c478bd9Sstevel@tonic-gate return (error);
7087c478bd9Sstevel@tonic-gate }
7097c478bd9Sstevel@tonic-gate
7107c478bd9Sstevel@tonic-gate int
rw_rdlock_impl(rwlock_t * rwlp,timespec_t * tsp)7117c478bd9Sstevel@tonic-gate rw_rdlock_impl(rwlock_t *rwlp, timespec_t *tsp)
7127c478bd9Sstevel@tonic-gate {
7137c478bd9Sstevel@tonic-gate ulwp_t *self = curthread;
7147c478bd9Sstevel@tonic-gate uberdata_t *udp = self->ul_uberdata;
7157c478bd9Sstevel@tonic-gate readlock_t *readlockp;
7167c478bd9Sstevel@tonic-gate tdb_rwlock_stats_t *rwsp = RWLOCK_STATS(rwlp, udp);
7177c478bd9Sstevel@tonic-gate int error;
7187c478bd9Sstevel@tonic-gate
7197c478bd9Sstevel@tonic-gate /*
7207c478bd9Sstevel@tonic-gate * If we already hold a readers lock on this rwlock,
7217c478bd9Sstevel@tonic-gate * just increment our reference count and return.
7227c478bd9Sstevel@tonic-gate */
72341efec22Sraf sigoff(self);
7247c478bd9Sstevel@tonic-gate readlockp = rwl_entry(rwlp);
7257c478bd9Sstevel@tonic-gate if (readlockp->rd_count != 0) {
72641efec22Sraf if (readlockp->rd_count == READ_LOCK_MAX) {
72741efec22Sraf sigon(self);
72841efec22Sraf error = EAGAIN;
72941efec22Sraf goto out;
7307c478bd9Sstevel@tonic-gate }
73141efec22Sraf sigon(self);
73241efec22Sraf error = 0;
73341efec22Sraf goto out;
73441efec22Sraf }
73541efec22Sraf sigon(self);
7367c478bd9Sstevel@tonic-gate
7377c478bd9Sstevel@tonic-gate /*
7387c478bd9Sstevel@tonic-gate * If we hold the writer lock, bail out.
7397c478bd9Sstevel@tonic-gate */
7407257d1b4Sraf if (rw_write_held(rwlp)) {
7417c478bd9Sstevel@tonic-gate if (self->ul_error_detection)
7427c478bd9Sstevel@tonic-gate rwlock_error(rwlp, "rwlock_rdlock",
7437c478bd9Sstevel@tonic-gate "calling thread owns the writer lock");
74441efec22Sraf error = EDEADLK;
74541efec22Sraf goto out;
7467c478bd9Sstevel@tonic-gate }
7477c478bd9Sstevel@tonic-gate
74841efec22Sraf if (read_lock_try(rwlp, 0))
74941efec22Sraf error = 0;
75041efec22Sraf else if (rwlp->rwlock_type == USYNC_PROCESS) /* kernel-level */
7517c478bd9Sstevel@tonic-gate error = shared_rwlock_lock(rwlp, tsp, READ_LOCK);
7527c478bd9Sstevel@tonic-gate else /* user-level */
7537c478bd9Sstevel@tonic-gate error = rwlock_lock(rwlp, tsp, READ_LOCK);
7547c478bd9Sstevel@tonic-gate
75541efec22Sraf out:
7567c478bd9Sstevel@tonic-gate if (error == 0) {
75741efec22Sraf sigoff(self);
75841efec22Sraf rwl_entry(rwlp)->rd_count++;
75941efec22Sraf sigon(self);
7607c478bd9Sstevel@tonic-gate if (rwsp)
7617c478bd9Sstevel@tonic-gate tdb_incr(rwsp->rw_rdlock);
76241efec22Sraf DTRACE_PROBE2(plockstat, rw__acquire, rwlp, READ_LOCK);
76341efec22Sraf } else {
76441efec22Sraf DTRACE_PROBE3(plockstat, rw__error, rwlp, READ_LOCK, error);
7657c478bd9Sstevel@tonic-gate }
7667c478bd9Sstevel@tonic-gate
7677c478bd9Sstevel@tonic-gate return (error);
7687c478bd9Sstevel@tonic-gate }
7697c478bd9Sstevel@tonic-gate
7707257d1b4Sraf #pragma weak pthread_rwlock_rdlock = rw_rdlock
7717257d1b4Sraf #pragma weak _rw_rdlock = rw_rdlock
7727c478bd9Sstevel@tonic-gate int
rw_rdlock(rwlock_t * rwlp)7737257d1b4Sraf rw_rdlock(rwlock_t *rwlp)
7747c478bd9Sstevel@tonic-gate {
7757c478bd9Sstevel@tonic-gate ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
7767c478bd9Sstevel@tonic-gate return (rw_rdlock_impl(rwlp, NULL));
7777c478bd9Sstevel@tonic-gate }
7787c478bd9Sstevel@tonic-gate
7797c478bd9Sstevel@tonic-gate void
lrw_rdlock(rwlock_t * rwlp)7807c478bd9Sstevel@tonic-gate lrw_rdlock(rwlock_t *rwlp)
7817c478bd9Sstevel@tonic-gate {
7827c478bd9Sstevel@tonic-gate enter_critical(curthread);
7837c478bd9Sstevel@tonic-gate (void) rw_rdlock_impl(rwlp, NULL);
7847c478bd9Sstevel@tonic-gate }
7857c478bd9Sstevel@tonic-gate
7867c478bd9Sstevel@tonic-gate int
pthread_rwlock_relclockrdlock_np(pthread_rwlock_t * restrict rwlp,clockid_t clock,const struct timespec * restrict reltime)787*8b6b46dcSRobert Mustacchi pthread_rwlock_relclockrdlock_np(pthread_rwlock_t *restrict rwlp,
788*8b6b46dcSRobert Mustacchi clockid_t clock, const struct timespec *restrict reltime)
7897c478bd9Sstevel@tonic-gate {
7907c478bd9Sstevel@tonic-gate timespec_t tslocal = *reltime;
7917c478bd9Sstevel@tonic-gate int error;
7927c478bd9Sstevel@tonic-gate
7937c478bd9Sstevel@tonic-gate ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
794*8b6b46dcSRobert Mustacchi
795*8b6b46dcSRobert Mustacchi switch (clock) {
796*8b6b46dcSRobert Mustacchi case CLOCK_REALTIME:
797*8b6b46dcSRobert Mustacchi case CLOCK_HIGHRES:
798*8b6b46dcSRobert Mustacchi break;
799*8b6b46dcSRobert Mustacchi default:
800*8b6b46dcSRobert Mustacchi return (EINVAL);
801*8b6b46dcSRobert Mustacchi }
802*8b6b46dcSRobert Mustacchi
8037257d1b4Sraf error = rw_rdlock_impl((rwlock_t *)rwlp, &tslocal);
8047c478bd9Sstevel@tonic-gate if (error == ETIME)
8057c478bd9Sstevel@tonic-gate error = ETIMEDOUT;
8067c478bd9Sstevel@tonic-gate return (error);
8077c478bd9Sstevel@tonic-gate }
8087c478bd9Sstevel@tonic-gate
8097c478bd9Sstevel@tonic-gate int
pthread_rwlock_reltimedrdlock_np(pthread_rwlock_t * restrict rwlp,const struct timespec * restrict reltime)810*8b6b46dcSRobert Mustacchi pthread_rwlock_reltimedrdlock_np(pthread_rwlock_t *restrict rwlp,
811*8b6b46dcSRobert Mustacchi const struct timespec *restrict reltime)
812*8b6b46dcSRobert Mustacchi {
813*8b6b46dcSRobert Mustacchi return (pthread_rwlock_relclockrdlock_np(rwlp, CLOCK_REALTIME,
814*8b6b46dcSRobert Mustacchi reltime));
815*8b6b46dcSRobert Mustacchi }
816*8b6b46dcSRobert Mustacchi
817*8b6b46dcSRobert Mustacchi int
pthread_rwlock_clockrdlock(pthread_rwlock_t * restrict rwlp,clockid_t clock,const struct timespec * restrict abstime)818*8b6b46dcSRobert Mustacchi pthread_rwlock_clockrdlock(pthread_rwlock_t *restrict rwlp, clockid_t clock,
819*8b6b46dcSRobert Mustacchi const struct timespec *restrict abstime)
8207c478bd9Sstevel@tonic-gate {
8217c478bd9Sstevel@tonic-gate timespec_t tslocal;
8227c478bd9Sstevel@tonic-gate int error;
8237c478bd9Sstevel@tonic-gate
8247c478bd9Sstevel@tonic-gate ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
825*8b6b46dcSRobert Mustacchi
826*8b6b46dcSRobert Mustacchi switch (clock) {
827*8b6b46dcSRobert Mustacchi case CLOCK_REALTIME:
828*8b6b46dcSRobert Mustacchi case CLOCK_HIGHRES:
829*8b6b46dcSRobert Mustacchi break;
830*8b6b46dcSRobert Mustacchi default:
831*8b6b46dcSRobert Mustacchi return (EINVAL);
832*8b6b46dcSRobert Mustacchi }
833*8b6b46dcSRobert Mustacchi
834*8b6b46dcSRobert Mustacchi abstime_to_reltime(clock, abstime, &tslocal);
8357257d1b4Sraf error = rw_rdlock_impl((rwlock_t *)rwlp, &tslocal);
8367c478bd9Sstevel@tonic-gate if (error == ETIME)
8377c478bd9Sstevel@tonic-gate error = ETIMEDOUT;
8387c478bd9Sstevel@tonic-gate return (error);
8397c478bd9Sstevel@tonic-gate }
8407c478bd9Sstevel@tonic-gate
8417c478bd9Sstevel@tonic-gate int
pthread_rwlock_timedrdlock(pthread_rwlock_t * restrict rwlp,const struct timespec * restrict abstime)842*8b6b46dcSRobert Mustacchi pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rwlp,
843*8b6b46dcSRobert Mustacchi const struct timespec *restrict abstime)
844*8b6b46dcSRobert Mustacchi {
845*8b6b46dcSRobert Mustacchi return (pthread_rwlock_clockrdlock(rwlp, CLOCK_REALTIME, abstime));
846*8b6b46dcSRobert Mustacchi }
847*8b6b46dcSRobert Mustacchi
848*8b6b46dcSRobert Mustacchi int
rw_wrlock_impl(rwlock_t * rwlp,timespec_t * tsp)8497c478bd9Sstevel@tonic-gate rw_wrlock_impl(rwlock_t *rwlp, timespec_t *tsp)
8507c478bd9Sstevel@tonic-gate {
8517c478bd9Sstevel@tonic-gate ulwp_t *self = curthread;
8527c478bd9Sstevel@tonic-gate uberdata_t *udp = self->ul_uberdata;
8537c478bd9Sstevel@tonic-gate tdb_rwlock_stats_t *rwsp = RWLOCK_STATS(rwlp, udp);
8547c478bd9Sstevel@tonic-gate int error;
8557c478bd9Sstevel@tonic-gate
8567c478bd9Sstevel@tonic-gate /*
8577c478bd9Sstevel@tonic-gate * If we hold a readers lock on this rwlock, bail out.
8587c478bd9Sstevel@tonic-gate */
8597257d1b4Sraf if (rw_read_held(rwlp)) {
8607c478bd9Sstevel@tonic-gate if (self->ul_error_detection)
8617c478bd9Sstevel@tonic-gate rwlock_error(rwlp, "rwlock_wrlock",
8627c478bd9Sstevel@tonic-gate "calling thread owns the readers lock");
86341efec22Sraf error = EDEADLK;
86441efec22Sraf goto out;
8657c478bd9Sstevel@tonic-gate }
8667c478bd9Sstevel@tonic-gate
8677c478bd9Sstevel@tonic-gate /*
8687c478bd9Sstevel@tonic-gate * If we hold the writer lock, bail out.
8697c478bd9Sstevel@tonic-gate */
8707257d1b4Sraf if (rw_write_held(rwlp)) {
8717c478bd9Sstevel@tonic-gate if (self->ul_error_detection)
8727c478bd9Sstevel@tonic-gate rwlock_error(rwlp, "rwlock_wrlock",
8737c478bd9Sstevel@tonic-gate "calling thread owns the writer lock");
87441efec22Sraf error = EDEADLK;
87541efec22Sraf goto out;
8767c478bd9Sstevel@tonic-gate }
8777c478bd9Sstevel@tonic-gate
87841efec22Sraf if (write_lock_try(rwlp, 0))
87941efec22Sraf error = 0;
88041efec22Sraf else if (rwlp->rwlock_type == USYNC_PROCESS) /* kernel-level */
8817c478bd9Sstevel@tonic-gate error = shared_rwlock_lock(rwlp, tsp, WRITE_LOCK);
88241efec22Sraf else /* user-level */
8837c478bd9Sstevel@tonic-gate error = rwlock_lock(rwlp, tsp, WRITE_LOCK);
8847c478bd9Sstevel@tonic-gate
88541efec22Sraf out:
88641efec22Sraf if (error == 0) {
88741efec22Sraf rwlp->rwlock_owner = (uintptr_t)self;
88841efec22Sraf if (rwlp->rwlock_type == USYNC_PROCESS)
88941efec22Sraf rwlp->rwlock_ownerpid = udp->pid;
89041efec22Sraf if (rwsp) {
8917c478bd9Sstevel@tonic-gate tdb_incr(rwsp->rw_wrlock);
8927c478bd9Sstevel@tonic-gate rwsp->rw_wrlock_begin_hold = gethrtime();
8937c478bd9Sstevel@tonic-gate }
89441efec22Sraf DTRACE_PROBE2(plockstat, rw__acquire, rwlp, WRITE_LOCK);
89541efec22Sraf } else {
89641efec22Sraf DTRACE_PROBE3(plockstat, rw__error, rwlp, WRITE_LOCK, error);
89741efec22Sraf }
8987c478bd9Sstevel@tonic-gate return (error);
8997c478bd9Sstevel@tonic-gate }
9007c478bd9Sstevel@tonic-gate
9017257d1b4Sraf #pragma weak pthread_rwlock_wrlock = rw_wrlock
9027257d1b4Sraf #pragma weak _rw_wrlock = rw_wrlock
9037c478bd9Sstevel@tonic-gate int
rw_wrlock(rwlock_t * rwlp)9047257d1b4Sraf rw_wrlock(rwlock_t *rwlp)
9057c478bd9Sstevel@tonic-gate {
9067c478bd9Sstevel@tonic-gate ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
9077c478bd9Sstevel@tonic-gate return (rw_wrlock_impl(rwlp, NULL));
9087c478bd9Sstevel@tonic-gate }
9097c478bd9Sstevel@tonic-gate
9107c478bd9Sstevel@tonic-gate void
lrw_wrlock(rwlock_t * rwlp)9117c478bd9Sstevel@tonic-gate lrw_wrlock(rwlock_t *rwlp)
9127c478bd9Sstevel@tonic-gate {
9137c478bd9Sstevel@tonic-gate enter_critical(curthread);
9147c478bd9Sstevel@tonic-gate (void) rw_wrlock_impl(rwlp, NULL);
9157c478bd9Sstevel@tonic-gate }
9167c478bd9Sstevel@tonic-gate
9177c478bd9Sstevel@tonic-gate int
pthread_rwlock_relclockwrlock_np(pthread_rwlock_t * restrict rwlp,clockid_t clock,const struct timespec * restrict reltime)918*8b6b46dcSRobert Mustacchi pthread_rwlock_relclockwrlock_np(pthread_rwlock_t *restrict rwlp,
919*8b6b46dcSRobert Mustacchi clockid_t clock, const struct timespec *restrict reltime)
9207c478bd9Sstevel@tonic-gate {
9217c478bd9Sstevel@tonic-gate timespec_t tslocal = *reltime;
9227c478bd9Sstevel@tonic-gate int error;
9237c478bd9Sstevel@tonic-gate
9247c478bd9Sstevel@tonic-gate ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
925*8b6b46dcSRobert Mustacchi
926*8b6b46dcSRobert Mustacchi switch (clock) {
927*8b6b46dcSRobert Mustacchi case CLOCK_REALTIME:
928*8b6b46dcSRobert Mustacchi case CLOCK_HIGHRES:
929*8b6b46dcSRobert Mustacchi break;
930*8b6b46dcSRobert Mustacchi default:
931*8b6b46dcSRobert Mustacchi return (EINVAL);
932*8b6b46dcSRobert Mustacchi }
933*8b6b46dcSRobert Mustacchi
934*8b6b46dcSRobert Mustacchi error = rw_wrlock_impl((rwlock_t *)rwlp, &tslocal);
935*8b6b46dcSRobert Mustacchi if (error == ETIME)
936*8b6b46dcSRobert Mustacchi error = ETIMEDOUT;
937*8b6b46dcSRobert Mustacchi return (error);
938*8b6b46dcSRobert Mustacchi }
939*8b6b46dcSRobert Mustacchi
940*8b6b46dcSRobert Mustacchi int
pthread_rwlock_reltimedwrlock_np(pthread_rwlock_t * restrict rwlp,const struct timespec * restrict reltime)941*8b6b46dcSRobert Mustacchi pthread_rwlock_reltimedwrlock_np(pthread_rwlock_t *restrict rwlp,
942*8b6b46dcSRobert Mustacchi const struct timespec *restrict reltime)
943*8b6b46dcSRobert Mustacchi {
944*8b6b46dcSRobert Mustacchi return (pthread_rwlock_relclockwrlock_np(rwlp, CLOCK_REALTIME,
945*8b6b46dcSRobert Mustacchi reltime));
946*8b6b46dcSRobert Mustacchi }
947*8b6b46dcSRobert Mustacchi
948*8b6b46dcSRobert Mustacchi int
pthread_rwlock_clockwrlock(pthread_rwlock_t * rwlp,clockid_t clock,const timespec_t * abstime)949*8b6b46dcSRobert Mustacchi pthread_rwlock_clockwrlock(pthread_rwlock_t *rwlp, clockid_t clock,
950*8b6b46dcSRobert Mustacchi const timespec_t *abstime)
951*8b6b46dcSRobert Mustacchi {
952*8b6b46dcSRobert Mustacchi timespec_t tslocal;
953*8b6b46dcSRobert Mustacchi int error;
954*8b6b46dcSRobert Mustacchi
955*8b6b46dcSRobert Mustacchi ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
956*8b6b46dcSRobert Mustacchi
957*8b6b46dcSRobert Mustacchi switch (clock) {
958*8b6b46dcSRobert Mustacchi case CLOCK_REALTIME:
959*8b6b46dcSRobert Mustacchi case CLOCK_HIGHRES:
960*8b6b46dcSRobert Mustacchi break;
961*8b6b46dcSRobert Mustacchi default:
962*8b6b46dcSRobert Mustacchi return (EINVAL);
963*8b6b46dcSRobert Mustacchi }
964*8b6b46dcSRobert Mustacchi
965*8b6b46dcSRobert Mustacchi abstime_to_reltime(clock, abstime, &tslocal);
9667257d1b4Sraf error = rw_wrlock_impl((rwlock_t *)rwlp, &tslocal);
9677c478bd9Sstevel@tonic-gate if (error == ETIME)
9687c478bd9Sstevel@tonic-gate error = ETIMEDOUT;
9697c478bd9Sstevel@tonic-gate return (error);
9707c478bd9Sstevel@tonic-gate }
9717c478bd9Sstevel@tonic-gate
9727c478bd9Sstevel@tonic-gate int
pthread_rwlock_timedwrlock(pthread_rwlock_t * rwlp,const timespec_t * abstime)9737257d1b4Sraf pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlp, const timespec_t *abstime)
9747c478bd9Sstevel@tonic-gate {
975*8b6b46dcSRobert Mustacchi return (pthread_rwlock_clockwrlock(rwlp, CLOCK_REALTIME, abstime));
9767c478bd9Sstevel@tonic-gate }
9777c478bd9Sstevel@tonic-gate
9787257d1b4Sraf #pragma weak pthread_rwlock_tryrdlock = rw_tryrdlock
9797c478bd9Sstevel@tonic-gate int
rw_tryrdlock(rwlock_t * rwlp)9807257d1b4Sraf rw_tryrdlock(rwlock_t *rwlp)
9817c478bd9Sstevel@tonic-gate {
9827c478bd9Sstevel@tonic-gate ulwp_t *self = curthread;
9837c478bd9Sstevel@tonic-gate uberdata_t *udp = self->ul_uberdata;
9847c478bd9Sstevel@tonic-gate tdb_rwlock_stats_t *rwsp = RWLOCK_STATS(rwlp, udp);
9857c478bd9Sstevel@tonic-gate readlock_t *readlockp;
9867c478bd9Sstevel@tonic-gate int error;
9877c478bd9Sstevel@tonic-gate
9887c478bd9Sstevel@tonic-gate ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
9897c478bd9Sstevel@tonic-gate
9907c478bd9Sstevel@tonic-gate if (rwsp)
9917c478bd9Sstevel@tonic-gate tdb_incr(rwsp->rw_rdlock_try);
9927c478bd9Sstevel@tonic-gate
9937c478bd9Sstevel@tonic-gate /*
9947c478bd9Sstevel@tonic-gate * If we already hold a readers lock on this rwlock,
9957c478bd9Sstevel@tonic-gate * just increment our reference count and return.
9967c478bd9Sstevel@tonic-gate */
99741efec22Sraf sigoff(self);
9987c478bd9Sstevel@tonic-gate readlockp = rwl_entry(rwlp);
9997c478bd9Sstevel@tonic-gate if (readlockp->rd_count != 0) {
100041efec22Sraf if (readlockp->rd_count == READ_LOCK_MAX) {
100141efec22Sraf sigon(self);
100241efec22Sraf error = EAGAIN;
100341efec22Sraf goto out;
10047c478bd9Sstevel@tonic-gate }
100541efec22Sraf sigon(self);
100641efec22Sraf error = 0;
100741efec22Sraf goto out;
100841efec22Sraf }
100941efec22Sraf sigon(self);
10107c478bd9Sstevel@tonic-gate
101141efec22Sraf if (read_lock_try(rwlp, 0))
101241efec22Sraf error = 0;
101341efec22Sraf else if (rwlp->rwlock_type == USYNC_PROCESS) /* kernel-level */
10147c478bd9Sstevel@tonic-gate error = shared_rwlock_lock(rwlp, NULL, READ_LOCK_TRY);
10157c478bd9Sstevel@tonic-gate else /* user-level */
10167c478bd9Sstevel@tonic-gate error = rwlock_lock(rwlp, NULL, READ_LOCK_TRY);
10177c478bd9Sstevel@tonic-gate
101841efec22Sraf out:
101941efec22Sraf if (error == 0) {
102041efec22Sraf sigoff(self);
102141efec22Sraf rwl_entry(rwlp)->rd_count++;
102241efec22Sraf sigon(self);
102341efec22Sraf DTRACE_PROBE2(plockstat, rw__acquire, rwlp, READ_LOCK);
102441efec22Sraf } else {
102541efec22Sraf if (rwsp)
10267c478bd9Sstevel@tonic-gate tdb_incr(rwsp->rw_rdlock_try_fail);
102741efec22Sraf if (error != EBUSY) {
102841efec22Sraf DTRACE_PROBE3(plockstat, rw__error, rwlp, READ_LOCK,
102941efec22Sraf error);
103041efec22Sraf }
103141efec22Sraf }
10327c478bd9Sstevel@tonic-gate
10337c478bd9Sstevel@tonic-gate return (error);
10347c478bd9Sstevel@tonic-gate }
10357c478bd9Sstevel@tonic-gate
10367257d1b4Sraf #pragma weak pthread_rwlock_trywrlock = rw_trywrlock
10377c478bd9Sstevel@tonic-gate int
rw_trywrlock(rwlock_t * rwlp)10387257d1b4Sraf rw_trywrlock(rwlock_t *rwlp)
10397c478bd9Sstevel@tonic-gate {
10407c478bd9Sstevel@tonic-gate ulwp_t *self = curthread;
10417c478bd9Sstevel@tonic-gate uberdata_t *udp = self->ul_uberdata;
10427c478bd9Sstevel@tonic-gate tdb_rwlock_stats_t *rwsp = RWLOCK_STATS(rwlp, udp);
10437c478bd9Sstevel@tonic-gate int error;
10447c478bd9Sstevel@tonic-gate
104541efec22Sraf ASSERT(!self->ul_critical || self->ul_bindflags);
10467c478bd9Sstevel@tonic-gate
10477c478bd9Sstevel@tonic-gate if (rwsp)
10487c478bd9Sstevel@tonic-gate tdb_incr(rwsp->rw_wrlock_try);
10497c478bd9Sstevel@tonic-gate
105041efec22Sraf if (write_lock_try(rwlp, 0))
105141efec22Sraf error = 0;
105241efec22Sraf else if (rwlp->rwlock_type == USYNC_PROCESS) /* kernel-level */
10537c478bd9Sstevel@tonic-gate error = shared_rwlock_lock(rwlp, NULL, WRITE_LOCK_TRY);
105441efec22Sraf else /* user-level */
10557c478bd9Sstevel@tonic-gate error = rwlock_lock(rwlp, NULL, WRITE_LOCK_TRY);
105641efec22Sraf
105741efec22Sraf if (error == 0) {
105841efec22Sraf rwlp->rwlock_owner = (uintptr_t)self;
105941efec22Sraf if (rwlp->rwlock_type == USYNC_PROCESS)
106041efec22Sraf rwlp->rwlock_ownerpid = udp->pid;
106141efec22Sraf if (rwsp)
10627c478bd9Sstevel@tonic-gate rwsp->rw_wrlock_begin_hold = gethrtime();
106341efec22Sraf DTRACE_PROBE2(plockstat, rw__acquire, rwlp, WRITE_LOCK);
106441efec22Sraf } else {
106541efec22Sraf if (rwsp)
106641efec22Sraf tdb_incr(rwsp->rw_wrlock_try_fail);
106741efec22Sraf if (error != EBUSY) {
106841efec22Sraf DTRACE_PROBE3(plockstat, rw__error, rwlp, WRITE_LOCK,
106941efec22Sraf error);
107041efec22Sraf }
10717c478bd9Sstevel@tonic-gate }
10727c478bd9Sstevel@tonic-gate return (error);
10737c478bd9Sstevel@tonic-gate }
10747c478bd9Sstevel@tonic-gate
10757257d1b4Sraf #pragma weak pthread_rwlock_unlock = rw_unlock
10767257d1b4Sraf #pragma weak _rw_unlock = rw_unlock
10777c478bd9Sstevel@tonic-gate int
rw_unlock(rwlock_t * rwlp)10787257d1b4Sraf rw_unlock(rwlock_t *rwlp)
10797c478bd9Sstevel@tonic-gate {
108041efec22Sraf volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
108141efec22Sraf uint32_t readers;
10827c478bd9Sstevel@tonic-gate ulwp_t *self = curthread;
10837c478bd9Sstevel@tonic-gate uberdata_t *udp = self->ul_uberdata;
10847c478bd9Sstevel@tonic-gate tdb_rwlock_stats_t *rwsp;
108541efec22Sraf int rd_wr;
10867c478bd9Sstevel@tonic-gate
108741efec22Sraf readers = *rwstate;
108841efec22Sraf ASSERT_CONSISTENT_STATE(readers);
108941efec22Sraf if (readers & URW_WRITE_LOCKED) {
109041efec22Sraf rd_wr = WRITE_LOCK;
109141efec22Sraf readers = 0;
109241efec22Sraf } else {
109341efec22Sraf rd_wr = READ_LOCK;
109441efec22Sraf readers &= URW_READERS_MASK;
10957c478bd9Sstevel@tonic-gate }
10967c478bd9Sstevel@tonic-gate
109741efec22Sraf if (rd_wr == WRITE_LOCK) {
10987c478bd9Sstevel@tonic-gate /*
10997c478bd9Sstevel@tonic-gate * Since the writer lock is held, we'd better be
11007c478bd9Sstevel@tonic-gate * holding it, else we cannot legitimately be here.
11017c478bd9Sstevel@tonic-gate */
11027257d1b4Sraf if (!rw_write_held(rwlp)) {
11037c478bd9Sstevel@tonic-gate if (self->ul_error_detection)
11047c478bd9Sstevel@tonic-gate rwlock_error(rwlp, "rwlock_unlock",
11057c478bd9Sstevel@tonic-gate "writer lock held, "
11067c478bd9Sstevel@tonic-gate "but not by the calling thread");
11077c478bd9Sstevel@tonic-gate return (EPERM);
11087c478bd9Sstevel@tonic-gate }
11097c478bd9Sstevel@tonic-gate if ((rwsp = RWLOCK_STATS(rwlp, udp)) != NULL) {
11107c478bd9Sstevel@tonic-gate if (rwsp->rw_wrlock_begin_hold)
11117c478bd9Sstevel@tonic-gate rwsp->rw_wrlock_hold_time +=
11127c478bd9Sstevel@tonic-gate gethrtime() - rwsp->rw_wrlock_begin_hold;
11137c478bd9Sstevel@tonic-gate rwsp->rw_wrlock_begin_hold = 0;
11147c478bd9Sstevel@tonic-gate }
111541efec22Sraf rwlp->rwlock_owner = 0;
111641efec22Sraf rwlp->rwlock_ownerpid = 0;
111741efec22Sraf } else if (readers > 0) {
11187c478bd9Sstevel@tonic-gate /*
11197c478bd9Sstevel@tonic-gate * A readers lock is held; if we don't hold one, bail out.
11207c478bd9Sstevel@tonic-gate */
112141efec22Sraf readlock_t *readlockp;
112241efec22Sraf
112341efec22Sraf sigoff(self);
112441efec22Sraf readlockp = rwl_entry(rwlp);
11257c478bd9Sstevel@tonic-gate if (readlockp->rd_count == 0) {
112641efec22Sraf sigon(self);
11277c478bd9Sstevel@tonic-gate if (self->ul_error_detection)
11287c478bd9Sstevel@tonic-gate rwlock_error(rwlp, "rwlock_unlock",
11297c478bd9Sstevel@tonic-gate "readers lock held, "
11307c478bd9Sstevel@tonic-gate "but not by the calling thread");
11317c478bd9Sstevel@tonic-gate return (EPERM);
11327c478bd9Sstevel@tonic-gate }
11337c478bd9Sstevel@tonic-gate /*
11347c478bd9Sstevel@tonic-gate * If we hold more than one readers lock on this rwlock,
11357c478bd9Sstevel@tonic-gate * just decrement our reference count and return.
11367c478bd9Sstevel@tonic-gate */
11377c478bd9Sstevel@tonic-gate if (--readlockp->rd_count != 0) {
113841efec22Sraf sigon(self);
113941efec22Sraf goto out;
11407c478bd9Sstevel@tonic-gate }
114141efec22Sraf sigon(self);
11427c478bd9Sstevel@tonic-gate } else {
11437c478bd9Sstevel@tonic-gate /*
11447c478bd9Sstevel@tonic-gate * This is a usage error.
11457c478bd9Sstevel@tonic-gate * No thread should release an unowned lock.
11467c478bd9Sstevel@tonic-gate */
11477c478bd9Sstevel@tonic-gate if (self->ul_error_detection)
11487c478bd9Sstevel@tonic-gate rwlock_error(rwlp, "rwlock_unlock", "lock not owned");
11497c478bd9Sstevel@tonic-gate return (EPERM);
11507c478bd9Sstevel@tonic-gate }
11517c478bd9Sstevel@tonic-gate
115241efec22Sraf if (rd_wr == WRITE_LOCK && write_unlock_try(rwlp)) {
115341efec22Sraf /* EMPTY */;
115441efec22Sraf } else if (rd_wr == READ_LOCK && read_unlock_try(rwlp)) {
115541efec22Sraf /* EMPTY */;
115641efec22Sraf } else if (rwlp->rwlock_type == USYNC_PROCESS) {
11578cd45542Sraf (void) mutex_lock(&rwlp->mutex);
115841efec22Sraf (void) __lwp_rwlock_unlock(rwlp);
11598cd45542Sraf (void) mutex_unlock(&rwlp->mutex);
11607c478bd9Sstevel@tonic-gate } else {
1161bbbbacb4SRoger A. Faulkner rw_queue_release(rwlp);
11627c478bd9Sstevel@tonic-gate }
11637c478bd9Sstevel@tonic-gate
116441efec22Sraf out:
116541efec22Sraf DTRACE_PROBE2(plockstat, rw__release, rwlp, rd_wr);
11667c478bd9Sstevel@tonic-gate return (0);
11677c478bd9Sstevel@tonic-gate }
11687c478bd9Sstevel@tonic-gate
11697c478bd9Sstevel@tonic-gate void
lrw_unlock(rwlock_t * rwlp)11707c478bd9Sstevel@tonic-gate lrw_unlock(rwlock_t *rwlp)
11717c478bd9Sstevel@tonic-gate {
11727257d1b4Sraf (void) rw_unlock(rwlp);
11737c478bd9Sstevel@tonic-gate exit_critical(curthread);
11747c478bd9Sstevel@tonic-gate }
1175