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 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 #include <sys/rwstlock.h>
30 #include <sys/errno.h>
31 #include <sys/debug.h>
32 #include <sys/lockstat.h>
33 #include <sys/sysmacros.h>
34 #include <sys/condvar_impl.h>
35
36 /*
37 * Alternate rwlock that is interruptible and can be released by a thread
38 * other than the one that acquired the lock.
39 *
40 * There is no priority inheritance mechanism for these locks.
41 * Writers have priority over readers, so reader starvation is possible.
42 */
43
44 /*
45 * Common code to grab a lock. There are three cases:
46 *
47 * (1) If RWST_TRYENTER is set, we try the lock without blocking.
48 * In this case we return 1 on success, 0 on failure.
49 *
50 * (2) If RWST_SIG is set, we block interruptibly until we get the lock.
51 * In this case we return 0 on success, EINTR if we're interrupted.
52 *
53 * (3) If neither flag is set, we block uninterruptibly until we get the lock.
54 * In this case we return 0 (we always succeed).
55 */
56 static int
rwst_enter_common(rwstlock_t * l,krw_t rw,int flags)57 rwst_enter_common(rwstlock_t *l, krw_t rw, int flags)
58 {
59 hrtime_t sleep_time;
60 int writer;
61 intptr_t readers;
62
63 mutex_enter(&l->rwst_lock);
64 if (rw == RW_READER) {
65 while (RWST_WRITE_HELD(l) || RWST_WRITE_WANTED(l)) {
66
67 if (flags & RWST_TRYENTER) {
68 mutex_exit(&l->rwst_lock);
69 return (0);
70 }
71 if (panicstr)
72 return (0);
73
74 if (RWST_WRITE_HELD(l)) {
75 writer = 1;
76 readers = 0;
77 } else {
78 writer = 0;
79 readers = l->rwst_count;
80 }
81 sleep_time = -gethrtime();
82 if (!RWST_READ_WAIT(l, flags)) {
83 mutex_exit(&l->rwst_lock);
84 return (EINTR);
85 }
86 sleep_time += gethrtime();
87 LOCKSTAT_RECORD4(LS_RW_ENTER_BLOCK, l, sleep_time, rw,
88 writer, readers);
89 }
90 RWST_READ_ENTER(l);
91 LOCKSTAT_RECORD(LS_RW_ENTER_ACQUIRE, l, rw);
92 } else {
93 ASSERT(rw == RW_WRITER);
94 while (RWST_HELD(l)) {
95 if (flags & RWST_TRYENTER) {
96 mutex_exit(&l->rwst_lock);
97 return (0);
98 }
99 if (panicstr)
100 return (0);
101 if (RWST_WRITE_HELD(l)) {
102 writer = 1;
103 readers = 0;
104 } else {
105 writer = 0;
106 readers = l->rwst_count;
107 }
108 sleep_time = -gethrtime();
109 if (!RWST_WRITE_WAIT(l, flags)) {
110 if (!RWST_WRITE_HELD(l) &&
111 !RWST_WRITE_WANTED(l))
112 RWST_READ_WAKE_ALL(l);
113 mutex_exit(&l->rwst_lock);
114 return (EINTR);
115 }
116 sleep_time += gethrtime();
117 LOCKSTAT_RECORD4(LS_RW_ENTER_BLOCK, l, sleep_time, rw,
118 writer, readers);
119 }
120 RWST_WRITE_ENTER(l);
121 LOCKSTAT_RECORD(LS_RW_ENTER_ACQUIRE, l, rw);
122 }
123 mutex_exit(&l->rwst_lock);
124 return (flags & RWST_TRYENTER);
125 }
126
127 void
rwst_exit(rwstlock_t * l)128 rwst_exit(rwstlock_t *l)
129 {
130 mutex_enter(&l->rwst_lock);
131 if (RWST_WRITE_HELD(l)) {
132 LOCKSTAT_RECORD(LS_RW_EXIT_RELEASE, l, RW_WRITER);
133 RWST_WRITE_EXIT(l);
134 } else {
135 ASSERT(RWST_READ_HELD(l));
136 LOCKSTAT_RECORD(LS_RW_EXIT_RELEASE, l, RW_READER);
137 RWST_READ_EXIT(l);
138 }
139 if (!RWST_WRITE_WANTED(l))
140 RWST_READ_WAKE_ALL(l);
141 else if (!RWST_HELD(l))
142 RWST_WRITE_WAKE_ONE(l);
143 mutex_exit(&l->rwst_lock);
144 }
145
146 void
rwst_enter(rwstlock_t * l,krw_t rw)147 rwst_enter(rwstlock_t *l, krw_t rw)
148 {
149 (void) rwst_enter_common(l, rw, 0);
150 }
151
152 int
rwst_enter_sig(rwstlock_t * l,krw_t rw)153 rwst_enter_sig(rwstlock_t *l, krw_t rw)
154 {
155 return (rwst_enter_common(l, rw, RWST_SIG));
156 }
157
158 int
rwst_tryenter(rwstlock_t * l,krw_t rw)159 rwst_tryenter(rwstlock_t *l, krw_t rw)
160 {
161 return (rwst_enter_common(l, rw, RWST_TRYENTER));
162 }
163
164 int
rwst_lock_held(rwstlock_t * l,krw_t rw)165 rwst_lock_held(rwstlock_t *l, krw_t rw)
166 {
167 if (rw == RW_READER)
168 return (RWST_READ_HELD(l));
169 ASSERT(rw == RW_WRITER);
170 return (RWST_WRITE_OWNER(l));
171 }
172
173 /*ARGSUSED*/
174 void
rwst_init(rwstlock_t * l,char * name,krw_type_t krw_t,void * arg)175 rwst_init(rwstlock_t *l, char *name, krw_type_t krw_t, void *arg)
176 {
177 l->rwst_count = 0;
178 mutex_init(&l->rwst_lock, NULL, MUTEX_DEFAULT, NULL);
179 cv_init(&l->rwst_rcv, NULL, CV_DEFAULT, NULL);
180 cv_init(&l->rwst_wcv, NULL, CV_DEFAULT, NULL);
181 }
182
183 void
rwst_destroy(rwstlock_t * l)184 rwst_destroy(rwstlock_t *l)
185 {
186 ASSERT(l->rwst_count == 0);
187 mutex_destroy(&l->rwst_lock);
188 cv_destroy(&l->rwst_rcv);
189 cv_destroy(&l->rwst_wcv);
190 }
191
192 struct _kthread *
rwst_owner(rwstlock_t * l)193 rwst_owner(rwstlock_t *l)
194 {
195 return (RWST_OWNER(l));
196 }
197