xref: /illumos-gate/usr/src/uts/common/fs/zfs/refcount.c (revision 91ebeef555ce7f899b6270a3c2df47b51f7ad59a)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/zfs_context.h>
29 #include <sys/refcount.h>
30 
31 #if defined(DEBUG) || !defined(_KERNEL)
32 
33 #ifdef _KERNEL
34 int reference_tracking_enable = FALSE; /* runs out of memory too easily */
35 #else
36 int reference_tracking_enable = TRUE;
37 #endif
38 int reference_history = 4; /* tunable */
39 
40 static kmem_cache_t *reference_cache;
41 static kmem_cache_t *reference_history_cache;
42 
43 void
44 refcount_init(void)
45 {
46 	reference_cache = kmem_cache_create("reference_cache",
47 	    sizeof (reference_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
48 
49 	reference_history_cache = kmem_cache_create("reference_history_cache",
50 	    sizeof (uint64_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
51 }
52 
53 void
54 refcount_fini(void)
55 {
56 	kmem_cache_destroy(reference_cache);
57 	kmem_cache_destroy(reference_history_cache);
58 }
59 
60 void
61 refcount_create(refcount_t *rc)
62 {
63 	mutex_init(&rc->rc_mtx, NULL, MUTEX_DEFAULT, NULL);
64 	list_create(&rc->rc_list, sizeof (reference_t),
65 	    offsetof(reference_t, ref_link));
66 	list_create(&rc->rc_removed, sizeof (reference_t),
67 	    offsetof(reference_t, ref_link));
68 	rc->rc_count = 0;
69 	rc->rc_removed_count = 0;
70 }
71 
72 void
73 refcount_destroy_many(refcount_t *rc, uint64_t number)
74 {
75 	reference_t *ref;
76 
77 	ASSERT(rc->rc_count == number);
78 	while (ref = list_head(&rc->rc_list)) {
79 		list_remove(&rc->rc_list, ref);
80 		kmem_cache_free(reference_cache, ref);
81 	}
82 	list_destroy(&rc->rc_list);
83 
84 	while (ref = list_head(&rc->rc_removed)) {
85 		list_remove(&rc->rc_removed, ref);
86 		kmem_cache_free(reference_history_cache, ref->ref_removed);
87 		kmem_cache_free(reference_cache, ref);
88 	}
89 	list_destroy(&rc->rc_removed);
90 	mutex_destroy(&rc->rc_mtx);
91 }
92 
93 void
94 refcount_destroy(refcount_t *rc)
95 {
96 	refcount_destroy_many(rc, 0);
97 }
98 
99 int
100 refcount_is_zero(refcount_t *rc)
101 {
102 	ASSERT(rc->rc_count >= 0);
103 	return (rc->rc_count == 0);
104 }
105 
106 int64_t
107 refcount_count(refcount_t *rc)
108 {
109 	ASSERT(rc->rc_count >= 0);
110 	return (rc->rc_count);
111 }
112 
113 int64_t
114 refcount_add_many(refcount_t *rc, uint64_t number, void *holder)
115 {
116 	reference_t *ref;
117 	int64_t count;
118 
119 	if (reference_tracking_enable) {
120 		ref = kmem_cache_alloc(reference_cache, KM_SLEEP);
121 		ref->ref_holder = holder;
122 		ref->ref_number = number;
123 	}
124 	mutex_enter(&rc->rc_mtx);
125 	ASSERT(rc->rc_count >= 0);
126 	if (reference_tracking_enable)
127 		list_insert_head(&rc->rc_list, ref);
128 	rc->rc_count += number;
129 	count = rc->rc_count;
130 	mutex_exit(&rc->rc_mtx);
131 
132 	return (count);
133 }
134 
135 int64_t
136 refcount_add(refcount_t *rc, void *holder)
137 {
138 	return (refcount_add_many(rc, 1, holder));
139 }
140 
141 int64_t
142 refcount_remove_many(refcount_t *rc, uint64_t number, void *holder)
143 {
144 	reference_t *ref;
145 	int64_t count;
146 
147 	mutex_enter(&rc->rc_mtx);
148 	ASSERT(rc->rc_count >= number);
149 
150 	if (!reference_tracking_enable) {
151 		rc->rc_count -= number;
152 		count = rc->rc_count;
153 		mutex_exit(&rc->rc_mtx);
154 		return (count);
155 	}
156 
157 	for (ref = list_head(&rc->rc_list); ref;
158 	    ref = list_next(&rc->rc_list, ref)) {
159 		if (ref->ref_holder == holder && ref->ref_number == number) {
160 			list_remove(&rc->rc_list, ref);
161 			if (reference_history > 0) {
162 				ref->ref_removed =
163 				    kmem_cache_alloc(reference_history_cache,
164 				    KM_SLEEP);
165 				list_insert_head(&rc->rc_removed, ref);
166 				rc->rc_removed_count++;
167 				if (rc->rc_removed_count >= reference_history) {
168 					ref = list_tail(&rc->rc_removed);
169 					list_remove(&rc->rc_removed, ref);
170 					kmem_cache_free(reference_history_cache,
171 					    ref->ref_removed);
172 					kmem_cache_free(reference_cache, ref);
173 					rc->rc_removed_count--;
174 				}
175 			} else {
176 				kmem_cache_free(reference_cache, ref);
177 			}
178 			rc->rc_count -= number;
179 			count = rc->rc_count;
180 			mutex_exit(&rc->rc_mtx);
181 			return (count);
182 		}
183 	}
184 	panic("No such hold %p on refcount %llx", holder,
185 	    (u_longlong_t)(uintptr_t)rc);
186 	return (-1);
187 }
188 
189 int64_t
190 refcount_remove(refcount_t *rc, void *holder)
191 {
192 	return (refcount_remove_many(rc, 1, holder));
193 }
194 
195 #endif
196