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