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