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