kern_sx.c revision 82246
1/*
2 * Copyright (C) 2001 Jason Evans <jasone@freebsd.org>.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice(s), this list of conditions and the following disclaimer as
9 *    the first lines of this file unmodified other than the possible
10 *    addition of one or more copyright notices.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice(s), this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
25 * DAMAGE.
26 *
27 * $FreeBSD: head/sys/kern/kern_sx.c 82246 2001-08-23 22:51:22Z jhb $
28 */
29
30/*
31 * Shared/exclusive locks.  This implementation assures deterministic lock
32 * granting behavior, so that slocks and xlocks are interleaved.
33 *
34 * Priority propagation will not generally raise the priority of lock holders,
35 * so should not be relied upon in combination with sx locks.
36 */
37
38#include <sys/param.h>
39#include <sys/systm.h>
40#include <sys/ktr.h>
41#include <sys/condvar.h>
42#include <sys/lock.h>
43#include <sys/mutex.h>
44#include <sys/sx.h>
45
46struct lock_class lock_class_sx = {
47	"sx",
48	LC_SLEEPLOCK | LC_SLEEPABLE | LC_RECURSABLE | LC_UPGRADABLE
49};
50
51void
52sx_init(struct sx *sx, const char *description)
53{
54	struct lock_object *lock;
55
56	bzero(sx, sizeof(*sx));
57	lock = &sx->sx_object;
58	lock->lo_class = &lock_class_sx;
59	lock->lo_name = description;
60	lock->lo_flags = LO_WITNESS | LO_RECURSABLE | LO_SLEEPABLE |
61	    LO_UPGRADABLE;
62	mtx_init(&sx->sx_lock, "sx backing lock",
63	    MTX_DEF | MTX_NOWITNESS | MTX_QUIET);
64	sx->sx_cnt = 0;
65	cv_init(&sx->sx_shrd_cv, description);
66	sx->sx_shrd_wcnt = 0;
67	cv_init(&sx->sx_excl_cv, description);
68	sx->sx_excl_wcnt = 0;
69	sx->sx_xholder = NULL;
70
71	LOCK_LOG_INIT(lock, 0);
72
73	WITNESS_INIT(lock);
74}
75
76void
77sx_destroy(struct sx *sx)
78{
79
80	LOCK_LOG_DESTROY(&sx->sx_object, 0);
81
82	KASSERT((sx->sx_cnt == 0 && sx->sx_shrd_wcnt == 0 && sx->sx_excl_wcnt ==
83	    0), ("%s (%s): holders or waiters\n", __FUNCTION__,
84	    sx->sx_object.lo_name));
85
86	mtx_destroy(&sx->sx_lock);
87	cv_destroy(&sx->sx_shrd_cv);
88	cv_destroy(&sx->sx_excl_cv);
89
90	WITNESS_DESTROY(&sx->sx_object);
91}
92
93void
94_sx_slock(struct sx *sx, const char *file, int line)
95{
96
97	mtx_lock(&sx->sx_lock);
98	KASSERT(sx->sx_xholder != curproc,
99	    ("%s (%s): slock while xlock is held @ %s:%d\n", __FUNCTION__,
100	    sx->sx_object.lo_name, file, line));
101
102	/*
103	 * Loop in case we lose the race for lock acquisition.
104	 */
105	while (sx->sx_cnt < 0) {
106		sx->sx_shrd_wcnt++;
107		cv_wait(&sx->sx_shrd_cv, &sx->sx_lock);
108		sx->sx_shrd_wcnt--;
109	}
110
111	/* Acquire a shared lock. */
112	sx->sx_cnt++;
113
114	LOCK_LOG_LOCK("SLOCK", &sx->sx_object, 0, 0, file, line);
115	WITNESS_LOCK(&sx->sx_object, 0, file, line);
116
117	mtx_unlock(&sx->sx_lock);
118}
119
120int
121_sx_try_slock(struct sx *sx, const char *file, int line)
122{
123
124	mtx_lock(&sx->sx_lock);
125	if (sx->sx_cnt >= 0) {
126		sx->sx_cnt++;
127		LOCK_LOG_TRY("SLOCK", &sx->sx_object, 0, 1, file, line);
128		WITNESS_LOCK(&sx->sx_object, LOP_TRYLOCK, file, line);
129		mtx_unlock(&sx->sx_lock);
130		return (1);
131	} else {
132		LOCK_LOG_TRY("SLOCK", &sx->sx_object, 0, 0, file, line);
133		mtx_unlock(&sx->sx_lock);
134		return (0);
135	}
136}
137
138void
139_sx_xlock(struct sx *sx, const char *file, int line)
140{
141
142	mtx_lock(&sx->sx_lock);
143
144	/*
145	 * With sx locks, we're absolutely not permitted to recurse on
146	 * xlocks, as it is fatal (deadlock). Normally, recursion is handled
147	 * by WITNESS, but as it is not semantically correct to hold the
148	 * xlock while in here, we consider it API abuse and put it under
149	 * INVARIANTS.
150	 */
151	KASSERT(sx->sx_xholder != curproc,
152	    ("%s (%s): xlock already held @ %s:%d", __FUNCTION__,
153	    sx->sx_object.lo_name, file, line));
154
155	/* Loop in case we lose the race for lock acquisition. */
156	while (sx->sx_cnt != 0) {
157		sx->sx_excl_wcnt++;
158		cv_wait(&sx->sx_excl_cv, &sx->sx_lock);
159		sx->sx_excl_wcnt--;
160	}
161
162	MPASS(sx->sx_cnt == 0);
163
164	/* Acquire an exclusive lock. */
165	sx->sx_cnt--;
166	sx->sx_xholder = curproc;
167
168	LOCK_LOG_LOCK("XLOCK", &sx->sx_object, 0, 0, file, line);
169	WITNESS_LOCK(&sx->sx_object, LOP_EXCLUSIVE, file, line);
170
171	mtx_unlock(&sx->sx_lock);
172}
173
174int
175_sx_try_xlock(struct sx *sx, const char *file, int line)
176{
177
178	mtx_lock(&sx->sx_lock);
179	if (sx->sx_cnt == 0) {
180		sx->sx_cnt--;
181		sx->sx_xholder = curproc;
182		LOCK_LOG_TRY("XLOCK", &sx->sx_object, 0, 1, file, line);
183		WITNESS_LOCK(&sx->sx_object, LOP_EXCLUSIVE | LOP_TRYLOCK, file,
184		    line);
185		mtx_unlock(&sx->sx_lock);
186		return (1);
187	} else {
188		LOCK_LOG_TRY("XLOCK", &sx->sx_object, 0, 0, file, line);
189		mtx_unlock(&sx->sx_lock);
190		return (0);
191	}
192}
193
194void
195_sx_sunlock(struct sx *sx, const char *file, int line)
196{
197
198	mtx_lock(&sx->sx_lock);
199	_SX_ASSERT_SLOCKED(sx, file, line);
200
201	WITNESS_UNLOCK(&sx->sx_object, 0, file, line);
202
203	/* Release. */
204	sx->sx_cnt--;
205
206	/*
207	 * If we just released the last shared lock, wake any waiters up, giving
208	 * exclusive lockers precedence.  In order to make sure that exclusive
209	 * lockers won't be blocked forever, don't wake shared lock waiters if
210	 * there are exclusive lock waiters.
211	 */
212	if (sx->sx_excl_wcnt > 0) {
213		if (sx->sx_cnt == 0)
214			cv_signal(&sx->sx_excl_cv);
215	} else if (sx->sx_shrd_wcnt > 0)
216		cv_broadcast(&sx->sx_shrd_cv);
217
218	LOCK_LOG_LOCK("SUNLOCK", &sx->sx_object, 0, 0, file, line);
219
220	mtx_unlock(&sx->sx_lock);
221}
222
223void
224_sx_xunlock(struct sx *sx, const char *file, int line)
225{
226
227	mtx_lock(&sx->sx_lock);
228	_SX_ASSERT_XLOCKED(sx, file, line);
229	MPASS(sx->sx_cnt == -1);
230
231	WITNESS_UNLOCK(&sx->sx_object, LOP_EXCLUSIVE, file, line);
232
233	/* Release. */
234	sx->sx_cnt++;
235	sx->sx_xholder = NULL;
236
237	/*
238	 * Wake up waiters if there are any.  Give precedence to slock waiters.
239	 */
240	if (sx->sx_shrd_wcnt > 0)
241		cv_broadcast(&sx->sx_shrd_cv);
242	else if (sx->sx_excl_wcnt > 0)
243		cv_signal(&sx->sx_excl_cv);
244
245	LOCK_LOG_LOCK("XUNLOCK", &sx->sx_object, 0, 0, file, line);
246
247	mtx_unlock(&sx->sx_lock);
248}
249
250int
251_sx_try_upgrade(struct sx *sx, const char *file, int line)
252{
253
254	mtx_lock(&sx->sx_lock);
255	_SX_ASSERT_SLOCKED(sx, file, line);
256
257	if (sx->sx_cnt == 1) {
258		sx->sx_cnt = -1;
259		sx->sx_xholder = curproc;
260
261		LOCK_LOG_TRY("XUPGRADE", &sx->sx_object, 0, 1, file, line);
262		WITNESS_UPGRADE(&sx->sx_object, LOP_EXCLUSIVE | LOP_TRYLOCK,
263		    file, line);
264
265		mtx_unlock(&sx->sx_lock);
266		return (1);
267	} else {
268		LOCK_LOG_TRY("XUPGRADE", &sx->sx_object, 0, 0, file, line);
269		mtx_unlock(&sx->sx_lock);
270		return (0);
271	}
272}
273
274void
275_sx_downgrade(struct sx *sx, const char *file, int line)
276{
277
278	mtx_lock(&sx->sx_lock);
279	_SX_ASSERT_XLOCKED(sx, file, line);
280	MPASS(sx->sx_cnt == -1);
281
282	WITNESS_DOWNGRADE(&sx->sx_object, 0, file, line);
283
284	sx->sx_cnt = 1;
285	sx->sx_xholder = NULL;
286        if (sx->sx_shrd_wcnt > 0)
287                cv_broadcast(&sx->sx_shrd_cv);
288
289	LOCK_LOG_LOCK("XDOWNGRADE", &sx->sx_object, 0, 0, file, line);
290
291	mtx_unlock(&sx->sx_lock);
292}
293