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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 *
25 * Copyright (c) 1983,1984,1985,1986,1987,1988,1989 AT&T.
26 * All rights reserved.
27 */
28
29 #pragma ident "%Z%%M% %I% %E% SMI"
30
31 /*
32 * A homegrown reader/writer lock implementation. It addresses
33 * two requirements not addressed by the system primitives. They
34 * are that the `enter" operation is optionally interruptible and
35 * that that they can be re`enter'ed by writers without deadlock.
36 *
37 * All of this was borrowed from NFS.
38 * See: uts/common/fs/nfs/nfs_subr.c
39 *
40 * XXX: Could we make this serve our needs instead?
41 * See: uts/common/os/rwstlock.c
42 * (and then use it for NFS too)
43 */
44
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/time.h>
48 #include <sys/vnode.h>
49
50 #include <smbfs/smbfs.h>
51 #include <smbfs/smbfs_node.h>
52 #include <smbfs/smbfs_subr.h>
53
54
55 /*
56 * Only can return non-zero if intr != 0.
57 */
58 int
smbfs_rw_enter_sig(smbfs_rwlock_t * l,krw_t rw,int intr)59 smbfs_rw_enter_sig(smbfs_rwlock_t *l, krw_t rw, int intr)
60 {
61
62 mutex_enter(&l->lock);
63
64 /*
65 * If this is a nested enter, then allow it. There
66 * must be as many exits as enters through.
67 */
68 if (l->owner == curthread) {
69 /* lock is held for writing by current thread */
70 ASSERT(rw == RW_READER || rw == RW_WRITER);
71 l->count--;
72 } else if (rw == RW_READER) {
73 /*
74 * While there is a writer active or writers waiting,
75 * then wait for them to finish up and move on. Then,
76 * increment the count to indicate that a reader is
77 * active.
78 */
79 while (l->count < 0 || l->waiters > 0) {
80 if (intr) {
81 klwp_t *lwp = ttolwp(curthread);
82
83 if (lwp != NULL)
84 lwp->lwp_nostop++;
85 if (!cv_wait_sig(&l->cv, &l->lock)) {
86 if (lwp != NULL)
87 lwp->lwp_nostop--;
88 mutex_exit(&l->lock);
89 return (EINTR);
90 }
91 if (lwp != NULL)
92 lwp->lwp_nostop--;
93 } else
94 cv_wait(&l->cv, &l->lock);
95 }
96 ASSERT(l->count < INT_MAX);
97 #ifdef SMBDEBUG
98 if ((l->count % 10000) == 9999)
99 cmn_err(CE_WARN, "smbfs_rw_enter_sig: count %d on"
100 "rwlock @ %p\n", l->count, (void *)&l);
101 #endif
102 l->count++;
103 } else {
104 ASSERT(rw == RW_WRITER);
105 /*
106 * While there are readers active or a writer
107 * active, then wait for all of the readers
108 * to finish or for the writer to finish.
109 * Then, set the owner field to curthread and
110 * decrement count to indicate that a writer
111 * is active.
112 */
113 while (l->count > 0 || l->owner != NULL) {
114 l->waiters++;
115 if (intr) {
116 klwp_t *lwp = ttolwp(curthread);
117
118 if (lwp != NULL)
119 lwp->lwp_nostop++;
120 if (!cv_wait_sig(&l->cv, &l->lock)) {
121 if (lwp != NULL)
122 lwp->lwp_nostop--;
123 l->waiters--;
124 cv_broadcast(&l->cv);
125 mutex_exit(&l->lock);
126 return (EINTR);
127 }
128 if (lwp != NULL)
129 lwp->lwp_nostop--;
130 } else
131 cv_wait(&l->cv, &l->lock);
132 l->waiters--;
133 }
134 l->owner = curthread;
135 l->count--;
136 }
137
138 mutex_exit(&l->lock);
139
140 return (0);
141 }
142
143 /*
144 * If the lock is available, obtain it and return non-zero. If there is
145 * already a conflicting lock, return 0 immediately.
146 */
147
148 int
smbfs_rw_tryenter(smbfs_rwlock_t * l,krw_t rw)149 smbfs_rw_tryenter(smbfs_rwlock_t *l, krw_t rw)
150 {
151 mutex_enter(&l->lock);
152
153 /*
154 * If this is a nested enter, then allow it. There
155 * must be as many exits as enters through.
156 */
157 if (l->owner == curthread) {
158 /* lock is held for writing by current thread */
159 ASSERT(rw == RW_READER || rw == RW_WRITER);
160 l->count--;
161 } else if (rw == RW_READER) {
162 /*
163 * If there is a writer active or writers waiting, deny the
164 * lock. Otherwise, bump the count of readers.
165 */
166 if (l->count < 0 || l->waiters > 0) {
167 mutex_exit(&l->lock);
168 return (0);
169 }
170 l->count++;
171 } else {
172 ASSERT(rw == RW_WRITER);
173 /*
174 * If there are readers active or a writer active, deny the
175 * lock. Otherwise, set the owner field to curthread and
176 * decrement count to indicate that a writer is active.
177 */
178 if (l->count > 0 || l->owner != NULL) {
179 mutex_exit(&l->lock);
180 return (0);
181 }
182 l->owner = curthread;
183 l->count--;
184 }
185
186 mutex_exit(&l->lock);
187
188 return (1);
189 }
190
191 void
smbfs_rw_exit(smbfs_rwlock_t * l)192 smbfs_rw_exit(smbfs_rwlock_t *l)
193 {
194
195 mutex_enter(&l->lock);
196 /*
197 * If this is releasing a writer lock, then increment count to
198 * indicate that there is one less writer active. If this was
199 * the last of possibly nested writer locks, then clear the owner
200 * field as well to indicate that there is no writer active
201 * and wakeup any possible waiting writers or readers.
202 *
203 * If releasing a reader lock, then just decrement count to
204 * indicate that there is one less reader active. If this was
205 * the last active reader and there are writer(s) waiting,
206 * then wake up the first.
207 */
208 if (l->owner != NULL) {
209 ASSERT(l->owner == curthread);
210 l->count++;
211 if (l->count == 0) {
212 l->owner = NULL;
213 cv_broadcast(&l->cv);
214 }
215 } else {
216 ASSERT(l->count > 0);
217 l->count--;
218 if (l->count == 0 && l->waiters > 0)
219 cv_broadcast(&l->cv);
220 }
221 mutex_exit(&l->lock);
222 }
223
224 int
smbfs_rw_lock_held(smbfs_rwlock_t * l,krw_t rw)225 smbfs_rw_lock_held(smbfs_rwlock_t *l, krw_t rw)
226 {
227
228 if (rw == RW_READER)
229 return (l->count > 0);
230 ASSERT(rw == RW_WRITER);
231 return (l->count < 0);
232 }
233
234 /* ARGSUSED */
235 void
smbfs_rw_init(smbfs_rwlock_t * l,char * name,krw_type_t type,void * arg)236 smbfs_rw_init(smbfs_rwlock_t *l, char *name, krw_type_t type, void *arg)
237 {
238
239 l->count = 0;
240 l->waiters = 0;
241 l->owner = NULL;
242 mutex_init(&l->lock, NULL, MUTEX_DEFAULT, NULL);
243 cv_init(&l->cv, NULL, CV_DEFAULT, NULL);
244 }
245
246 void
smbfs_rw_destroy(smbfs_rwlock_t * l)247 smbfs_rw_destroy(smbfs_rwlock_t *l)
248 {
249
250 mutex_destroy(&l->lock);
251 cv_destroy(&l->cv);
252 }
253