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