1 // SPDX-License-Identifier: CDDL-1.0
2 /*
3 * CDDL HEADER START
4 *
5 * The contents of this file are subject to the terms of the
6 * Common Development and Distribution License (the "License").
7 * You may not use this file except in compliance with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or https://opensource.org/licenses/CDDL-1.0.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2012, 2021 by Delphix. All rights reserved.
25 */
26
27 #include <sys/zfs_context.h>
28 #include <sys/zfs_refcount.h>
29
30 #ifdef ZFS_DEBUG
31 /*
32 * Reference count tracking is disabled by default. It's memory requirements
33 * are reasonable, however as implemented it consumes a significant amount of
34 * cpu time. Until its performance is improved it should be manually enabled.
35 */
36 int reference_tracking_enable = B_FALSE;
37 static uint_t reference_history = 3; /* tunable */
38
39 static kmem_cache_t *reference_cache;
40
41 void
zfs_refcount_init(void)42 zfs_refcount_init(void)
43 {
44 reference_cache = kmem_cache_create("reference_cache",
45 sizeof (reference_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
46 }
47
48 void
zfs_refcount_fini(void)49 zfs_refcount_fini(void)
50 {
51 kmem_cache_destroy(reference_cache);
52 }
53
54 static int
zfs_refcount_compare(const void * x1,const void * x2)55 zfs_refcount_compare(const void *x1, const void *x2)
56 {
57 const reference_t *r1 = (const reference_t *)x1;
58 const reference_t *r2 = (const reference_t *)x2;
59
60 int cmp1 = TREE_CMP(r1->ref_holder, r2->ref_holder);
61 int cmp2 = TREE_CMP(r1->ref_number, r2->ref_number);
62 int cmp = cmp1 ? cmp1 : cmp2;
63 return ((cmp || r1->ref_search) ? cmp : TREE_PCMP(r1, r2));
64 }
65
66 void
zfs_refcount_create(zfs_refcount_t * rc)67 zfs_refcount_create(zfs_refcount_t *rc)
68 {
69 mutex_init(&rc->rc_mtx, NULL, MUTEX_DEFAULT, NULL);
70 avl_create(&rc->rc_tree, zfs_refcount_compare, sizeof (reference_t),
71 offsetof(reference_t, ref_link.a));
72 list_create(&rc->rc_removed, sizeof (reference_t),
73 offsetof(reference_t, ref_link.l));
74 rc->rc_count = 0;
75 rc->rc_removed_count = 0;
76 rc->rc_tracked = reference_tracking_enable;
77 }
78
79 void
zfs_refcount_create_tracked(zfs_refcount_t * rc)80 zfs_refcount_create_tracked(zfs_refcount_t *rc)
81 {
82 zfs_refcount_create(rc);
83 rc->rc_tracked = B_TRUE;
84 }
85
86 void
zfs_refcount_create_untracked(zfs_refcount_t * rc)87 zfs_refcount_create_untracked(zfs_refcount_t *rc)
88 {
89 zfs_refcount_create(rc);
90 rc->rc_tracked = B_FALSE;
91 }
92
93 void
zfs_refcount_destroy_many(zfs_refcount_t * rc,uint64_t number)94 zfs_refcount_destroy_many(zfs_refcount_t *rc, uint64_t number)
95 {
96 reference_t *ref;
97 void *cookie = NULL;
98
99 ASSERT3U(rc->rc_count, ==, number);
100 while ((ref = avl_destroy_nodes(&rc->rc_tree, &cookie)) != NULL)
101 kmem_cache_free(reference_cache, ref);
102 avl_destroy(&rc->rc_tree);
103
104 while ((ref = list_remove_head(&rc->rc_removed)))
105 kmem_cache_free(reference_cache, ref);
106 list_destroy(&rc->rc_removed);
107 mutex_destroy(&rc->rc_mtx);
108 }
109
110 void
zfs_refcount_destroy(zfs_refcount_t * rc)111 zfs_refcount_destroy(zfs_refcount_t *rc)
112 {
113 zfs_refcount_destroy_many(rc, 0);
114 }
115
116 int
zfs_refcount_is_zero(zfs_refcount_t * rc)117 zfs_refcount_is_zero(zfs_refcount_t *rc)
118 {
119 return (zfs_refcount_count(rc) == 0);
120 }
121
122 int64_t
zfs_refcount_count(zfs_refcount_t * rc)123 zfs_refcount_count(zfs_refcount_t *rc)
124 {
125 return (atomic_load_64(&rc->rc_count));
126 }
127
128 int64_t
zfs_refcount_add_many(zfs_refcount_t * rc,uint64_t number,const void * holder)129 zfs_refcount_add_many(zfs_refcount_t *rc, uint64_t number, const void *holder)
130 {
131 reference_t *ref;
132 int64_t count;
133
134 if (likely(!rc->rc_tracked)) {
135 count = atomic_add_64_nv(&(rc)->rc_count, number);
136 ASSERT3U(count, >=, number);
137 return (count);
138 }
139
140 ref = kmem_cache_alloc(reference_cache, KM_SLEEP);
141 ref->ref_holder = holder;
142 ref->ref_number = number;
143 ref->ref_search = B_FALSE;
144 mutex_enter(&rc->rc_mtx);
145 avl_add(&rc->rc_tree, ref);
146 rc->rc_count += number;
147 count = rc->rc_count;
148 mutex_exit(&rc->rc_mtx);
149
150 return (count);
151 }
152
153 int64_t
zfs_refcount_add(zfs_refcount_t * rc,const void * holder)154 zfs_refcount_add(zfs_refcount_t *rc, const void *holder)
155 {
156 return (zfs_refcount_add_many(rc, 1, holder));
157 }
158
159 void
zfs_refcount_add_few(zfs_refcount_t * rc,uint64_t number,const void * holder)160 zfs_refcount_add_few(zfs_refcount_t *rc, uint64_t number, const void *holder)
161 {
162 if (likely(!rc->rc_tracked))
163 (void) zfs_refcount_add_many(rc, number, holder);
164 else for (; number > 0; number--)
165 (void) zfs_refcount_add(rc, holder);
166 }
167
168 int64_t
zfs_refcount_remove_many(zfs_refcount_t * rc,uint64_t number,const void * holder)169 zfs_refcount_remove_many(zfs_refcount_t *rc, uint64_t number,
170 const void *holder)
171 {
172 reference_t *ref, s;
173 int64_t count;
174
175 if (likely(!rc->rc_tracked)) {
176 count = atomic_add_64_nv(&(rc)->rc_count, -number);
177 ASSERT3S(count, >=, 0);
178 return (count);
179 }
180
181 s.ref_holder = holder;
182 s.ref_number = number;
183 s.ref_search = B_TRUE;
184 mutex_enter(&rc->rc_mtx);
185 ASSERT3U(rc->rc_count, >=, number);
186 ref = avl_find(&rc->rc_tree, &s, NULL);
187 if (unlikely(ref == NULL)) {
188 panic("No such hold %p on refcount %llx", holder,
189 (u_longlong_t)(uintptr_t)rc);
190 return (-1);
191 }
192 avl_remove(&rc->rc_tree, ref);
193 if (reference_history > 0) {
194 list_insert_head(&rc->rc_removed, ref);
195 if (rc->rc_removed_count >= reference_history) {
196 ref = list_remove_tail(&rc->rc_removed);
197 kmem_cache_free(reference_cache, ref);
198 } else {
199 rc->rc_removed_count++;
200 }
201 } else {
202 kmem_cache_free(reference_cache, ref);
203 }
204 rc->rc_count -= number;
205 count = rc->rc_count;
206 mutex_exit(&rc->rc_mtx);
207 return (count);
208 }
209
210 int64_t
zfs_refcount_remove(zfs_refcount_t * rc,const void * holder)211 zfs_refcount_remove(zfs_refcount_t *rc, const void *holder)
212 {
213 return (zfs_refcount_remove_many(rc, 1, holder));
214 }
215
216 void
zfs_refcount_remove_few(zfs_refcount_t * rc,uint64_t number,const void * holder)217 zfs_refcount_remove_few(zfs_refcount_t *rc, uint64_t number, const void *holder)
218 {
219 if (likely(!rc->rc_tracked))
220 (void) zfs_refcount_remove_many(rc, number, holder);
221 else for (; number > 0; number--)
222 (void) zfs_refcount_remove(rc, holder);
223 }
224
225 void
zfs_refcount_transfer(zfs_refcount_t * dst,zfs_refcount_t * src)226 zfs_refcount_transfer(zfs_refcount_t *dst, zfs_refcount_t *src)
227 {
228 avl_tree_t tree;
229 list_t removed;
230 reference_t *ref;
231 void *cookie = NULL;
232 uint64_t count;
233 uint_t removed_count;
234
235 avl_create(&tree, zfs_refcount_compare, sizeof (reference_t),
236 offsetof(reference_t, ref_link.a));
237 list_create(&removed, sizeof (reference_t),
238 offsetof(reference_t, ref_link.l));
239
240 mutex_enter(&src->rc_mtx);
241 count = src->rc_count;
242 removed_count = src->rc_removed_count;
243 src->rc_count = 0;
244 src->rc_removed_count = 0;
245 avl_swap(&tree, &src->rc_tree);
246 list_move_tail(&removed, &src->rc_removed);
247 mutex_exit(&src->rc_mtx);
248
249 mutex_enter(&dst->rc_mtx);
250 dst->rc_count += count;
251 dst->rc_removed_count += removed_count;
252 if (avl_is_empty(&dst->rc_tree))
253 avl_swap(&dst->rc_tree, &tree);
254 else while ((ref = avl_destroy_nodes(&tree, &cookie)) != NULL)
255 avl_add(&dst->rc_tree, ref);
256 list_move_tail(&dst->rc_removed, &removed);
257 mutex_exit(&dst->rc_mtx);
258
259 avl_destroy(&tree);
260 list_destroy(&removed);
261 }
262
263 void
zfs_refcount_transfer_ownership_many(zfs_refcount_t * rc,uint64_t number,const void * current_holder,const void * new_holder)264 zfs_refcount_transfer_ownership_many(zfs_refcount_t *rc, uint64_t number,
265 const void *current_holder, const void *new_holder)
266 {
267 reference_t *ref, s;
268
269 if (likely(!rc->rc_tracked))
270 return;
271
272 s.ref_holder = current_holder;
273 s.ref_number = number;
274 s.ref_search = B_TRUE;
275 mutex_enter(&rc->rc_mtx);
276 ref = avl_find(&rc->rc_tree, &s, NULL);
277 ASSERT(ref);
278 ref->ref_holder = new_holder;
279 avl_update(&rc->rc_tree, ref);
280 mutex_exit(&rc->rc_mtx);
281 }
282
283 void
zfs_refcount_transfer_ownership(zfs_refcount_t * rc,const void * current_holder,const void * new_holder)284 zfs_refcount_transfer_ownership(zfs_refcount_t *rc, const void *current_holder,
285 const void *new_holder)
286 {
287 return (zfs_refcount_transfer_ownership_many(rc, 1, current_holder,
288 new_holder));
289 }
290
291 /*
292 * If tracking is enabled, return true if a reference exists that matches
293 * the "holder" tag. If tracking is disabled, then return true if a reference
294 * might be held.
295 */
296 boolean_t
zfs_refcount_held(zfs_refcount_t * rc,const void * holder)297 zfs_refcount_held(zfs_refcount_t *rc, const void *holder)
298 {
299 reference_t *ref, s;
300 avl_index_t idx;
301 boolean_t res;
302
303 if (likely(!rc->rc_tracked))
304 return (zfs_refcount_count(rc) > 0);
305
306 s.ref_holder = holder;
307 s.ref_number = 0;
308 s.ref_search = B_TRUE;
309 mutex_enter(&rc->rc_mtx);
310 ref = avl_find(&rc->rc_tree, &s, &idx);
311 if (likely(ref == NULL))
312 ref = avl_nearest(&rc->rc_tree, idx, AVL_AFTER);
313 res = ref && ref->ref_holder == holder;
314 mutex_exit(&rc->rc_mtx);
315 return (res);
316 }
317
318 /*
319 * If tracking is enabled, return true if a reference does not exist that
320 * matches the "holder" tag. If tracking is disabled, always return true
321 * since the reference might not be held.
322 */
323 boolean_t
zfs_refcount_not_held(zfs_refcount_t * rc,const void * holder)324 zfs_refcount_not_held(zfs_refcount_t *rc, const void *holder)
325 {
326 reference_t *ref, s;
327 avl_index_t idx;
328 boolean_t res;
329
330 if (likely(!rc->rc_tracked))
331 return (B_TRUE);
332
333 mutex_enter(&rc->rc_mtx);
334 s.ref_holder = holder;
335 s.ref_number = 0;
336 s.ref_search = B_TRUE;
337 ref = avl_find(&rc->rc_tree, &s, &idx);
338 if (likely(ref == NULL))
339 ref = avl_nearest(&rc->rc_tree, idx, AVL_AFTER);
340 res = ref == NULL || ref->ref_holder != holder;
341 mutex_exit(&rc->rc_mtx);
342 return (res);
343 }
344
345 EXPORT_SYMBOL(zfs_refcount_create);
346 EXPORT_SYMBOL(zfs_refcount_destroy);
347 EXPORT_SYMBOL(zfs_refcount_is_zero);
348 EXPORT_SYMBOL(zfs_refcount_count);
349 EXPORT_SYMBOL(zfs_refcount_add);
350 EXPORT_SYMBOL(zfs_refcount_remove);
351 EXPORT_SYMBOL(zfs_refcount_held);
352
353 ZFS_MODULE_PARAM(zfs, , reference_tracking_enable, INT, ZMOD_RW,
354 "Track reference holders to refcount_t objects");
355
356 ZFS_MODULE_PARAM(zfs, , reference_history, UINT, ZMOD_RW,
357 "Maximum reference holders being tracked");
358 #endif /* ZFS_DEBUG */
359