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