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