1fa9e4066Sahrens /* 2fa9e4066Sahrens * CDDL HEADER START 3fa9e4066Sahrens * 4fa9e4066Sahrens * The contents of this file are subject to the terms of the 591ebeef5Sahrens * Common Development and Distribution License (the "License"). 691ebeef5Sahrens * You may not use this file except in compliance with the License. 7fa9e4066Sahrens * 8fa9e4066Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9fa9e4066Sahrens * or http://www.opensolaris.org/os/licensing. 10fa9e4066Sahrens * See the License for the specific language governing permissions 11fa9e4066Sahrens * and limitations under the License. 12fa9e4066Sahrens * 13fa9e4066Sahrens * When distributing Covered Code, include this CDDL HEADER in each 14fa9e4066Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15fa9e4066Sahrens * If applicable, add the following below this CDDL HEADER, with the 16fa9e4066Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 17fa9e4066Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 18fa9e4066Sahrens * 19fa9e4066Sahrens * CDDL HEADER END 20fa9e4066Sahrens */ 21fa9e4066Sahrens /* 223f9d6ad7SLin Ling * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 23*3b2aab18SMatthew Ahrens * Copyright (c) 2012 by Delphix. All rights reserved. 24fa9e4066Sahrens */ 25fa9e4066Sahrens 26fa9e4066Sahrens #include <sys/zfs_context.h> 27fa9e4066Sahrens #include <sys/refcount.h> 28fa9e4066Sahrens 29744947dcSTom Erickson #ifdef ZFS_DEBUG 30fa9e4066Sahrens 31fa9e4066Sahrens #ifdef _KERNEL 32fa9e4066Sahrens int reference_tracking_enable = FALSE; /* runs out of memory too easily */ 33fa9e4066Sahrens #else 34fa9e4066Sahrens int reference_tracking_enable = TRUE; 35fa9e4066Sahrens #endif 36*3b2aab18SMatthew Ahrens int reference_history = 3; /* tunable */ 37fa9e4066Sahrens 38fa9e4066Sahrens static kmem_cache_t *reference_cache; 39fa9e4066Sahrens static kmem_cache_t *reference_history_cache; 40fa9e4066Sahrens 41fa9e4066Sahrens void 42fa9e4066Sahrens refcount_init(void) 43fa9e4066Sahrens { 44fa9e4066Sahrens reference_cache = kmem_cache_create("reference_cache", 45fa9e4066Sahrens sizeof (reference_t), 0, NULL, NULL, NULL, NULL, NULL, 0); 46fa9e4066Sahrens 47fa9e4066Sahrens reference_history_cache = kmem_cache_create("reference_history_cache", 48fa9e4066Sahrens sizeof (uint64_t), 0, NULL, NULL, NULL, NULL, NULL, 0); 49fa9e4066Sahrens } 50fa9e4066Sahrens 51fa9e4066Sahrens void 52fa9e4066Sahrens refcount_fini(void) 53fa9e4066Sahrens { 54fa9e4066Sahrens kmem_cache_destroy(reference_cache); 55fa9e4066Sahrens kmem_cache_destroy(reference_history_cache); 56fa9e4066Sahrens } 57fa9e4066Sahrens 58fa9e4066Sahrens void 59fa9e4066Sahrens refcount_create(refcount_t *rc) 60fa9e4066Sahrens { 6191ebeef5Sahrens mutex_init(&rc->rc_mtx, NULL, MUTEX_DEFAULT, NULL); 62fa9e4066Sahrens list_create(&rc->rc_list, sizeof (reference_t), 63fa9e4066Sahrens offsetof(reference_t, ref_link)); 64fa9e4066Sahrens list_create(&rc->rc_removed, sizeof (reference_t), 65fa9e4066Sahrens offsetof(reference_t, ref_link)); 6691ebeef5Sahrens rc->rc_count = 0; 6791ebeef5Sahrens rc->rc_removed_count = 0; 68*3b2aab18SMatthew Ahrens rc->rc_tracked = reference_tracking_enable; 69*3b2aab18SMatthew Ahrens } 70*3b2aab18SMatthew Ahrens 71*3b2aab18SMatthew Ahrens void 72*3b2aab18SMatthew Ahrens refcount_create_untracked(refcount_t *rc) 73*3b2aab18SMatthew Ahrens { 74*3b2aab18SMatthew Ahrens refcount_create(rc); 75*3b2aab18SMatthew Ahrens rc->rc_tracked = B_FALSE; 76fa9e4066Sahrens } 77fa9e4066Sahrens 78fa9e4066Sahrens void 79fa9e4066Sahrens refcount_destroy_many(refcount_t *rc, uint64_t number) 80fa9e4066Sahrens { 81fa9e4066Sahrens reference_t *ref; 82fa9e4066Sahrens 83fa9e4066Sahrens ASSERT(rc->rc_count == number); 84fa9e4066Sahrens while (ref = list_head(&rc->rc_list)) { 85fa9e4066Sahrens list_remove(&rc->rc_list, ref); 86fa9e4066Sahrens kmem_cache_free(reference_cache, ref); 87fa9e4066Sahrens } 88fa9e4066Sahrens list_destroy(&rc->rc_list); 89fa9e4066Sahrens 90fa9e4066Sahrens while (ref = list_head(&rc->rc_removed)) { 91fa9e4066Sahrens list_remove(&rc->rc_removed, ref); 92fa9e4066Sahrens kmem_cache_free(reference_history_cache, ref->ref_removed); 93fa9e4066Sahrens kmem_cache_free(reference_cache, ref); 94fa9e4066Sahrens } 95fa9e4066Sahrens list_destroy(&rc->rc_removed); 96fa9e4066Sahrens mutex_destroy(&rc->rc_mtx); 97fa9e4066Sahrens } 98fa9e4066Sahrens 99fa9e4066Sahrens void 100fa9e4066Sahrens refcount_destroy(refcount_t *rc) 101fa9e4066Sahrens { 102fa9e4066Sahrens refcount_destroy_many(rc, 0); 103fa9e4066Sahrens } 104fa9e4066Sahrens 105fa9e4066Sahrens int 106fa9e4066Sahrens refcount_is_zero(refcount_t *rc) 107fa9e4066Sahrens { 108fa9e4066Sahrens return (rc->rc_count == 0); 109fa9e4066Sahrens } 110fa9e4066Sahrens 111fa9e4066Sahrens int64_t 112fa9e4066Sahrens refcount_count(refcount_t *rc) 113fa9e4066Sahrens { 114fa9e4066Sahrens return (rc->rc_count); 115fa9e4066Sahrens } 116fa9e4066Sahrens 117fa9e4066Sahrens int64_t 118fa9e4066Sahrens refcount_add_many(refcount_t *rc, uint64_t number, void *holder) 119fa9e4066Sahrens { 120d5285caeSGeorge Wilson reference_t *ref = NULL; 121fa9e4066Sahrens int64_t count; 122fa9e4066Sahrens 123*3b2aab18SMatthew Ahrens if (rc->rc_tracked) { 124fa9e4066Sahrens ref = kmem_cache_alloc(reference_cache, KM_SLEEP); 125fa9e4066Sahrens ref->ref_holder = holder; 126fa9e4066Sahrens ref->ref_number = number; 127fa9e4066Sahrens } 128fa9e4066Sahrens mutex_enter(&rc->rc_mtx); 129fa9e4066Sahrens ASSERT(rc->rc_count >= 0); 130*3b2aab18SMatthew Ahrens if (rc->rc_tracked) 131fa9e4066Sahrens list_insert_head(&rc->rc_list, ref); 132fa9e4066Sahrens rc->rc_count += number; 133fa9e4066Sahrens count = rc->rc_count; 134fa9e4066Sahrens mutex_exit(&rc->rc_mtx); 135fa9e4066Sahrens 136fa9e4066Sahrens return (count); 137fa9e4066Sahrens } 138fa9e4066Sahrens 139fa9e4066Sahrens int64_t 140fa9e4066Sahrens refcount_add(refcount_t *rc, void *holder) 141fa9e4066Sahrens { 142fa9e4066Sahrens return (refcount_add_many(rc, 1, holder)); 143fa9e4066Sahrens } 144fa9e4066Sahrens 145fa9e4066Sahrens int64_t 146fa9e4066Sahrens refcount_remove_many(refcount_t *rc, uint64_t number, void *holder) 147fa9e4066Sahrens { 148fa9e4066Sahrens reference_t *ref; 149fa9e4066Sahrens int64_t count; 150fa9e4066Sahrens 151fa9e4066Sahrens mutex_enter(&rc->rc_mtx); 152fa9e4066Sahrens ASSERT(rc->rc_count >= number); 153fa9e4066Sahrens 154*3b2aab18SMatthew Ahrens if (!rc->rc_tracked) { 155fa9e4066Sahrens rc->rc_count -= number; 156fa9e4066Sahrens count = rc->rc_count; 157fa9e4066Sahrens mutex_exit(&rc->rc_mtx); 158fa9e4066Sahrens return (count); 159fa9e4066Sahrens } 160fa9e4066Sahrens 161fa9e4066Sahrens for (ref = list_head(&rc->rc_list); ref; 162fa9e4066Sahrens ref = list_next(&rc->rc_list, ref)) { 163fa9e4066Sahrens if (ref->ref_holder == holder && ref->ref_number == number) { 164fa9e4066Sahrens list_remove(&rc->rc_list, ref); 165fa9e4066Sahrens if (reference_history > 0) { 166fa9e4066Sahrens ref->ref_removed = 167fa9e4066Sahrens kmem_cache_alloc(reference_history_cache, 168fa9e4066Sahrens KM_SLEEP); 169fa9e4066Sahrens list_insert_head(&rc->rc_removed, ref); 170fa9e4066Sahrens rc->rc_removed_count++; 171*3b2aab18SMatthew Ahrens if (rc->rc_removed_count > reference_history) { 172fa9e4066Sahrens ref = list_tail(&rc->rc_removed); 173fa9e4066Sahrens list_remove(&rc->rc_removed, ref); 174fa9e4066Sahrens kmem_cache_free(reference_history_cache, 175fa9e4066Sahrens ref->ref_removed); 176fa9e4066Sahrens kmem_cache_free(reference_cache, ref); 177fa9e4066Sahrens rc->rc_removed_count--; 178fa9e4066Sahrens } 179fa9e4066Sahrens } else { 180fa9e4066Sahrens kmem_cache_free(reference_cache, ref); 181fa9e4066Sahrens } 182fa9e4066Sahrens rc->rc_count -= number; 183fa9e4066Sahrens count = rc->rc_count; 184fa9e4066Sahrens mutex_exit(&rc->rc_mtx); 185fa9e4066Sahrens return (count); 186fa9e4066Sahrens } 187fa9e4066Sahrens } 188fa9e4066Sahrens panic("No such hold %p on refcount %llx", holder, 189fa9e4066Sahrens (u_longlong_t)(uintptr_t)rc); 190fa9e4066Sahrens return (-1); 191fa9e4066Sahrens } 192fa9e4066Sahrens 193fa9e4066Sahrens int64_t 194fa9e4066Sahrens refcount_remove(refcount_t *rc, void *holder) 195fa9e4066Sahrens { 196fa9e4066Sahrens return (refcount_remove_many(rc, 1, holder)); 197fa9e4066Sahrens } 198fa9e4066Sahrens 199744947dcSTom Erickson void 200744947dcSTom Erickson refcount_transfer(refcount_t *dst, refcount_t *src) 201744947dcSTom Erickson { 202744947dcSTom Erickson int64_t count, removed_count; 203744947dcSTom Erickson list_t list, removed; 204744947dcSTom Erickson 205744947dcSTom Erickson list_create(&list, sizeof (reference_t), 206744947dcSTom Erickson offsetof(reference_t, ref_link)); 207744947dcSTom Erickson list_create(&removed, sizeof (reference_t), 208744947dcSTom Erickson offsetof(reference_t, ref_link)); 209744947dcSTom Erickson 210744947dcSTom Erickson mutex_enter(&src->rc_mtx); 211744947dcSTom Erickson count = src->rc_count; 212744947dcSTom Erickson removed_count = src->rc_removed_count; 213744947dcSTom Erickson src->rc_count = 0; 214744947dcSTom Erickson src->rc_removed_count = 0; 215744947dcSTom Erickson list_move_tail(&list, &src->rc_list); 216744947dcSTom Erickson list_move_tail(&removed, &src->rc_removed); 217744947dcSTom Erickson mutex_exit(&src->rc_mtx); 218744947dcSTom Erickson 219744947dcSTom Erickson mutex_enter(&dst->rc_mtx); 220744947dcSTom Erickson dst->rc_count += count; 221744947dcSTom Erickson dst->rc_removed_count += removed_count; 222744947dcSTom Erickson list_move_tail(&dst->rc_list, &list); 223744947dcSTom Erickson list_move_tail(&dst->rc_removed, &removed); 224744947dcSTom Erickson mutex_exit(&dst->rc_mtx); 225744947dcSTom Erickson 226744947dcSTom Erickson list_destroy(&list); 227744947dcSTom Erickson list_destroy(&removed); 228744947dcSTom Erickson } 229744947dcSTom Erickson 230744947dcSTom Erickson #endif /* ZFS_DEBUG */ 231