xref: /illumos-gate/usr/src/uts/common/os/rwstlock.c (revision b3d32f0c)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Copyright (c) 2013, Joyent, Inc.  All rights reserved.
29  */
30 
31 #include <sys/rwstlock.h>
32 #include <sys/errno.h>
33 #include <sys/debug.h>
34 #include <sys/lockstat.h>
35 #include <sys/sysmacros.h>
36 #include <sys/condvar_impl.h>
37 
38 /*
39  * Alternate rwlock that is interruptible and can be released by a thread
40  * other than the one that acquired the lock.
41  *
42  * There is no priority inheritance mechanism for these locks.
43  * For RW_READER, writers have priority over readers, so reader starvation
44  * is possible; as with rwlocks, this behavior may be overridden by
45  * specifying RW_READER_STARVEWRITER.
46  */
47 
48 /*
49  * Common code to grab a lock.  There are three cases:
50  *
51  * (1) If RWST_TRYENTER is set, we try the lock without blocking.
52  *     In this case we return 1 on success, 0 on failure.
53  *
54  * (2) If RWST_SIG is set, we block interruptibly until we get the lock.
55  *     In this case we return 0 on success, EINTR if we're interrupted.
56  *
57  * (3) If neither flag is set, we block uninterruptibly until we get the lock.
58  *     In this case we return 0 (we always succeed).
59  */
60 static int
rwst_enter_common(rwstlock_t * l,krw_t rw,int flags)61 rwst_enter_common(rwstlock_t *l, krw_t rw, int flags)
62 {
63 	hrtime_t sleep_time;
64 	int writer;
65 	intptr_t readers;
66 
67 	mutex_enter(&l->rwst_lock);
68 	if (rw == RW_READER || rw == RW_READER_STARVEWRITER) {
69 		while (RWST_WRITE_HELD(l) ||
70 		    (rw != RW_READER_STARVEWRITER && RWST_WRITE_WANTED(l))) {
71 
72 			if (flags & RWST_TRYENTER) {
73 				mutex_exit(&l->rwst_lock);
74 				return (0);
75 			}
76 			if (panicstr)
77 				return (0);
78 
79 			if (RWST_WRITE_HELD(l)) {
80 				writer = 1;
81 				readers = 0;
82 			} else {
83 				writer = 0;
84 				readers = l->rwst_count;
85 			}
86 			sleep_time = -gethrtime();
87 			if (!RWST_READ_WAIT(l, flags)) {
88 				mutex_exit(&l->rwst_lock);
89 				return (EINTR);
90 			}
91 			sleep_time += gethrtime();
92 			LOCKSTAT_RECORD4(LS_RW_ENTER_BLOCK, l, sleep_time, rw,
93 			    writer, readers);
94 		}
95 		RWST_READ_ENTER(l);
96 		LOCKSTAT_RECORD(LS_RW_ENTER_ACQUIRE, l, rw);
97 	} else {
98 		ASSERT(rw == RW_WRITER);
99 		while (RWST_HELD(l)) {
100 			if (flags & RWST_TRYENTER) {
101 				mutex_exit(&l->rwst_lock);
102 				return (0);
103 			}
104 			if (panicstr)
105 				return (0);
106 			if (RWST_WRITE_HELD(l)) {
107 				writer = 1;
108 				readers = 0;
109 			} else {
110 				writer = 0;
111 				readers = l->rwst_count;
112 			}
113 			sleep_time = -gethrtime();
114 			if (!RWST_WRITE_WAIT(l, flags)) {
115 				if (!RWST_WRITE_HELD(l) &&
116 				    !RWST_WRITE_WANTED(l))
117 					RWST_READ_WAKE_ALL(l);
118 				mutex_exit(&l->rwst_lock);
119 				return (EINTR);
120 			}
121 			sleep_time += gethrtime();
122 			LOCKSTAT_RECORD4(LS_RW_ENTER_BLOCK, l, sleep_time, rw,
123 			    writer, readers);
124 		}
125 		RWST_WRITE_ENTER(l);
126 		LOCKSTAT_RECORD(LS_RW_ENTER_ACQUIRE, l, rw);
127 	}
128 	mutex_exit(&l->rwst_lock);
129 	return (flags & RWST_TRYENTER);
130 }
131 
132 void
rwst_exit(rwstlock_t * l)133 rwst_exit(rwstlock_t *l)
134 {
135 	mutex_enter(&l->rwst_lock);
136 	if (RWST_WRITE_HELD(l)) {
137 		LOCKSTAT_RECORD(LS_RW_EXIT_RELEASE, l, RW_WRITER);
138 		RWST_WRITE_EXIT(l);
139 	} else {
140 		ASSERT(RWST_READ_HELD(l));
141 		LOCKSTAT_RECORD(LS_RW_EXIT_RELEASE, l, RW_READER);
142 		RWST_READ_EXIT(l);
143 	}
144 	if (!RWST_WRITE_WANTED(l))
145 		RWST_READ_WAKE_ALL(l);
146 	else if (!RWST_HELD(l))
147 		RWST_WRITE_WAKE_ONE(l);
148 	mutex_exit(&l->rwst_lock);
149 }
150 
151 void
rwst_enter(rwstlock_t * l,krw_t rw)152 rwst_enter(rwstlock_t *l, krw_t rw)
153 {
154 	(void) rwst_enter_common(l, rw, 0);
155 }
156 
157 int
rwst_enter_sig(rwstlock_t * l,krw_t rw)158 rwst_enter_sig(rwstlock_t *l, krw_t rw)
159 {
160 	return (rwst_enter_common(l, rw, RWST_SIG));
161 }
162 
163 int
rwst_tryenter(rwstlock_t * l,krw_t rw)164 rwst_tryenter(rwstlock_t *l, krw_t rw)
165 {
166 	return (rwst_enter_common(l, rw, RWST_TRYENTER));
167 }
168 
169 int
rwst_lock_held(rwstlock_t * l,krw_t rw)170 rwst_lock_held(rwstlock_t *l, krw_t rw)
171 {
172 	if (rw != RW_WRITER)
173 		return (RWST_READ_HELD(l));
174 	ASSERT(rw == RW_WRITER);
175 	return (RWST_WRITE_OWNER(l));
176 }
177 
178 /*ARGSUSED*/
179 void
rwst_init(rwstlock_t * l,char * name,krw_type_t krw_t,void * arg)180 rwst_init(rwstlock_t *l, char *name, krw_type_t krw_t, void *arg)
181 {
182 	l->rwst_count = 0;
183 	mutex_init(&l->rwst_lock, NULL, MUTEX_DEFAULT, NULL);
184 	cv_init(&l->rwst_rcv, NULL, CV_DEFAULT, NULL);
185 	cv_init(&l->rwst_wcv, NULL, CV_DEFAULT, NULL);
186 }
187 
188 void
rwst_destroy(rwstlock_t * l)189 rwst_destroy(rwstlock_t *l)
190 {
191 	ASSERT(l->rwst_count == 0);
192 	mutex_destroy(&l->rwst_lock);
193 	cv_destroy(&l->rwst_rcv);
194 	cv_destroy(&l->rwst_wcv);
195 }
196 
197 struct _kthread *
rwst_owner(rwstlock_t * l)198 rwst_owner(rwstlock_t *l)
199 {
200 	return (RWST_OWNER(l));
201 }
202