]>
Commit | Line | Data |
---|---|---|
34dc7c2f BB |
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 | /* | |
428870ff | 22 | * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. |
3dfb57a3 | 23 | * Copyright (c) 2012, 2015 by Delphix. All rights reserved. |
34dc7c2f BB |
24 | */ |
25 | ||
34dc7c2f BB |
26 | #include <sys/zfs_context.h> |
27 | #include <sys/refcount.h> | |
28 | ||
572e2857 | 29 | #ifdef ZFS_DEBUG |
34dc7c2f BB |
30 | |
31 | #ifdef _KERNEL | |
32 | int reference_tracking_enable = FALSE; /* runs out of memory too easily */ | |
33 | #else | |
34 | int reference_tracking_enable = TRUE; | |
35 | #endif | |
13fe0198 | 36 | int reference_history = 3; /* tunable */ |
34dc7c2f BB |
37 | |
38 | static kmem_cache_t *reference_cache; | |
39 | static kmem_cache_t *reference_history_cache; | |
40 | ||
41 | void | |
42 | 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 | reference_history_cache = kmem_cache_create("reference_history_cache", | |
48 | sizeof (uint64_t), 0, NULL, NULL, NULL, NULL, NULL, 0); | |
49 | } | |
50 | ||
51 | void | |
52 | refcount_fini(void) | |
53 | { | |
54 | kmem_cache_destroy(reference_cache); | |
55 | kmem_cache_destroy(reference_history_cache); | |
56 | } | |
57 | ||
58 | void | |
59 | refcount_create(refcount_t *rc) | |
60 | { | |
61 | mutex_init(&rc->rc_mtx, NULL, MUTEX_DEFAULT, NULL); | |
62 | list_create(&rc->rc_list, sizeof (reference_t), | |
63 | offsetof(reference_t, ref_link)); | |
64 | list_create(&rc->rc_removed, sizeof (reference_t), | |
65 | offsetof(reference_t, ref_link)); | |
66 | rc->rc_count = 0; | |
67 | rc->rc_removed_count = 0; | |
13fe0198 MA |
68 | rc->rc_tracked = reference_tracking_enable; |
69 | } | |
70 | ||
3dfb57a3 DB |
71 | void |
72 | refcount_create_tracked(refcount_t *rc) | |
73 | { | |
74 | refcount_create(rc); | |
75 | rc->rc_tracked = B_TRUE; | |
76 | } | |
77 | ||
13fe0198 MA |
78 | void |
79 | refcount_create_untracked(refcount_t *rc) | |
80 | { | |
81 | refcount_create(rc); | |
82 | rc->rc_tracked = B_FALSE; | |
34dc7c2f BB |
83 | } |
84 | ||
85 | void | |
86 | refcount_destroy_many(refcount_t *rc, uint64_t number) | |
87 | { | |
88 | reference_t *ref; | |
89 | ||
90 | ASSERT(rc->rc_count == number); | |
c65aa5b2 | 91 | while ((ref = list_head(&rc->rc_list))) { |
34dc7c2f BB |
92 | list_remove(&rc->rc_list, ref); |
93 | kmem_cache_free(reference_cache, ref); | |
94 | } | |
95 | list_destroy(&rc->rc_list); | |
96 | ||
c65aa5b2 | 97 | while ((ref = list_head(&rc->rc_removed))) { |
34dc7c2f BB |
98 | list_remove(&rc->rc_removed, ref); |
99 | kmem_cache_free(reference_history_cache, ref->ref_removed); | |
100 | kmem_cache_free(reference_cache, ref); | |
101 | } | |
102 | list_destroy(&rc->rc_removed); | |
103 | mutex_destroy(&rc->rc_mtx); | |
104 | } | |
105 | ||
106 | void | |
107 | refcount_destroy(refcount_t *rc) | |
108 | { | |
109 | refcount_destroy_many(rc, 0); | |
110 | } | |
111 | ||
112 | int | |
113 | refcount_is_zero(refcount_t *rc) | |
114 | { | |
34dc7c2f BB |
115 | return (rc->rc_count == 0); |
116 | } | |
117 | ||
118 | int64_t | |
119 | refcount_count(refcount_t *rc) | |
120 | { | |
34dc7c2f BB |
121 | return (rc->rc_count); |
122 | } | |
123 | ||
124 | int64_t | |
125 | refcount_add_many(refcount_t *rc, uint64_t number, void *holder) | |
126 | { | |
d4ed6673 | 127 | reference_t *ref = NULL; |
34dc7c2f BB |
128 | int64_t count; |
129 | ||
13fe0198 | 130 | if (rc->rc_tracked) { |
79c76d5b | 131 | ref = kmem_cache_alloc(reference_cache, KM_SLEEP); |
34dc7c2f BB |
132 | ref->ref_holder = holder; |
133 | ref->ref_number = number; | |
134 | } | |
135 | mutex_enter(&rc->rc_mtx); | |
136 | ASSERT(rc->rc_count >= 0); | |
13fe0198 | 137 | if (rc->rc_tracked) |
34dc7c2f BB |
138 | list_insert_head(&rc->rc_list, ref); |
139 | rc->rc_count += number; | |
140 | count = rc->rc_count; | |
141 | mutex_exit(&rc->rc_mtx); | |
142 | ||
143 | return (count); | |
144 | } | |
145 | ||
146 | int64_t | |
147 | refcount_add(refcount_t *rc, void *holder) | |
148 | { | |
149 | return (refcount_add_many(rc, 1, holder)); | |
150 | } | |
151 | ||
152 | int64_t | |
153 | refcount_remove_many(refcount_t *rc, uint64_t number, void *holder) | |
154 | { | |
155 | reference_t *ref; | |
156 | int64_t count; | |
157 | ||
158 | mutex_enter(&rc->rc_mtx); | |
159 | ASSERT(rc->rc_count >= number); | |
160 | ||
13fe0198 | 161 | if (!rc->rc_tracked) { |
34dc7c2f BB |
162 | rc->rc_count -= number; |
163 | count = rc->rc_count; | |
164 | mutex_exit(&rc->rc_mtx); | |
165 | return (count); | |
166 | } | |
167 | ||
168 | for (ref = list_head(&rc->rc_list); ref; | |
169 | ref = list_next(&rc->rc_list, ref)) { | |
170 | if (ref->ref_holder == holder && ref->ref_number == number) { | |
171 | list_remove(&rc->rc_list, ref); | |
172 | if (reference_history > 0) { | |
173 | ref->ref_removed = | |
174 | kmem_cache_alloc(reference_history_cache, | |
79c76d5b | 175 | KM_SLEEP); |
34dc7c2f BB |
176 | list_insert_head(&rc->rc_removed, ref); |
177 | rc->rc_removed_count++; | |
13fe0198 | 178 | if (rc->rc_removed_count > reference_history) { |
34dc7c2f BB |
179 | ref = list_tail(&rc->rc_removed); |
180 | list_remove(&rc->rc_removed, ref); | |
181 | kmem_cache_free(reference_history_cache, | |
182 | ref->ref_removed); | |
183 | kmem_cache_free(reference_cache, ref); | |
184 | rc->rc_removed_count--; | |
185 | } | |
186 | } else { | |
187 | kmem_cache_free(reference_cache, ref); | |
188 | } | |
189 | rc->rc_count -= number; | |
190 | count = rc->rc_count; | |
191 | mutex_exit(&rc->rc_mtx); | |
192 | return (count); | |
193 | } | |
194 | } | |
195 | panic("No such hold %p on refcount %llx", holder, | |
196 | (u_longlong_t)(uintptr_t)rc); | |
197 | return (-1); | |
198 | } | |
199 | ||
200 | int64_t | |
201 | refcount_remove(refcount_t *rc, void *holder) | |
202 | { | |
203 | return (refcount_remove_many(rc, 1, holder)); | |
204 | } | |
205 | ||
572e2857 BB |
206 | void |
207 | refcount_transfer(refcount_t *dst, refcount_t *src) | |
208 | { | |
209 | int64_t count, removed_count; | |
210 | list_t list, removed; | |
211 | ||
212 | list_create(&list, sizeof (reference_t), | |
213 | offsetof(reference_t, ref_link)); | |
214 | list_create(&removed, sizeof (reference_t), | |
215 | offsetof(reference_t, ref_link)); | |
216 | ||
217 | mutex_enter(&src->rc_mtx); | |
218 | count = src->rc_count; | |
219 | removed_count = src->rc_removed_count; | |
220 | src->rc_count = 0; | |
221 | src->rc_removed_count = 0; | |
222 | list_move_tail(&list, &src->rc_list); | |
223 | list_move_tail(&removed, &src->rc_removed); | |
224 | mutex_exit(&src->rc_mtx); | |
225 | ||
226 | mutex_enter(&dst->rc_mtx); | |
227 | dst->rc_count += count; | |
228 | dst->rc_removed_count += removed_count; | |
229 | list_move_tail(&dst->rc_list, &list); | |
230 | list_move_tail(&dst->rc_removed, &removed); | |
231 | mutex_exit(&dst->rc_mtx); | |
232 | ||
233 | list_destroy(&list); | |
234 | list_destroy(&removed); | |
235 | } | |
236 | ||
d3c2ae1c GW |
237 | void |
238 | refcount_transfer_ownership(refcount_t *rc, void *current_holder, | |
239 | void *new_holder) | |
240 | { | |
241 | reference_t *ref; | |
242 | boolean_t found = B_FALSE; | |
243 | ||
244 | mutex_enter(&rc->rc_mtx); | |
245 | if (!rc->rc_tracked) { | |
246 | mutex_exit(&rc->rc_mtx); | |
247 | return; | |
248 | } | |
249 | ||
250 | for (ref = list_head(&rc->rc_list); ref; | |
251 | ref = list_next(&rc->rc_list, ref)) { | |
252 | if (ref->ref_holder == current_holder) { | |
253 | ref->ref_holder = new_holder; | |
254 | found = B_TRUE; | |
255 | break; | |
256 | } | |
257 | } | |
258 | ASSERT(found); | |
259 | mutex_exit(&rc->rc_mtx); | |
260 | } | |
3dfb57a3 DB |
261 | |
262 | /* | |
263 | * If tracking is enabled, return true if a reference exists that matches | |
264 | * the "holder" tag. If tracking is disabled, then return true if a reference | |
265 | * might be held. | |
266 | */ | |
267 | boolean_t | |
268 | refcount_held(refcount_t *rc, void *holder) | |
269 | { | |
270 | reference_t *ref; | |
271 | ||
272 | mutex_enter(&rc->rc_mtx); | |
273 | ||
274 | if (!rc->rc_tracked) { | |
275 | mutex_exit(&rc->rc_mtx); | |
276 | return (rc->rc_count > 0); | |
277 | } | |
278 | ||
279 | for (ref = list_head(&rc->rc_list); ref; | |
280 | ref = list_next(&rc->rc_list, ref)) { | |
281 | if (ref->ref_holder == holder) { | |
282 | mutex_exit(&rc->rc_mtx); | |
283 | return (B_TRUE); | |
284 | } | |
285 | } | |
286 | mutex_exit(&rc->rc_mtx); | |
287 | return (B_FALSE); | |
288 | } | |
289 | ||
290 | /* | |
291 | * If tracking is enabled, return true if a reference does not exist that | |
292 | * matches the "holder" tag. If tracking is disabled, always return true | |
293 | * since the reference might not be held. | |
294 | */ | |
295 | boolean_t | |
296 | refcount_not_held(refcount_t *rc, void *holder) | |
297 | { | |
298 | reference_t *ref; | |
299 | ||
300 | mutex_enter(&rc->rc_mtx); | |
301 | ||
302 | if (!rc->rc_tracked) { | |
303 | mutex_exit(&rc->rc_mtx); | |
304 | return (B_TRUE); | |
305 | } | |
306 | ||
307 | for (ref = list_head(&rc->rc_list); ref; | |
308 | ref = list_next(&rc->rc_list, ref)) { | |
309 | if (ref->ref_holder == holder) { | |
310 | mutex_exit(&rc->rc_mtx); | |
311 | return (B_FALSE); | |
312 | } | |
313 | } | |
314 | mutex_exit(&rc->rc_mtx); | |
315 | return (B_TRUE); | |
316 | } | |
572e2857 | 317 | #endif /* ZFS_DEBUG */ |