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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 /*
26 * A Zero Reference Lock (ZRL) is a reference count that can lock out new
27 * references only when the count is zero and only without waiting if the count
28 * is not already zero. It is similar to a read-write lock in that it allows
29 * multiple readers and only a single writer, but it does not allow a writer to
30 * block while waiting for readers to exit, and therefore the question of
31 * reader/writer priority is moot (no WRWANT bit). Since the equivalent of
32 * rw_enter(&lock, RW_WRITER) is disallowed and only tryenter() is allowed, it
33 * is perfectly safe for the same reader to acquire the same lock multiple
34 * times. The fact that a ZRL is reentrant for readers (through multiple calls
35 * to zrl_add()) makes it convenient for determining whether something is
36 * actively referenced without the fuss of flagging lock ownership across
37 * function calls.
38 */
39 #include <sys/zrlock.h>
40
41 /*
42 * A ZRL can be locked only while there are zero references, so ZRL_LOCKED is
43 * treated as zero references.
44 */
45 #define ZRL_LOCKED ((uint32_t)-1)
46 #define ZRL_DESTROYED -2
47
48 void
49 zrl_init(zrlock_t *zrl)
50 {
51 mutex_init(&zrl->zr_mtx, NULL, MUTEX_DEFAULT, NULL);
52 zrl->zr_refcount = 0;
53 cv_init(&zrl->zr_cv, NULL, CV_DEFAULT, NULL);
54 #ifdef ZFS_DEBUG
55 zrl->zr_owner = NULL;
56 zrl->zr_caller = NULL;
57 #endif
58 }
59
60 void
61 zrl_destroy(zrlock_t *zrl)
62 {
63 ASSERT(zrl->zr_refcount == 0);
64
65 mutex_destroy(&zrl->zr_mtx);
66 zrl->zr_refcount = ZRL_DESTROYED;
67 cv_destroy(&zrl->zr_cv);
68 }
69
70 void
71 #ifdef ZFS_DEBUG
72 zrl_add_debug(zrlock_t *zrl, const char *zc)
73 #else
74 zrl_add(zrlock_t *zrl)
75 #endif
76 {
77 uint32_t n = (uint32_t)zrl->zr_refcount;
78
79 while (1) {
80 while (n != ZRL_LOCKED) {
81 uint32_t cas = atomic_cas_32(
82 (uint32_t *)&zrl->zr_refcount, n, n + 1);
83 if (cas == n) {
84 ASSERT((int32_t)n >= 0);
85 #ifdef ZFS_DEBUG
86 if (zrl->zr_owner == curthread) {
87 DTRACE_PROBE2(zrlock__reentry,
88 zrlock_t *, zrl, uint32_t, n);
89 }
90 zrl->zr_owner = curthread;
91 zrl->zr_caller = zc;
92 #endif
93 return;
94 }
95 n = cas;
96 }
97 mutex_enter(&zrl->zr_mtx);
98 while (zrl->zr_refcount == ZRL_LOCKED) {
99 cv_wait(&zrl->zr_cv, &zrl->zr_mtx);
100 }
101 mutex_exit(&zrl->zr_mtx);
102 }
103 }
104
105 void
106 zrl_remove(zrlock_t *zrl)
107 {
108 uint32_t n;
109
110 n = atomic_dec_32_nv((uint32_t *)&zrl->zr_refcount);
111 ASSERT((int32_t)n >= 0);
112 #ifdef ZFS_DEBUG
113 if (zrl->zr_owner == curthread) {
114 zrl->zr_owner = NULL;
115 zrl->zr_caller = NULL;
116 }
117 #endif
118 }
119
120 int
121 zrl_tryenter(zrlock_t *zrl)
122 {
123 uint32_t n = (uint32_t)zrl->zr_refcount;
124
125 if (n == 0) {
126 uint32_t cas = atomic_cas_32(
127 (uint32_t *)&zrl->zr_refcount, 0, ZRL_LOCKED);
128 if (cas == 0) {
129 #ifdef ZFS_DEBUG
130 ASSERT(zrl->zr_owner == NULL);
131 zrl->zr_owner = curthread;
132 #endif
133 return (1);
134 }
135 }
136
137 ASSERT((int32_t)n > ZRL_DESTROYED);
138
139 return (0);
140 }
141
142 void
143 zrl_exit(zrlock_t *zrl)
144 {
145 ASSERT(zrl->zr_refcount == ZRL_LOCKED);
146
147 mutex_enter(&zrl->zr_mtx);
148 #ifdef ZFS_DEBUG
149 ASSERT(zrl->zr_owner == curthread);
150 zrl->zr_owner = NULL;
151 membar_producer(); /* make sure the owner store happens first */
152 #endif
153 zrl->zr_refcount = 0;
154 cv_broadcast(&zrl->zr_cv);
155 mutex_exit(&zrl->zr_mtx);
156 }
157
158 int
159 zrl_refcount(zrlock_t *zrl)
160 {
161 ASSERT(zrl->zr_refcount > ZRL_DESTROYED);
162
163 int n = (int)zrl->zr_refcount;
164 return (n <= 0 ? 0 : n);
165 }
166
167 int
168 zrl_is_zero(zrlock_t *zrl)
169 {
170 ASSERT(zrl->zr_refcount > ZRL_DESTROYED);
171
172 return (zrl->zr_refcount <= 0);
173 }
174
175 int
176 zrl_is_locked(zrlock_t *zrl)
177 {
178 ASSERT(zrl->zr_refcount > ZRL_DESTROYED);
179
180 return (zrl->zr_refcount == ZRL_LOCKED);
181 }
182
183 #ifdef ZFS_DEBUG
184 kthread_t *
185 zrl_owner(zrlock_t *zrl)
186 {
187 return (zrl->zr_owner);
188 }
189 #endif