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