xref: /illumos-gate/usr/src/lib/libc/port/threads/rwlock.c (revision a74f7440e9d4ba2cf59e6cbfc445479a28170f2a)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include "lint.h"
30 #include "thr_uberdata.h"
31 #include <sys/sdt.h>
32 
33 #define	TRY_FLAG		0x10
34 #define	READ_LOCK		0
35 #define	WRITE_LOCK		1
36 #define	READ_LOCK_TRY		(READ_LOCK | TRY_FLAG)
37 #define	WRITE_LOCK_TRY		(WRITE_LOCK | TRY_FLAG)
38 
39 #define	NLOCKS	4	/* initial number of readlock_t structs allocated */
40 
41 #define	ASSERT_CONSISTENT_STATE(readers)		\
42 	ASSERT(!((readers) & URW_WRITE_LOCKED) ||	\
43 		((readers) & ~URW_HAS_WAITERS) == URW_WRITE_LOCKED)
44 
45 /*
46  * Find/allocate an entry for rwlp in our array of rwlocks held for reading.
47  * We must be deferring signals for this to be safe.
48  * Else if we are returning an entry with ul_rdlockcnt == 0,
49  * it could be reassigned behind our back in a signal handler.
50  */
51 static readlock_t *
52 rwl_entry(rwlock_t *rwlp)
53 {
54 	ulwp_t *self = curthread;
55 	readlock_t *remembered = NULL;
56 	readlock_t *readlockp;
57 	uint_t nlocks;
58 
59 	/* we must be deferring signals */
60 	ASSERT((self->ul_critical + self->ul_sigdefer) != 0);
61 
62 	if ((nlocks = self->ul_rdlockcnt) != 0)
63 		readlockp = self->ul_readlock.array;
64 	else {
65 		nlocks = 1;
66 		readlockp = &self->ul_readlock.single;
67 	}
68 
69 	for (; nlocks; nlocks--, readlockp++) {
70 		if (readlockp->rd_rwlock == rwlp)
71 			return (readlockp);
72 		if (readlockp->rd_count == 0 && remembered == NULL)
73 			remembered = readlockp;
74 	}
75 	if (remembered != NULL) {
76 		remembered->rd_rwlock = rwlp;
77 		return (remembered);
78 	}
79 
80 	/*
81 	 * No entry available.  Allocate more space, converting the single
82 	 * readlock_t entry into an array of readlock_t entries if necessary.
83 	 */
84 	if ((nlocks = self->ul_rdlockcnt) == 0) {
85 		/*
86 		 * Initial allocation of the readlock_t array.
87 		 * Convert the single entry into an array.
88 		 */
89 		self->ul_rdlockcnt = nlocks = NLOCKS;
90 		readlockp = lmalloc(nlocks * sizeof (readlock_t));
91 		/*
92 		 * The single readlock_t becomes the first entry in the array.
93 		 */
94 		*readlockp = self->ul_readlock.single;
95 		self->ul_readlock.single.rd_count = 0;
96 		self->ul_readlock.array = readlockp;
97 		/*
98 		 * Return the next available entry in the array.
99 		 */
100 		(++readlockp)->rd_rwlock = rwlp;
101 		return (readlockp);
102 	}
103 	/*
104 	 * Reallocate the array, double the size each time.
105 	 */
106 	readlockp = lmalloc(nlocks * 2 * sizeof (readlock_t));
107 	(void) memcpy(readlockp, self->ul_readlock.array,
108 	    nlocks * sizeof (readlock_t));
109 	lfree(self->ul_readlock.array, nlocks * sizeof (readlock_t));
110 	self->ul_readlock.array = readlockp;
111 	self->ul_rdlockcnt *= 2;
112 	/*
113 	 * Return the next available entry in the newly allocated array.
114 	 */
115 	(readlockp += nlocks)->rd_rwlock = rwlp;
116 	return (readlockp);
117 }
118 
119 /*
120  * Free the array of rwlocks held for reading.
121  */
122 void
123 rwl_free(ulwp_t *ulwp)
124 {
125 	uint_t nlocks;
126 
127 	if ((nlocks = ulwp->ul_rdlockcnt) != 0)
128 		lfree(ulwp->ul_readlock.array, nlocks * sizeof (readlock_t));
129 	ulwp->ul_rdlockcnt = 0;
130 	ulwp->ul_readlock.single.rd_rwlock = NULL;
131 	ulwp->ul_readlock.single.rd_count = 0;
132 }
133 
134 /*
135  * Check if a reader version of the lock is held by the current thread.
136  */
137 #pragma weak _rw_read_held = rw_read_held
138 int
139 rw_read_held(rwlock_t *rwlp)
140 {
141 	volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
142 	uint32_t readers;
143 	ulwp_t *self = curthread;
144 	readlock_t *readlockp;
145 	uint_t nlocks;
146 	int rval = 0;
147 
148 	no_preempt(self);
149 
150 	readers = *rwstate;
151 	ASSERT_CONSISTENT_STATE(readers);
152 	if (!(readers & URW_WRITE_LOCKED) &&
153 	    (readers & URW_READERS_MASK) != 0) {
154 		/*
155 		 * The lock is held for reading by some thread.
156 		 * Search our array of rwlocks held for reading for a match.
157 		 */
158 		if ((nlocks = self->ul_rdlockcnt) != 0)
159 			readlockp = self->ul_readlock.array;
160 		else {
161 			nlocks = 1;
162 			readlockp = &self->ul_readlock.single;
163 		}
164 		for (; nlocks; nlocks--, readlockp++) {
165 			if (readlockp->rd_rwlock == rwlp) {
166 				if (readlockp->rd_count)
167 					rval = 1;
168 				break;
169 			}
170 		}
171 	}
172 
173 	preempt(self);
174 	return (rval);
175 }
176 
177 /*
178  * Check if a writer version of the lock is held by the current thread.
179  */
180 #pragma weak _rw_write_held = rw_write_held
181 int
182 rw_write_held(rwlock_t *rwlp)
183 {
184 	volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
185 	uint32_t readers;
186 	ulwp_t *self = curthread;
187 	int rval;
188 
189 	no_preempt(self);
190 
191 	readers = *rwstate;
192 	ASSERT_CONSISTENT_STATE(readers);
193 	rval = ((readers & URW_WRITE_LOCKED) &&
194 	    rwlp->rwlock_owner == (uintptr_t)self &&
195 	    (rwlp->rwlock_type == USYNC_THREAD ||
196 	    rwlp->rwlock_ownerpid == self->ul_uberdata->pid));
197 
198 	preempt(self);
199 	return (rval);
200 }
201 
202 #pragma weak _rwlock_init = rwlock_init
203 /* ARGSUSED2 */
204 int
205 rwlock_init(rwlock_t *rwlp, int type, void *arg)
206 {
207 	if (type != USYNC_THREAD && type != USYNC_PROCESS)
208 		return (EINVAL);
209 	/*
210 	 * Once reinitialized, we can no longer be holding a read or write lock.
211 	 * We can do nothing about other threads that are holding read locks.
212 	 */
213 	sigoff(curthread);
214 	rwl_entry(rwlp)->rd_count = 0;
215 	sigon(curthread);
216 	(void) memset(rwlp, 0, sizeof (*rwlp));
217 	rwlp->rwlock_type = (uint16_t)type;
218 	rwlp->rwlock_magic = RWL_MAGIC;
219 	rwlp->mutex.mutex_type = (uint8_t)type;
220 	rwlp->mutex.mutex_flag = LOCK_INITED;
221 	rwlp->mutex.mutex_magic = MUTEX_MAGIC;
222 	return (0);
223 }
224 
225 #pragma weak pthread_rwlock_destroy = rwlock_destroy
226 #pragma weak _rwlock_destroy = rwlock_destroy
227 int
228 rwlock_destroy(rwlock_t *rwlp)
229 {
230 	/*
231 	 * Once destroyed, we can no longer be holding a read or write lock.
232 	 * We can do nothing about other threads that are holding read locks.
233 	 */
234 	sigoff(curthread);
235 	rwl_entry(rwlp)->rd_count = 0;
236 	sigon(curthread);
237 	rwlp->rwlock_magic = 0;
238 	tdb_sync_obj_deregister(rwlp);
239 	return (0);
240 }
241 
242 /*
243  * Attempt to acquire a readers lock.  Return true on success.
244  */
245 static int
246 read_lock_try(rwlock_t *rwlp, int ignore_waiters_flag)
247 {
248 	volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
249 	uint32_t mask = ignore_waiters_flag?
250 	    URW_WRITE_LOCKED : (URW_HAS_WAITERS | URW_WRITE_LOCKED);
251 	uint32_t readers;
252 	ulwp_t *self = curthread;
253 
254 	no_preempt(self);
255 	while (((readers = *rwstate) & mask) == 0) {
256 		if (atomic_cas_32(rwstate, readers, readers + 1) == readers) {
257 			preempt(self);
258 			return (1);
259 		}
260 	}
261 	preempt(self);
262 	return (0);
263 }
264 
265 /*
266  * Attempt to release a reader lock.  Return true on success.
267  */
268 static int
269 read_unlock_try(rwlock_t *rwlp)
270 {
271 	volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
272 	uint32_t readers;
273 	ulwp_t *self = curthread;
274 
275 	no_preempt(self);
276 	while (((readers = *rwstate) & URW_HAS_WAITERS) == 0) {
277 		if (atomic_cas_32(rwstate, readers, readers - 1) == readers) {
278 			preempt(self);
279 			return (1);
280 		}
281 	}
282 	preempt(self);
283 	return (0);
284 }
285 
286 /*
287  * Attempt to acquire a writer lock.  Return true on success.
288  */
289 static int
290 write_lock_try(rwlock_t *rwlp, int ignore_waiters_flag)
291 {
292 	volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
293 	uint32_t mask = ignore_waiters_flag?
294 	    (URW_WRITE_LOCKED | URW_READERS_MASK) :
295 	    (URW_HAS_WAITERS | URW_WRITE_LOCKED | URW_READERS_MASK);
296 	ulwp_t *self = curthread;
297 	uint32_t readers;
298 
299 	no_preempt(self);
300 	while (((readers = *rwstate) & mask) == 0) {
301 		if (atomic_cas_32(rwstate, readers, readers | URW_WRITE_LOCKED)
302 		    == readers) {
303 			preempt(self);
304 			return (1);
305 		}
306 	}
307 	preempt(self);
308 	return (0);
309 }
310 
311 /*
312  * Attempt to release a writer lock.  Return true on success.
313  */
314 static int
315 write_unlock_try(rwlock_t *rwlp)
316 {
317 	volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
318 	uint32_t readers;
319 	ulwp_t *self = curthread;
320 
321 	no_preempt(self);
322 	while (((readers = *rwstate) & URW_HAS_WAITERS) == 0) {
323 		if (atomic_cas_32(rwstate, readers, 0) == readers) {
324 			preempt(self);
325 			return (1);
326 		}
327 	}
328 	preempt(self);
329 	return (0);
330 }
331 
332 /*
333  * Wake up thread(s) sleeping on the rwlock queue and then
334  * drop the queue lock.  Return non-zero if we wake up someone.
335  * This is called when a thread releases a lock that appears to have waiters.
336  */
337 static int
338 rw_queue_release(queue_head_t *qp, rwlock_t *rwlp)
339 {
340 	volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
341 	uint32_t readers;
342 	uint32_t writers;
343 	ulwp_t **ulwpp;
344 	ulwp_t *ulwp;
345 	ulwp_t *prev;
346 	int nlwpid = 0;
347 	int more;
348 	int maxlwps = MAXLWPS;
349 	lwpid_t buffer[MAXLWPS];
350 	lwpid_t *lwpid = buffer;
351 
352 	readers = *rwstate;
353 	ASSERT_CONSISTENT_STATE(readers);
354 	if (!(readers & URW_HAS_WAITERS)) {
355 		queue_unlock(qp);
356 		return (0);
357 	}
358 	readers &= URW_READERS_MASK;
359 	writers = 0;
360 
361 	/*
362 	 * Examine the queue of waiters in priority order and prepare
363 	 * to wake up as many readers as we encounter before encountering
364 	 * a writer.  If the highest priority thread on the queue is a
365 	 * writer, stop there and wake it up.
366 	 *
367 	 * We keep track of lwpids that are to be unparked in lwpid[].
368 	 * __lwp_unpark_all() is called to unpark all of them after
369 	 * they have been removed from the sleep queue and the sleep
370 	 * queue lock has been dropped.  If we run out of space in our
371 	 * on-stack buffer, we need to allocate more but we can't call
372 	 * lmalloc() because we are holding a queue lock when the overflow
373 	 * occurs and lmalloc() acquires a lock.  We can't use alloca()
374 	 * either because the application may have allocated a small
375 	 * stack and we don't want to overrun the stack.  So we call
376 	 * alloc_lwpids() to allocate a bigger buffer using the mmap()
377 	 * system call directly since that path acquires no locks.
378 	 */
379 	while ((ulwpp = queue_slot(qp, &prev, &more)) != NULL) {
380 		ulwp = *ulwpp;
381 		ASSERT(ulwp->ul_wchan == rwlp);
382 		if (ulwp->ul_writer) {
383 			if (writers != 0 || readers != 0)
384 				break;
385 			/* one writer to wake */
386 			writers++;
387 		} else {
388 			if (writers != 0)
389 				break;
390 			/* at least one reader to wake */
391 			readers++;
392 			if (nlwpid == maxlwps)
393 				lwpid = alloc_lwpids(lwpid, &nlwpid, &maxlwps);
394 		}
395 		queue_unlink(qp, ulwpp, prev);
396 		ulwp->ul_sleepq = NULL;
397 		ulwp->ul_wchan = NULL;
398 		lwpid[nlwpid++] = ulwp->ul_lwpid;
399 	}
400 	if (ulwpp == NULL)
401 		atomic_and_32(rwstate, ~URW_HAS_WAITERS);
402 	if (nlwpid == 0) {
403 		queue_unlock(qp);
404 	} else {
405 		ulwp_t *self = curthread;
406 		no_preempt(self);
407 		queue_unlock(qp);
408 		if (nlwpid == 1)
409 			(void) __lwp_unpark(lwpid[0]);
410 		else
411 			(void) __lwp_unpark_all(lwpid, nlwpid);
412 		preempt(self);
413 	}
414 	if (lwpid != buffer)
415 		(void) munmap((caddr_t)lwpid, maxlwps * sizeof (lwpid_t));
416 	return (nlwpid != 0);
417 }
418 
419 /*
420  * Common code for rdlock, timedrdlock, wrlock, timedwrlock, tryrdlock,
421  * and trywrlock for process-shared (USYNC_PROCESS) rwlocks.
422  *
423  * Note: if the lock appears to be contended we call __lwp_rwlock_rdlock()
424  * or __lwp_rwlock_wrlock() holding the mutex. These return with the mutex
425  * released, and if they need to sleep will release the mutex first. In the
426  * event of a spurious wakeup, these will return EAGAIN (because it is much
427  * easier for us to re-acquire the mutex here).
428  */
429 int
430 shared_rwlock_lock(rwlock_t *rwlp, timespec_t *tsp, int rd_wr)
431 {
432 	volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
433 	mutex_t *mp = &rwlp->mutex;
434 	uint32_t readers;
435 	int try_flag;
436 	int error;
437 
438 	try_flag = (rd_wr & TRY_FLAG);
439 	rd_wr &= ~TRY_FLAG;
440 	ASSERT(rd_wr == READ_LOCK || rd_wr == WRITE_LOCK);
441 
442 	if (!try_flag) {
443 		DTRACE_PROBE2(plockstat, rw__block, rwlp, rd_wr);
444 	}
445 
446 	do {
447 		if (try_flag && (*rwstate & URW_WRITE_LOCKED)) {
448 			error = EBUSY;
449 			break;
450 		}
451 		if ((error = mutex_lock(mp)) != 0)
452 			break;
453 		if (rd_wr == READ_LOCK) {
454 			if (read_lock_try(rwlp, 0)) {
455 				(void) mutex_unlock(mp);
456 				break;
457 			}
458 		} else {
459 			if (write_lock_try(rwlp, 0)) {
460 				(void) mutex_unlock(mp);
461 				break;
462 			}
463 		}
464 		atomic_or_32(rwstate, URW_HAS_WAITERS);
465 		readers = *rwstate;
466 		ASSERT_CONSISTENT_STATE(readers);
467 		/*
468 		 * The calls to __lwp_rwlock_*() below will release the mutex,
469 		 * so we need a dtrace probe here.
470 		 */
471 		mp->mutex_owner = 0;
472 		DTRACE_PROBE2(plockstat, mutex__release, mp, 0);
473 		/*
474 		 * The waiters bit may be inaccurate.
475 		 * Only the kernel knows for sure.
476 		 */
477 		if (rd_wr == READ_LOCK) {
478 			if (try_flag)
479 				error = __lwp_rwlock_tryrdlock(rwlp);
480 			else
481 				error = __lwp_rwlock_rdlock(rwlp, tsp);
482 		} else {
483 			if (try_flag)
484 				error = __lwp_rwlock_trywrlock(rwlp);
485 			else
486 				error = __lwp_rwlock_wrlock(rwlp, tsp);
487 		}
488 	} while (error == EAGAIN || error == EINTR);
489 
490 	if (!try_flag) {
491 		DTRACE_PROBE3(plockstat, rw__blocked, rwlp, rd_wr, error == 0);
492 	}
493 
494 	return (error);
495 }
496 
497 /*
498  * Common code for rdlock, timedrdlock, wrlock, timedwrlock, tryrdlock,
499  * and trywrlock for process-private (USYNC_THREAD) rwlocks.
500  */
501 int
502 rwlock_lock(rwlock_t *rwlp, timespec_t *tsp, int rd_wr)
503 {
504 	volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
505 	uint32_t readers;
506 	ulwp_t *self = curthread;
507 	queue_head_t *qp;
508 	ulwp_t *ulwp;
509 	int try_flag;
510 	int ignore_waiters_flag;
511 	int error = 0;
512 
513 	try_flag = (rd_wr & TRY_FLAG);
514 	rd_wr &= ~TRY_FLAG;
515 	ASSERT(rd_wr == READ_LOCK || rd_wr == WRITE_LOCK);
516 
517 	if (!try_flag) {
518 		DTRACE_PROBE2(plockstat, rw__block, rwlp, rd_wr);
519 	}
520 
521 	qp = queue_lock(rwlp, MX);
522 	/* initial attempt to acquire the lock fails if there are waiters */
523 	ignore_waiters_flag = 0;
524 	while (error == 0) {
525 		if (rd_wr == READ_LOCK) {
526 			if (read_lock_try(rwlp, ignore_waiters_flag))
527 				break;
528 		} else {
529 			if (write_lock_try(rwlp, ignore_waiters_flag))
530 				break;
531 		}
532 		/* subsequent attempts do not fail due to waiters */
533 		ignore_waiters_flag = 1;
534 		atomic_or_32(rwstate, URW_HAS_WAITERS);
535 		readers = *rwstate;
536 		ASSERT_CONSISTENT_STATE(readers);
537 		if ((readers & URW_WRITE_LOCKED) ||
538 		    (rd_wr == WRITE_LOCK &&
539 		    (readers & URW_READERS_MASK) != 0))
540 			/* EMPTY */;	/* somebody holds the lock */
541 		else if ((ulwp = queue_waiter(qp)) == NULL) {
542 			atomic_and_32(rwstate, ~URW_HAS_WAITERS);
543 			continue;	/* no queued waiters, try again */
544 		} else {
545 			/*
546 			 * Do a priority check on the queued waiter (the
547 			 * highest priority thread on the queue) to see
548 			 * if we should defer to him or just grab the lock.
549 			 */
550 			int our_pri = real_priority(self);
551 			int his_pri = real_priority(ulwp);
552 
553 			if (rd_wr == WRITE_LOCK) {
554 				/*
555 				 * We defer to a queued thread that has
556 				 * a higher priority than ours.
557 				 */
558 				if (his_pri <= our_pri)
559 					continue;	/* try again */
560 			} else {
561 				/*
562 				 * We defer to a queued thread that has
563 				 * a higher priority than ours or that
564 				 * is a writer whose priority equals ours.
565 				 */
566 				if (his_pri < our_pri ||
567 				    (his_pri == our_pri && !ulwp->ul_writer))
568 					continue;	/* try again */
569 			}
570 		}
571 		/*
572 		 * We are about to block.
573 		 * If we're doing a trylock, return EBUSY instead.
574 		 */
575 		if (try_flag) {
576 			error = EBUSY;
577 			break;
578 		}
579 		/*
580 		 * Enqueue writers ahead of readers.
581 		 */
582 		self->ul_writer = rd_wr;	/* *must* be 0 or 1 */
583 		enqueue(qp, self, 0);
584 		set_parking_flag(self, 1);
585 		queue_unlock(qp);
586 		if ((error = __lwp_park(tsp, 0)) == EINTR)
587 			error = ignore_waiters_flag = 0;
588 		set_parking_flag(self, 0);
589 		qp = queue_lock(rwlp, MX);
590 		if (self->ul_sleepq && dequeue_self(qp) == 0)
591 			atomic_and_32(rwstate, ~URW_HAS_WAITERS);
592 		self->ul_writer = 0;
593 	}
594 
595 	queue_unlock(qp);
596 
597 	if (!try_flag) {
598 		DTRACE_PROBE3(plockstat, rw__blocked, rwlp, rd_wr, error == 0);
599 	}
600 
601 	return (error);
602 }
603 
604 int
605 rw_rdlock_impl(rwlock_t *rwlp, timespec_t *tsp)
606 {
607 	ulwp_t *self = curthread;
608 	uberdata_t *udp = self->ul_uberdata;
609 	readlock_t *readlockp;
610 	tdb_rwlock_stats_t *rwsp = RWLOCK_STATS(rwlp, udp);
611 	int error;
612 
613 	/*
614 	 * If we already hold a readers lock on this rwlock,
615 	 * just increment our reference count and return.
616 	 */
617 	sigoff(self);
618 	readlockp = rwl_entry(rwlp);
619 	if (readlockp->rd_count != 0) {
620 		if (readlockp->rd_count == READ_LOCK_MAX) {
621 			sigon(self);
622 			error = EAGAIN;
623 			goto out;
624 		}
625 		sigon(self);
626 		error = 0;
627 		goto out;
628 	}
629 	sigon(self);
630 
631 	/*
632 	 * If we hold the writer lock, bail out.
633 	 */
634 	if (rw_write_held(rwlp)) {
635 		if (self->ul_error_detection)
636 			rwlock_error(rwlp, "rwlock_rdlock",
637 			    "calling thread owns the writer lock");
638 		error = EDEADLK;
639 		goto out;
640 	}
641 
642 	if (read_lock_try(rwlp, 0))
643 		error = 0;
644 	else if (rwlp->rwlock_type == USYNC_PROCESS)	/* kernel-level */
645 		error = shared_rwlock_lock(rwlp, tsp, READ_LOCK);
646 	else						/* user-level */
647 		error = rwlock_lock(rwlp, tsp, READ_LOCK);
648 
649 out:
650 	if (error == 0) {
651 		sigoff(self);
652 		rwl_entry(rwlp)->rd_count++;
653 		sigon(self);
654 		if (rwsp)
655 			tdb_incr(rwsp->rw_rdlock);
656 		DTRACE_PROBE2(plockstat, rw__acquire, rwlp, READ_LOCK);
657 	} else {
658 		DTRACE_PROBE3(plockstat, rw__error, rwlp, READ_LOCK, error);
659 	}
660 
661 	return (error);
662 }
663 
664 #pragma weak pthread_rwlock_rdlock = rw_rdlock
665 #pragma weak _rw_rdlock = rw_rdlock
666 int
667 rw_rdlock(rwlock_t *rwlp)
668 {
669 	ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
670 	return (rw_rdlock_impl(rwlp, NULL));
671 }
672 
673 void
674 lrw_rdlock(rwlock_t *rwlp)
675 {
676 	enter_critical(curthread);
677 	(void) rw_rdlock_impl(rwlp, NULL);
678 }
679 
680 int
681 pthread_rwlock_reltimedrdlock_np(pthread_rwlock_t *_RESTRICT_KYWD rwlp,
682     const struct timespec *_RESTRICT_KYWD reltime)
683 {
684 	timespec_t tslocal = *reltime;
685 	int error;
686 
687 	ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
688 	error = rw_rdlock_impl((rwlock_t *)rwlp, &tslocal);
689 	if (error == ETIME)
690 		error = ETIMEDOUT;
691 	return (error);
692 }
693 
694 int
695 pthread_rwlock_timedrdlock(pthread_rwlock_t *_RESTRICT_KYWD rwlp,
696     const struct timespec *_RESTRICT_KYWD abstime)
697 {
698 	timespec_t tslocal;
699 	int error;
700 
701 	ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
702 	abstime_to_reltime(CLOCK_REALTIME, abstime, &tslocal);
703 	error = rw_rdlock_impl((rwlock_t *)rwlp, &tslocal);
704 	if (error == ETIME)
705 		error = ETIMEDOUT;
706 	return (error);
707 }
708 
709 int
710 rw_wrlock_impl(rwlock_t *rwlp, timespec_t *tsp)
711 {
712 	ulwp_t *self = curthread;
713 	uberdata_t *udp = self->ul_uberdata;
714 	tdb_rwlock_stats_t *rwsp = RWLOCK_STATS(rwlp, udp);
715 	int error;
716 
717 	/*
718 	 * If we hold a readers lock on this rwlock, bail out.
719 	 */
720 	if (rw_read_held(rwlp)) {
721 		if (self->ul_error_detection)
722 			rwlock_error(rwlp, "rwlock_wrlock",
723 			    "calling thread owns the readers lock");
724 		error = EDEADLK;
725 		goto out;
726 	}
727 
728 	/*
729 	 * If we hold the writer lock, bail out.
730 	 */
731 	if (rw_write_held(rwlp)) {
732 		if (self->ul_error_detection)
733 			rwlock_error(rwlp, "rwlock_wrlock",
734 			    "calling thread owns the writer lock");
735 		error = EDEADLK;
736 		goto out;
737 	}
738 
739 	if (write_lock_try(rwlp, 0))
740 		error = 0;
741 	else if (rwlp->rwlock_type == USYNC_PROCESS)	/* kernel-level */
742 		error = shared_rwlock_lock(rwlp, tsp, WRITE_LOCK);
743 	else						/* user-level */
744 		error = rwlock_lock(rwlp, tsp, WRITE_LOCK);
745 
746 out:
747 	if (error == 0) {
748 		rwlp->rwlock_owner = (uintptr_t)self;
749 		if (rwlp->rwlock_type == USYNC_PROCESS)
750 			rwlp->rwlock_ownerpid = udp->pid;
751 		if (rwsp) {
752 			tdb_incr(rwsp->rw_wrlock);
753 			rwsp->rw_wrlock_begin_hold = gethrtime();
754 		}
755 		DTRACE_PROBE2(plockstat, rw__acquire, rwlp, WRITE_LOCK);
756 	} else {
757 		DTRACE_PROBE3(plockstat, rw__error, rwlp, WRITE_LOCK, error);
758 	}
759 	return (error);
760 }
761 
762 #pragma weak pthread_rwlock_wrlock = rw_wrlock
763 #pragma weak _rw_wrlock = rw_wrlock
764 int
765 rw_wrlock(rwlock_t *rwlp)
766 {
767 	ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
768 	return (rw_wrlock_impl(rwlp, NULL));
769 }
770 
771 void
772 lrw_wrlock(rwlock_t *rwlp)
773 {
774 	enter_critical(curthread);
775 	(void) rw_wrlock_impl(rwlp, NULL);
776 }
777 
778 int
779 pthread_rwlock_reltimedwrlock_np(pthread_rwlock_t *_RESTRICT_KYWD rwlp,
780     const struct timespec *_RESTRICT_KYWD reltime)
781 {
782 	timespec_t tslocal = *reltime;
783 	int error;
784 
785 	ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
786 	error = rw_wrlock_impl((rwlock_t *)rwlp, &tslocal);
787 	if (error == ETIME)
788 		error = ETIMEDOUT;
789 	return (error);
790 }
791 
792 int
793 pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlp, const timespec_t *abstime)
794 {
795 	timespec_t tslocal;
796 	int error;
797 
798 	ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
799 	abstime_to_reltime(CLOCK_REALTIME, abstime, &tslocal);
800 	error = rw_wrlock_impl((rwlock_t *)rwlp, &tslocal);
801 	if (error == ETIME)
802 		error = ETIMEDOUT;
803 	return (error);
804 }
805 
806 #pragma weak pthread_rwlock_tryrdlock = rw_tryrdlock
807 int
808 rw_tryrdlock(rwlock_t *rwlp)
809 {
810 	ulwp_t *self = curthread;
811 	uberdata_t *udp = self->ul_uberdata;
812 	tdb_rwlock_stats_t *rwsp = RWLOCK_STATS(rwlp, udp);
813 	readlock_t *readlockp;
814 	int error;
815 
816 	ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
817 
818 	if (rwsp)
819 		tdb_incr(rwsp->rw_rdlock_try);
820 
821 	/*
822 	 * If we already hold a readers lock on this rwlock,
823 	 * just increment our reference count and return.
824 	 */
825 	sigoff(self);
826 	readlockp = rwl_entry(rwlp);
827 	if (readlockp->rd_count != 0) {
828 		if (readlockp->rd_count == READ_LOCK_MAX) {
829 			sigon(self);
830 			error = EAGAIN;
831 			goto out;
832 		}
833 		sigon(self);
834 		error = 0;
835 		goto out;
836 	}
837 	sigon(self);
838 
839 	if (read_lock_try(rwlp, 0))
840 		error = 0;
841 	else if (rwlp->rwlock_type == USYNC_PROCESS)	/* kernel-level */
842 		error = shared_rwlock_lock(rwlp, NULL, READ_LOCK_TRY);
843 	else						/* user-level */
844 		error = rwlock_lock(rwlp, NULL, READ_LOCK_TRY);
845 
846 out:
847 	if (error == 0) {
848 		sigoff(self);
849 		rwl_entry(rwlp)->rd_count++;
850 		sigon(self);
851 		DTRACE_PROBE2(plockstat, rw__acquire, rwlp, READ_LOCK);
852 	} else {
853 		if (rwsp)
854 			tdb_incr(rwsp->rw_rdlock_try_fail);
855 		if (error != EBUSY) {
856 			DTRACE_PROBE3(plockstat, rw__error, rwlp, READ_LOCK,
857 			    error);
858 		}
859 	}
860 
861 	return (error);
862 }
863 
864 #pragma weak pthread_rwlock_trywrlock = rw_trywrlock
865 int
866 rw_trywrlock(rwlock_t *rwlp)
867 {
868 	ulwp_t *self = curthread;
869 	uberdata_t *udp = self->ul_uberdata;
870 	tdb_rwlock_stats_t *rwsp = RWLOCK_STATS(rwlp, udp);
871 	int error;
872 
873 	ASSERT(!self->ul_critical || self->ul_bindflags);
874 
875 	if (rwsp)
876 		tdb_incr(rwsp->rw_wrlock_try);
877 
878 	if (write_lock_try(rwlp, 0))
879 		error = 0;
880 	else if (rwlp->rwlock_type == USYNC_PROCESS)	/* kernel-level */
881 		error = shared_rwlock_lock(rwlp, NULL, WRITE_LOCK_TRY);
882 	else						/* user-level */
883 		error = rwlock_lock(rwlp, NULL, WRITE_LOCK_TRY);
884 
885 	if (error == 0) {
886 		rwlp->rwlock_owner = (uintptr_t)self;
887 		if (rwlp->rwlock_type == USYNC_PROCESS)
888 			rwlp->rwlock_ownerpid = udp->pid;
889 		if (rwsp)
890 			rwsp->rw_wrlock_begin_hold = gethrtime();
891 		DTRACE_PROBE2(plockstat, rw__acquire, rwlp, WRITE_LOCK);
892 	} else {
893 		if (rwsp)
894 			tdb_incr(rwsp->rw_wrlock_try_fail);
895 		if (error != EBUSY) {
896 			DTRACE_PROBE3(plockstat, rw__error, rwlp, WRITE_LOCK,
897 			    error);
898 		}
899 	}
900 	return (error);
901 }
902 
903 #pragma weak pthread_rwlock_unlock = rw_unlock
904 #pragma weak _rw_unlock = rw_unlock
905 int
906 rw_unlock(rwlock_t *rwlp)
907 {
908 	volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
909 	uint32_t readers;
910 	ulwp_t *self = curthread;
911 	uberdata_t *udp = self->ul_uberdata;
912 	tdb_rwlock_stats_t *rwsp;
913 	queue_head_t *qp;
914 	int rd_wr;
915 	int waked = 0;
916 
917 	readers = *rwstate;
918 	ASSERT_CONSISTENT_STATE(readers);
919 	if (readers & URW_WRITE_LOCKED) {
920 		rd_wr = WRITE_LOCK;
921 		readers = 0;
922 	} else {
923 		rd_wr = READ_LOCK;
924 		readers &= URW_READERS_MASK;
925 	}
926 
927 	if (rd_wr == WRITE_LOCK) {
928 		/*
929 		 * Since the writer lock is held, we'd better be
930 		 * holding it, else we cannot legitimately be here.
931 		 */
932 		if (!rw_write_held(rwlp)) {
933 			if (self->ul_error_detection)
934 				rwlock_error(rwlp, "rwlock_unlock",
935 				    "writer lock held, "
936 				    "but not by the calling thread");
937 			return (EPERM);
938 		}
939 		if ((rwsp = RWLOCK_STATS(rwlp, udp)) != NULL) {
940 			if (rwsp->rw_wrlock_begin_hold)
941 				rwsp->rw_wrlock_hold_time +=
942 				    gethrtime() - rwsp->rw_wrlock_begin_hold;
943 			rwsp->rw_wrlock_begin_hold = 0;
944 		}
945 		rwlp->rwlock_owner = 0;
946 		rwlp->rwlock_ownerpid = 0;
947 	} else if (readers > 0) {
948 		/*
949 		 * A readers lock is held; if we don't hold one, bail out.
950 		 */
951 		readlock_t *readlockp;
952 
953 		sigoff(self);
954 		readlockp = rwl_entry(rwlp);
955 		if (readlockp->rd_count == 0) {
956 			sigon(self);
957 			if (self->ul_error_detection)
958 				rwlock_error(rwlp, "rwlock_unlock",
959 				    "readers lock held, "
960 				    "but not by the calling thread");
961 			return (EPERM);
962 		}
963 		/*
964 		 * If we hold more than one readers lock on this rwlock,
965 		 * just decrement our reference count and return.
966 		 */
967 		if (--readlockp->rd_count != 0) {
968 			sigon(self);
969 			goto out;
970 		}
971 		sigon(self);
972 	} else {
973 		/*
974 		 * This is a usage error.
975 		 * No thread should release an unowned lock.
976 		 */
977 		if (self->ul_error_detection)
978 			rwlock_error(rwlp, "rwlock_unlock", "lock not owned");
979 		return (EPERM);
980 	}
981 
982 	if (rd_wr == WRITE_LOCK && write_unlock_try(rwlp)) {
983 		/* EMPTY */;
984 	} else if (rd_wr == READ_LOCK && read_unlock_try(rwlp)) {
985 		/* EMPTY */;
986 	} else if (rwlp->rwlock_type == USYNC_PROCESS) {
987 		(void) mutex_lock(&rwlp->mutex);
988 		(void) __lwp_rwlock_unlock(rwlp);
989 		(void) mutex_unlock(&rwlp->mutex);
990 		waked = 1;
991 	} else {
992 		qp = queue_lock(rwlp, MX);
993 		if (rd_wr == READ_LOCK)
994 			atomic_dec_32(rwstate);
995 		else
996 			atomic_and_32(rwstate, ~URW_WRITE_LOCKED);
997 		waked = rw_queue_release(qp, rwlp);
998 	}
999 
1000 out:
1001 	DTRACE_PROBE2(plockstat, rw__release, rwlp, rd_wr);
1002 
1003 	/*
1004 	 * Yield to the thread we just waked up, just in case we might
1005 	 * be about to grab the rwlock again immediately upon return.
1006 	 * This is pretty weak but it helps on a uniprocessor and also
1007 	 * when cpu affinity has assigned both ourself and the other
1008 	 * thread to the same CPU.  Note that lwp_yield() will yield
1009 	 * the processor only if the writer is at the same or higher
1010 	 * priority than ourself.  This provides more balanced program
1011 	 * behavior; it doesn't guarantee acquisition of the lock by
1012 	 * the pending writer.
1013 	 */
1014 	if (waked)
1015 		yield();
1016 	return (0);
1017 }
1018 
1019 void
1020 lrw_unlock(rwlock_t *rwlp)
1021 {
1022 	(void) rw_unlock(rwlp);
1023 	exit_critical(curthread);
1024 }
1025