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
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
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
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
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
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
247 smbfs_rw_destroy(smbfs_rwlock_t *l)
248 {
249 
250 	mutex_destroy(&l->lock);
251 	cv_destroy(&l->cv);
252 }
253