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