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 cmp = TREE_CMP(r1->ref_holder, r2->ref_holder);
61 if (cmp == 0)
62 cmp = TREE_CMP(r1->ref_number, r2->ref_number);
63 if (cmp | r1->ref_search)
64 return (cmp);
65 return (TREE_PCMP(r1, r2));
66 }
67
68 void
zfs_refcount_create(zfs_refcount_t * rc)69 zfs_refcount_create(zfs_refcount_t *rc)
70 {
71 mutex_init(&rc->rc_mtx, NULL, MUTEX_DEFAULT, NULL);
72 avl_create(&rc->rc_tree, zfs_refcount_compare, sizeof (reference_t),
73 offsetof(reference_t, ref_link.a));
74 list_create(&rc->rc_removed, sizeof (reference_t),
75 offsetof(reference_t, ref_link.l));
76 rc->rc_count = 0;
77 rc->rc_removed_count = 0;
78 rc->rc_tracked = reference_tracking_enable;
79 }
80
81 void
zfs_refcount_create_tracked(zfs_refcount_t * rc)82 zfs_refcount_create_tracked(zfs_refcount_t *rc)
83 {
84 zfs_refcount_create(rc);
85 rc->rc_tracked = B_TRUE;
86 }
87
88 void
zfs_refcount_create_untracked(zfs_refcount_t * rc)89 zfs_refcount_create_untracked(zfs_refcount_t *rc)
90 {
91 zfs_refcount_create(rc);
92 rc->rc_tracked = B_FALSE;
93 }
94
95 void
zfs_refcount_destroy_many(zfs_refcount_t * rc,uint64_t number)96 zfs_refcount_destroy_many(zfs_refcount_t *rc, uint64_t number)
97 {
98 reference_t *ref;
99 void *cookie = NULL;
100
101 ASSERT3U(rc->rc_count, ==, number);
102 while ((ref = avl_destroy_nodes(&rc->rc_tree, &cookie)) != NULL)
103 kmem_cache_free(reference_cache, ref);
104 avl_destroy(&rc->rc_tree);
105
106 while ((ref = list_remove_head(&rc->rc_removed)))
107 kmem_cache_free(reference_cache, ref);
108 list_destroy(&rc->rc_removed);
109 mutex_destroy(&rc->rc_mtx);
110 }
111
112 void
zfs_refcount_destroy(zfs_refcount_t * rc)113 zfs_refcount_destroy(zfs_refcount_t *rc)
114 {
115 zfs_refcount_destroy_many(rc, 0);
116 }
117
118 int
zfs_refcount_is_zero(zfs_refcount_t * rc)119 zfs_refcount_is_zero(zfs_refcount_t *rc)
120 {
121 return (zfs_refcount_count(rc) == 0);
122 }
123
124 int64_t
zfs_refcount_count(zfs_refcount_t * rc)125 zfs_refcount_count(zfs_refcount_t *rc)
126 {
127 return (atomic_load_64(&rc->rc_count));
128 }
129
130 int64_t
zfs_refcount_add_many(zfs_refcount_t * rc,uint64_t number,const void * holder)131 zfs_refcount_add_many(zfs_refcount_t *rc, uint64_t number, const void *holder)
132 {
133 reference_t *ref;
134 int64_t count;
135
136 if (likely(!rc->rc_tracked)) {
137 count = atomic_add_64_nv(&(rc)->rc_count, number);
138 ASSERT3U(count, >=, number);
139 return (count);
140 }
141
142 ref = kmem_cache_alloc(reference_cache, KM_SLEEP);
143 ref->ref_holder = holder;
144 ref->ref_number = number;
145 ref->ref_search = B_FALSE;
146 mutex_enter(&rc->rc_mtx);
147 avl_add(&rc->rc_tree, ref);
148 rc->rc_count += number;
149 count = rc->rc_count;
150 mutex_exit(&rc->rc_mtx);
151
152 return (count);
153 }
154
155 int64_t
zfs_refcount_add(zfs_refcount_t * rc,const void * holder)156 zfs_refcount_add(zfs_refcount_t *rc, const void *holder)
157 {
158 return (zfs_refcount_add_many(rc, 1, holder));
159 }
160
161 void
zfs_refcount_add_few(zfs_refcount_t * rc,uint64_t number,const void * holder)162 zfs_refcount_add_few(zfs_refcount_t *rc, uint64_t number, const void *holder)
163 {
164 if (likely(!rc->rc_tracked))
165 (void) zfs_refcount_add_many(rc, number, holder);
166 else for (; number > 0; number--)
167 (void) zfs_refcount_add(rc, holder);
168 }
169
170 int64_t
zfs_refcount_remove_many(zfs_refcount_t * rc,uint64_t number,const void * holder)171 zfs_refcount_remove_many(zfs_refcount_t *rc, uint64_t number,
172 const void *holder)
173 {
174 reference_t *ref, s;
175 int64_t count;
176
177 if (likely(!rc->rc_tracked)) {
178 count = atomic_add_64_nv(&(rc)->rc_count, -number);
179 ASSERT3S(count, >=, 0);
180 return (count);
181 }
182
183 s.ref_holder = holder;
184 s.ref_number = number;
185 s.ref_search = B_TRUE;
186 mutex_enter(&rc->rc_mtx);
187 ASSERT3U(rc->rc_count, >=, number);
188 ref = avl_find(&rc->rc_tree, &s, NULL);
189 if (unlikely(ref == NULL)) {
190 PANIC("No such hold %llx on refcount %llx",
191 (u_longlong_t)(uintptr_t)holder,
192 (u_longlong_t)(uintptr_t)rc);
193 return (-1);
194 }
195 avl_remove(&rc->rc_tree, ref);
196 if (reference_history > 0) {
197 list_insert_head(&rc->rc_removed, ref);
198 if (rc->rc_removed_count >= reference_history) {
199 ref = list_remove_tail(&rc->rc_removed);
200 kmem_cache_free(reference_cache, ref);
201 } else {
202 rc->rc_removed_count++;
203 }
204 } else {
205 kmem_cache_free(reference_cache, ref);
206 }
207 rc->rc_count -= number;
208 count = rc->rc_count;
209 mutex_exit(&rc->rc_mtx);
210 return (count);
211 }
212
213 int64_t
zfs_refcount_remove(zfs_refcount_t * rc,const void * holder)214 zfs_refcount_remove(zfs_refcount_t *rc, const void *holder)
215 {
216 return (zfs_refcount_remove_many(rc, 1, holder));
217 }
218
219 void
zfs_refcount_remove_few(zfs_refcount_t * rc,uint64_t number,const void * holder)220 zfs_refcount_remove_few(zfs_refcount_t *rc, uint64_t number, const void *holder)
221 {
222 if (likely(!rc->rc_tracked))
223 (void) zfs_refcount_remove_many(rc, number, holder);
224 else for (; number > 0; number--)
225 (void) zfs_refcount_remove(rc, holder);
226 }
227
228 void
zfs_refcount_transfer(zfs_refcount_t * dst,zfs_refcount_t * src)229 zfs_refcount_transfer(zfs_refcount_t *dst, zfs_refcount_t *src)
230 {
231 avl_tree_t tree;
232 list_t removed;
233 reference_t *ref;
234 void *cookie = NULL;
235 uint64_t count;
236 uint_t removed_count;
237
238 avl_create(&tree, zfs_refcount_compare, sizeof (reference_t),
239 offsetof(reference_t, ref_link.a));
240 list_create(&removed, sizeof (reference_t),
241 offsetof(reference_t, ref_link.l));
242
243 mutex_enter(&src->rc_mtx);
244 count = src->rc_count;
245 removed_count = src->rc_removed_count;
246 src->rc_count = 0;
247 src->rc_removed_count = 0;
248 avl_swap(&tree, &src->rc_tree);
249 list_move_tail(&removed, &src->rc_removed);
250 mutex_exit(&src->rc_mtx);
251
252 mutex_enter(&dst->rc_mtx);
253 dst->rc_count += count;
254 dst->rc_removed_count += removed_count;
255 if (avl_is_empty(&dst->rc_tree))
256 avl_swap(&dst->rc_tree, &tree);
257 else while ((ref = avl_destroy_nodes(&tree, &cookie)) != NULL)
258 avl_add(&dst->rc_tree, ref);
259 list_move_tail(&dst->rc_removed, &removed);
260 mutex_exit(&dst->rc_mtx);
261
262 avl_destroy(&tree);
263 list_destroy(&removed);
264 }
265
266 void
zfs_refcount_transfer_ownership_many(zfs_refcount_t * rc,uint64_t number,const void * current_holder,const void * new_holder)267 zfs_refcount_transfer_ownership_many(zfs_refcount_t *rc, uint64_t number,
268 const void *current_holder, const void *new_holder)
269 {
270 reference_t *ref, s;
271
272 if (likely(!rc->rc_tracked))
273 return;
274
275 s.ref_holder = current_holder;
276 s.ref_number = number;
277 s.ref_search = B_TRUE;
278 mutex_enter(&rc->rc_mtx);
279 ref = avl_find(&rc->rc_tree, &s, NULL);
280 ASSERT(ref);
281 ref->ref_holder = new_holder;
282 avl_update(&rc->rc_tree, ref);
283 mutex_exit(&rc->rc_mtx);
284 }
285
286 void
zfs_refcount_transfer_ownership(zfs_refcount_t * rc,const void * current_holder,const void * new_holder)287 zfs_refcount_transfer_ownership(zfs_refcount_t *rc, const void *current_holder,
288 const void *new_holder)
289 {
290 return (zfs_refcount_transfer_ownership_many(rc, 1, current_holder,
291 new_holder));
292 }
293
294 /*
295 * If tracking is enabled, return true if a reference exists that matches
296 * the "holder" tag. If tracking is disabled, then return true if a reference
297 * might be held.
298 */
299 boolean_t
zfs_refcount_held(zfs_refcount_t * rc,const void * holder)300 zfs_refcount_held(zfs_refcount_t *rc, const void *holder)
301 {
302 reference_t *ref, s;
303 avl_index_t idx;
304 boolean_t res;
305
306 if (likely(!rc->rc_tracked))
307 return (zfs_refcount_count(rc) > 0);
308
309 s.ref_holder = holder;
310 s.ref_number = 0;
311 s.ref_search = B_TRUE;
312 mutex_enter(&rc->rc_mtx);
313 ref = avl_find(&rc->rc_tree, &s, &idx);
314 if (likely(ref == NULL))
315 ref = avl_nearest(&rc->rc_tree, idx, AVL_AFTER);
316 res = ref && ref->ref_holder == holder;
317 mutex_exit(&rc->rc_mtx);
318 return (res);
319 }
320
321 /*
322 * If tracking is enabled, return true if a reference does not exist that
323 * matches the "holder" tag. If tracking is disabled, always return true
324 * since the reference might not be held.
325 */
326 boolean_t
zfs_refcount_not_held(zfs_refcount_t * rc,const void * holder)327 zfs_refcount_not_held(zfs_refcount_t *rc, const void *holder)
328 {
329 reference_t *ref, s;
330 avl_index_t idx;
331 boolean_t res;
332
333 if (likely(!rc->rc_tracked))
334 return (B_TRUE);
335
336 mutex_enter(&rc->rc_mtx);
337 s.ref_holder = holder;
338 s.ref_number = 0;
339 s.ref_search = B_TRUE;
340 ref = avl_find(&rc->rc_tree, &s, &idx);
341 if (likely(ref == NULL))
342 ref = avl_nearest(&rc->rc_tree, idx, AVL_AFTER);
343 res = ref == NULL || ref->ref_holder != holder;
344 mutex_exit(&rc->rc_mtx);
345 return (res);
346 }
347
348 EXPORT_SYMBOL(zfs_refcount_create);
349 EXPORT_SYMBOL(zfs_refcount_destroy);
350 EXPORT_SYMBOL(zfs_refcount_is_zero);
351 EXPORT_SYMBOL(zfs_refcount_count);
352 EXPORT_SYMBOL(zfs_refcount_add);
353 EXPORT_SYMBOL(zfs_refcount_remove);
354 EXPORT_SYMBOL(zfs_refcount_held);
355
356 ZFS_MODULE_PARAM(zfs, , reference_tracking_enable, INT, ZMOD_RW,
357 "Track reference holders to refcount_t objects");
358
359 ZFS_MODULE_PARAM(zfs, , reference_history, UINT, ZMOD_RW,
360 "Maximum reference holders being tracked");
361 #endif /* ZFS_DEBUG */
362