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