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 */
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				// 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
131int
132smbfs_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
174void
175smbfs_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
207int
208smbfs_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 */
218void
219smbfs_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
229void
230smbfs_rw_destroy(smbfs_rwlock_t *l)
231{
232
233	mutex_destroy(&l->lock);
234	cv_destroy(&l->cv);
235}
236