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