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