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