FreeBSD ZFS
The Zettabyte File System
|
00001 /* 00002 * CDDL HEADER START 00003 * 00004 * The contents of this file are subject to the terms of the 00005 * Common Development and Distribution License (the "License"). 00006 * You may not use this file except in compliance with the License. 00007 * 00008 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 00009 * or http://www.opensolaris.org/os/licensing. 00010 * See the License for the specific language governing permissions 00011 * and limitations under the License. 00012 * 00013 * When distributing Covered Code, include this CDDL HEADER in each 00014 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 00015 * If applicable, add the following below this CDDL HEADER, with the 00016 * fields enclosed by brackets "[]" replaced with your own identifying 00017 * information: Portions Copyright [yyyy] [name of copyright owner] 00018 * 00019 * CDDL HEADER END 00020 */ 00021 /* 00022 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 00023 */ 00024 00025 #include <sys/zfs_context.h> 00026 #include <sys/refcount.h> 00027 00028 #ifdef ZFS_DEBUG 00029 00030 #ifdef _KERNEL 00031 int reference_tracking_enable = FALSE; /* runs out of memory too easily */ 00032 #else 00033 int reference_tracking_enable = TRUE; 00034 #endif 00035 int reference_history = 4; /* tunable */ 00036 00037 static kmem_cache_t *reference_cache; 00038 static kmem_cache_t *reference_history_cache; 00039 00040 void 00041 refcount_sysinit(void) 00042 { 00043 reference_cache = kmem_cache_create("reference_cache", 00044 sizeof (reference_t), 0, NULL, NULL, NULL, NULL, NULL, 0); 00045 00046 reference_history_cache = kmem_cache_create("reference_history_cache", 00047 sizeof (uint64_t), 0, NULL, NULL, NULL, NULL, NULL, 0); 00048 } 00049 00050 void 00051 refcount_fini(void) 00052 { 00053 kmem_cache_destroy(reference_cache); 00054 kmem_cache_destroy(reference_history_cache); 00055 } 00056 00058 void 00059 refcount_create(refcount_t *rc) 00060 { 00061 mutex_init(&rc->rc_mtx, NULL, MUTEX_DEFAULT, NULL); 00062 list_create(&rc->rc_list, sizeof (reference_t), 00063 offsetof(reference_t, ref_link)); 00064 list_create(&rc->rc_removed, sizeof (reference_t), 00065 offsetof(reference_t, ref_link)); 00066 rc->rc_count = 0; 00067 rc->rc_removed_count = 0; 00068 } 00069 00070 void 00071 refcount_destroy_many(refcount_t *rc, uint64_t number) 00072 { 00073 reference_t *ref; 00074 00075 ASSERT(rc->rc_count == number); 00076 while (ref = list_head(&rc->rc_list)) { 00077 list_remove(&rc->rc_list, ref); 00078 kmem_cache_free(reference_cache, ref); 00079 } 00080 list_destroy(&rc->rc_list); 00081 00082 while (ref = list_head(&rc->rc_removed)) { 00083 list_remove(&rc->rc_removed, ref); 00084 kmem_cache_free(reference_history_cache, ref->ref_removed); 00085 kmem_cache_free(reference_cache, ref); 00086 } 00087 list_destroy(&rc->rc_removed); 00088 mutex_destroy(&rc->rc_mtx); 00089 } 00090 00091 void 00092 refcount_destroy(refcount_t *rc) 00093 { 00094 refcount_destroy_many(rc, 0); 00095 } 00096 00097 int 00098 refcount_is_zero(refcount_t *rc) 00099 { 00100 ASSERT(rc->rc_count >= 0); 00101 return (rc->rc_count == 0); 00102 } 00103 00104 int64_t 00105 refcount_count(refcount_t *rc) 00106 { 00107 ASSERT(rc->rc_count >= 0); 00108 return (rc->rc_count); 00109 } 00110 00111 int64_t 00112 refcount_add_many(refcount_t *rc, uint64_t number, void *holder) 00113 { 00114 reference_t *ref; 00115 int64_t count; 00116 00117 if (reference_tracking_enable) { 00118 ref = kmem_cache_alloc(reference_cache, KM_SLEEP); 00119 ref->ref_holder = holder; 00120 ref->ref_number = number; 00121 } 00122 mutex_enter(&rc->rc_mtx); 00123 ASSERT(rc->rc_count >= 0); 00124 if (reference_tracking_enable) 00125 list_insert_head(&rc->rc_list, ref); 00126 rc->rc_count += number; 00127 count = rc->rc_count; 00128 mutex_exit(&rc->rc_mtx); 00129 00130 return (count); 00131 } 00132 00133 int64_t 00134 refcount_add(refcount_t *rc, void *holder) 00135 { 00136 return (refcount_add_many(rc, 1, holder)); 00137 } 00138 00139 int64_t 00140 refcount_remove_many(refcount_t *rc, uint64_t number, void *holder) 00141 { 00142 reference_t *ref; 00143 int64_t count; 00144 00145 mutex_enter(&rc->rc_mtx); 00146 ASSERT(rc->rc_count >= number); 00147 00148 if (!reference_tracking_enable) { 00149 rc->rc_count -= number; 00150 count = rc->rc_count; 00151 mutex_exit(&rc->rc_mtx); 00152 return (count); 00153 } 00154 00155 for (ref = list_head(&rc->rc_list); ref; 00156 ref = list_next(&rc->rc_list, ref)) { 00157 if (ref->ref_holder == holder && ref->ref_number == number) { 00158 list_remove(&rc->rc_list, ref); 00159 if (reference_history > 0) { 00160 ref->ref_removed = 00161 kmem_cache_alloc(reference_history_cache, 00162 KM_SLEEP); 00163 list_insert_head(&rc->rc_removed, ref); 00164 rc->rc_removed_count++; 00165 if (rc->rc_removed_count >= reference_history) { 00166 ref = list_tail(&rc->rc_removed); 00167 list_remove(&rc->rc_removed, ref); 00168 kmem_cache_free(reference_history_cache, 00169 ref->ref_removed); 00170 kmem_cache_free(reference_cache, ref); 00171 rc->rc_removed_count--; 00172 } 00173 } else { 00174 kmem_cache_free(reference_cache, ref); 00175 } 00176 rc->rc_count -= number; 00177 count = rc->rc_count; 00178 mutex_exit(&rc->rc_mtx); 00179 return (count); 00180 } 00181 } 00182 panic("No such hold %p on refcount %llx", holder, 00183 (u_longlong_t)(uintptr_t)rc); 00184 return (-1); 00185 } 00186 00187 int64_t 00188 refcount_remove(refcount_t *rc, void *holder) 00189 { 00190 return (refcount_remove_many(rc, 1, holder)); 00191 } 00192 00193 void 00194 refcount_transfer(refcount_t *dst, refcount_t *src) 00195 { 00196 int64_t count, removed_count; 00197 list_t list, removed; 00198 00199 list_create(&list, sizeof (reference_t), 00200 offsetof(reference_t, ref_link)); 00201 list_create(&removed, sizeof (reference_t), 00202 offsetof(reference_t, ref_link)); 00203 00204 mutex_enter(&src->rc_mtx); 00205 count = src->rc_count; 00206 removed_count = src->rc_removed_count; 00207 src->rc_count = 0; 00208 src->rc_removed_count = 0; 00209 list_move_tail(&list, &src->rc_list); 00210 list_move_tail(&removed, &src->rc_removed); 00211 mutex_exit(&src->rc_mtx); 00212 00213 mutex_enter(&dst->rc_mtx); 00214 dst->rc_count += count; 00215 dst->rc_removed_count += removed_count; 00216 list_move_tail(&dst->rc_list, &list); 00217 list_move_tail(&dst->rc_removed, &removed); 00218 mutex_exit(&dst->rc_mtx); 00219 00220 list_destroy(&list); 00221 list_destroy(&removed); 00222 } 00223 00224 #endif /* ZFS_DEBUG */