xref: /onnv-gate/usr/src/uts/common/fs/zfs/zrlock.c (revision 12684:397e44ebb8a9)
1*12684STom.Erickson@Sun.COM /*
2*12684STom.Erickson@Sun.COM  * CDDL HEADER START
3*12684STom.Erickson@Sun.COM  *
4*12684STom.Erickson@Sun.COM  * The contents of this file are subject to the terms of the
5*12684STom.Erickson@Sun.COM  * Common Development and Distribution License (the "License").
6*12684STom.Erickson@Sun.COM  * You may not use this file except in compliance with the License.
7*12684STom.Erickson@Sun.COM  *
8*12684STom.Erickson@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*12684STom.Erickson@Sun.COM  * or http://www.opensolaris.org/os/licensing.
10*12684STom.Erickson@Sun.COM  * See the License for the specific language governing permissions
11*12684STom.Erickson@Sun.COM  * and limitations under the License.
12*12684STom.Erickson@Sun.COM  *
13*12684STom.Erickson@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
14*12684STom.Erickson@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*12684STom.Erickson@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
16*12684STom.Erickson@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
17*12684STom.Erickson@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
18*12684STom.Erickson@Sun.COM  *
19*12684STom.Erickson@Sun.COM  * CDDL HEADER END
20*12684STom.Erickson@Sun.COM  */
21*12684STom.Erickson@Sun.COM /*
22*12684STom.Erickson@Sun.COM  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
23*12684STom.Erickson@Sun.COM  */
24*12684STom.Erickson@Sun.COM 
25*12684STom.Erickson@Sun.COM /*
26*12684STom.Erickson@Sun.COM  * A Zero Reference Lock (ZRL) is a reference count that can lock out new
27*12684STom.Erickson@Sun.COM  * references only when the count is zero and only without waiting if the count
28*12684STom.Erickson@Sun.COM  * is not already zero. It is similar to a read-write lock in that it allows
29*12684STom.Erickson@Sun.COM  * multiple readers and only a single writer, but it does not allow a writer to
30*12684STom.Erickson@Sun.COM  * block while waiting for readers to exit, and therefore the question of
31*12684STom.Erickson@Sun.COM  * reader/writer priority is moot (no WRWANT bit). Since the equivalent of
32*12684STom.Erickson@Sun.COM  * rw_enter(&lock, RW_WRITER) is disallowed and only tryenter() is allowed, it
33*12684STom.Erickson@Sun.COM  * is perfectly safe for the same reader to acquire the same lock multiple
34*12684STom.Erickson@Sun.COM  * times. The fact that a ZRL is reentrant for readers (through multiple calls
35*12684STom.Erickson@Sun.COM  * to zrl_add()) makes it convenient for determining whether something is
36*12684STom.Erickson@Sun.COM  * actively referenced without the fuss of flagging lock ownership across
37*12684STom.Erickson@Sun.COM  * function calls.
38*12684STom.Erickson@Sun.COM  */
39*12684STom.Erickson@Sun.COM #include <sys/zrlock.h>
40*12684STom.Erickson@Sun.COM 
41*12684STom.Erickson@Sun.COM /*
42*12684STom.Erickson@Sun.COM  * A ZRL can be locked only while there are zero references, so ZRL_LOCKED is
43*12684STom.Erickson@Sun.COM  * treated as zero references.
44*12684STom.Erickson@Sun.COM  */
45*12684STom.Erickson@Sun.COM #define	ZRL_LOCKED	((uint32_t)-1)
46*12684STom.Erickson@Sun.COM #define	ZRL_DESTROYED	-2
47*12684STom.Erickson@Sun.COM 
48*12684STom.Erickson@Sun.COM void
zrl_init(zrlock_t * zrl)49*12684STom.Erickson@Sun.COM zrl_init(zrlock_t *zrl)
50*12684STom.Erickson@Sun.COM {
51*12684STom.Erickson@Sun.COM 	mutex_init(&zrl->zr_mtx, NULL, MUTEX_DEFAULT, NULL);
52*12684STom.Erickson@Sun.COM 	zrl->zr_refcount = 0;
53*12684STom.Erickson@Sun.COM 	cv_init(&zrl->zr_cv, NULL, CV_DEFAULT, NULL);
54*12684STom.Erickson@Sun.COM #ifdef	ZFS_DEBUG
55*12684STom.Erickson@Sun.COM 	zrl->zr_owner = NULL;
56*12684STom.Erickson@Sun.COM 	zrl->zr_caller = NULL;
57*12684STom.Erickson@Sun.COM #endif
58*12684STom.Erickson@Sun.COM }
59*12684STom.Erickson@Sun.COM 
60*12684STom.Erickson@Sun.COM void
zrl_destroy(zrlock_t * zrl)61*12684STom.Erickson@Sun.COM zrl_destroy(zrlock_t *zrl)
62*12684STom.Erickson@Sun.COM {
63*12684STom.Erickson@Sun.COM 	ASSERT(zrl->zr_refcount == 0);
64*12684STom.Erickson@Sun.COM 
65*12684STom.Erickson@Sun.COM 	mutex_destroy(&zrl->zr_mtx);
66*12684STom.Erickson@Sun.COM 	zrl->zr_refcount = ZRL_DESTROYED;
67*12684STom.Erickson@Sun.COM 	cv_destroy(&zrl->zr_cv);
68*12684STom.Erickson@Sun.COM }
69*12684STom.Erickson@Sun.COM 
70*12684STom.Erickson@Sun.COM void
71*12684STom.Erickson@Sun.COM #ifdef	ZFS_DEBUG
zrl_add_debug(zrlock_t * zrl,const char * zc)72*12684STom.Erickson@Sun.COM zrl_add_debug(zrlock_t *zrl, const char *zc)
73*12684STom.Erickson@Sun.COM #else
74*12684STom.Erickson@Sun.COM zrl_add(zrlock_t *zrl)
75*12684STom.Erickson@Sun.COM #endif
76*12684STom.Erickson@Sun.COM {
77*12684STom.Erickson@Sun.COM 	uint32_t n = (uint32_t)zrl->zr_refcount;
78*12684STom.Erickson@Sun.COM 
79*12684STom.Erickson@Sun.COM 	while (n != ZRL_LOCKED) {
80*12684STom.Erickson@Sun.COM 		uint32_t cas = atomic_cas_32(
81*12684STom.Erickson@Sun.COM 		    (uint32_t *)&zrl->zr_refcount, n, n + 1);
82*12684STom.Erickson@Sun.COM 		if (cas == n) {
83*12684STom.Erickson@Sun.COM 			ASSERT((int32_t)n >= 0);
84*12684STom.Erickson@Sun.COM #ifdef	ZFS_DEBUG
85*12684STom.Erickson@Sun.COM 			if (zrl->zr_owner == curthread) {
86*12684STom.Erickson@Sun.COM 				DTRACE_PROBE2(zrlock__reentry,
87*12684STom.Erickson@Sun.COM 				    zrlock_t *, zrl, uint32_t, n);
88*12684STom.Erickson@Sun.COM 			}
89*12684STom.Erickson@Sun.COM 			zrl->zr_owner = curthread;
90*12684STom.Erickson@Sun.COM 			zrl->zr_caller = zc;
91*12684STom.Erickson@Sun.COM #endif
92*12684STom.Erickson@Sun.COM 			return;
93*12684STom.Erickson@Sun.COM 		}
94*12684STom.Erickson@Sun.COM 		n = cas;
95*12684STom.Erickson@Sun.COM 	}
96*12684STom.Erickson@Sun.COM 
97*12684STom.Erickson@Sun.COM 	mutex_enter(&zrl->zr_mtx);
98*12684STom.Erickson@Sun.COM 	while (zrl->zr_refcount == ZRL_LOCKED) {
99*12684STom.Erickson@Sun.COM 		cv_wait(&zrl->zr_cv, &zrl->zr_mtx);
100*12684STom.Erickson@Sun.COM 	}
101*12684STom.Erickson@Sun.COM 	ASSERT(zrl->zr_refcount >= 0);
102*12684STom.Erickson@Sun.COM 	zrl->zr_refcount++;
103*12684STom.Erickson@Sun.COM #ifdef	ZFS_DEBUG
104*12684STom.Erickson@Sun.COM 	zrl->zr_owner = curthread;
105*12684STom.Erickson@Sun.COM 	zrl->zr_caller = zc;
106*12684STom.Erickson@Sun.COM #endif
107*12684STom.Erickson@Sun.COM 	mutex_exit(&zrl->zr_mtx);
108*12684STom.Erickson@Sun.COM }
109*12684STom.Erickson@Sun.COM 
110*12684STom.Erickson@Sun.COM void
zrl_remove(zrlock_t * zrl)111*12684STom.Erickson@Sun.COM zrl_remove(zrlock_t *zrl)
112*12684STom.Erickson@Sun.COM {
113*12684STom.Erickson@Sun.COM 	uint32_t n;
114*12684STom.Erickson@Sun.COM 
115*12684STom.Erickson@Sun.COM 	n = atomic_dec_32_nv((uint32_t *)&zrl->zr_refcount);
116*12684STom.Erickson@Sun.COM 	ASSERT((int32_t)n >= 0);
117*12684STom.Erickson@Sun.COM #ifdef	ZFS_DEBUG
118*12684STom.Erickson@Sun.COM 	if (zrl->zr_owner == curthread) {
119*12684STom.Erickson@Sun.COM 		zrl->zr_owner = NULL;
120*12684STom.Erickson@Sun.COM 		zrl->zr_caller = NULL;
121*12684STom.Erickson@Sun.COM 	}
122*12684STom.Erickson@Sun.COM #endif
123*12684STom.Erickson@Sun.COM }
124*12684STom.Erickson@Sun.COM 
125*12684STom.Erickson@Sun.COM int
zrl_tryenter(zrlock_t * zrl)126*12684STom.Erickson@Sun.COM zrl_tryenter(zrlock_t *zrl)
127*12684STom.Erickson@Sun.COM {
128*12684STom.Erickson@Sun.COM 	uint32_t n = (uint32_t)zrl->zr_refcount;
129*12684STom.Erickson@Sun.COM 
130*12684STom.Erickson@Sun.COM 	if (n == 0) {
131*12684STom.Erickson@Sun.COM 		uint32_t cas = atomic_cas_32(
132*12684STom.Erickson@Sun.COM 		    (uint32_t *)&zrl->zr_refcount, 0, ZRL_LOCKED);
133*12684STom.Erickson@Sun.COM 		if (cas == 0) {
134*12684STom.Erickson@Sun.COM #ifdef	ZFS_DEBUG
135*12684STom.Erickson@Sun.COM 			ASSERT(zrl->zr_owner == NULL);
136*12684STom.Erickson@Sun.COM 			zrl->zr_owner = curthread;
137*12684STom.Erickson@Sun.COM #endif
138*12684STom.Erickson@Sun.COM 			return (1);
139*12684STom.Erickson@Sun.COM 		}
140*12684STom.Erickson@Sun.COM 	}
141*12684STom.Erickson@Sun.COM 
142*12684STom.Erickson@Sun.COM 	ASSERT((int32_t)n > ZRL_DESTROYED);
143*12684STom.Erickson@Sun.COM 
144*12684STom.Erickson@Sun.COM 	return (0);
145*12684STom.Erickson@Sun.COM }
146*12684STom.Erickson@Sun.COM 
147*12684STom.Erickson@Sun.COM void
zrl_exit(zrlock_t * zrl)148*12684STom.Erickson@Sun.COM zrl_exit(zrlock_t *zrl)
149*12684STom.Erickson@Sun.COM {
150*12684STom.Erickson@Sun.COM 	ASSERT(zrl->zr_refcount == ZRL_LOCKED);
151*12684STom.Erickson@Sun.COM 
152*12684STom.Erickson@Sun.COM 	mutex_enter(&zrl->zr_mtx);
153*12684STom.Erickson@Sun.COM #ifdef	ZFS_DEBUG
154*12684STom.Erickson@Sun.COM 	ASSERT(zrl->zr_owner == curthread);
155*12684STom.Erickson@Sun.COM 	zrl->zr_owner = NULL;
156*12684STom.Erickson@Sun.COM 	membar_producer();	/* make sure the owner store happens first */
157*12684STom.Erickson@Sun.COM #endif
158*12684STom.Erickson@Sun.COM 	zrl->zr_refcount = 0;
159*12684STom.Erickson@Sun.COM 	cv_broadcast(&zrl->zr_cv);
160*12684STom.Erickson@Sun.COM 	mutex_exit(&zrl->zr_mtx);
161*12684STom.Erickson@Sun.COM }
162*12684STom.Erickson@Sun.COM 
163*12684STom.Erickson@Sun.COM int
zrl_refcount(zrlock_t * zrl)164*12684STom.Erickson@Sun.COM zrl_refcount(zrlock_t *zrl)
165*12684STom.Erickson@Sun.COM {
166*12684STom.Erickson@Sun.COM 	ASSERT(zrl->zr_refcount > ZRL_DESTROYED);
167*12684STom.Erickson@Sun.COM 
168*12684STom.Erickson@Sun.COM 	int n = (int)zrl->zr_refcount;
169*12684STom.Erickson@Sun.COM 	return (n <= 0 ? 0 : n);
170*12684STom.Erickson@Sun.COM }
171*12684STom.Erickson@Sun.COM 
172*12684STom.Erickson@Sun.COM int
zrl_is_zero(zrlock_t * zrl)173*12684STom.Erickson@Sun.COM zrl_is_zero(zrlock_t *zrl)
174*12684STom.Erickson@Sun.COM {
175*12684STom.Erickson@Sun.COM 	ASSERT(zrl->zr_refcount > ZRL_DESTROYED);
176*12684STom.Erickson@Sun.COM 
177*12684STom.Erickson@Sun.COM 	return (zrl->zr_refcount <= 0);
178*12684STom.Erickson@Sun.COM }
179*12684STom.Erickson@Sun.COM 
180*12684STom.Erickson@Sun.COM int
zrl_is_locked(zrlock_t * zrl)181*12684STom.Erickson@Sun.COM zrl_is_locked(zrlock_t *zrl)
182*12684STom.Erickson@Sun.COM {
183*12684STom.Erickson@Sun.COM 	ASSERT(zrl->zr_refcount > ZRL_DESTROYED);
184*12684STom.Erickson@Sun.COM 
185*12684STom.Erickson@Sun.COM 	return (zrl->zr_refcount == ZRL_LOCKED);
186*12684STom.Erickson@Sun.COM }
187*12684STom.Erickson@Sun.COM 
188*12684STom.Erickson@Sun.COM #ifdef	ZFS_DEBUG
189*12684STom.Erickson@Sun.COM kthread_t *
zrl_owner(zrlock_t * zrl)190*12684STom.Erickson@Sun.COM zrl_owner(zrlock_t *zrl)
191*12684STom.Erickson@Sun.COM {
192*12684STom.Erickson@Sun.COM 	return (zrl->zr_owner);
193*12684STom.Erickson@Sun.COM }
194*12684STom.Erickson@Sun.COM #endif
195