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 */
58int
59smbfs_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
148int
149smbfs_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
191void
192smbfs_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
224int
225smbfs_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 */
235void
236smbfs_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
246void
247smbfs_rw_destroy(smbfs_rwlock_t *l)
248{
249
250	mutex_destroy(&l->lock);
251	cv_destroy(&l->cv);
252}
253