]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - module/zfs/refcount.c
dsl_scan_scrub_cb: don't double-account non-embedded blocks
[FreeBSD/FreeBSD.git] / module / zfs / refcount.c
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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright (c) 2012, 2015 by Delphix. All rights reserved.
24  */
25
26 #include <sys/zfs_context.h>
27 #include <sys/refcount.h>
28
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
34 int reference_history = 3; /* tunable */
35
36 #ifdef  ZFS_DEBUG
37 static kmem_cache_t *reference_cache;
38 static kmem_cache_t *reference_history_cache;
39
40 void
41 refcount_init(void)
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
51 refcount_fini(void)
52 {
53         kmem_cache_destroy(reference_cache);
54         kmem_cache_destroy(reference_history_cache);
55 }
56
57 void
58 refcount_create(refcount_t *rc)
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;
67         rc->rc_tracked = reference_tracking_enable;
68 }
69
70 void
71 refcount_create_tracked(refcount_t *rc)
72 {
73         refcount_create(rc);
74         rc->rc_tracked = B_TRUE;
75 }
76
77 void
78 refcount_create_untracked(refcount_t *rc)
79 {
80         refcount_create(rc);
81         rc->rc_tracked = B_FALSE;
82 }
83
84 void
85 refcount_destroy_many(refcount_t *rc, uint64_t number)
86 {
87         reference_t *ref;
88
89         ASSERT(rc->rc_count == number);
90         while ((ref = list_head(&rc->rc_list))) {
91                 list_remove(&rc->rc_list, ref);
92                 kmem_cache_free(reference_cache, ref);
93         }
94         list_destroy(&rc->rc_list);
95
96         while ((ref = list_head(&rc->rc_removed))) {
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
106 refcount_destroy(refcount_t *rc)
107 {
108         refcount_destroy_many(rc, 0);
109 }
110
111 int
112 refcount_is_zero(refcount_t *rc)
113 {
114         return (rc->rc_count == 0);
115 }
116
117 int64_t
118 refcount_count(refcount_t *rc)
119 {
120         return (rc->rc_count);
121 }
122
123 int64_t
124 refcount_add_many(refcount_t *rc, uint64_t number, void *holder)
125 {
126         reference_t *ref = NULL;
127         int64_t count;
128
129         if (rc->rc_tracked) {
130                 ref = kmem_cache_alloc(reference_cache, KM_SLEEP);
131                 ref->ref_holder = holder;
132                 ref->ref_number = number;
133         }
134         mutex_enter(&rc->rc_mtx);
135         ASSERT(rc->rc_count >= 0);
136         if (rc->rc_tracked)
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
146 zfs_refcount_add(refcount_t *rc, void *holder)
147 {
148         return (refcount_add_many(rc, 1, holder));
149 }
150
151 int64_t
152 refcount_remove_many(refcount_t *rc, uint64_t number, void *holder)
153 {
154         reference_t *ref;
155         int64_t count;
156
157         mutex_enter(&rc->rc_mtx);
158         ASSERT(rc->rc_count >= number);
159
160         if (!rc->rc_tracked) {
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,
174                                     KM_SLEEP);
175                                 list_insert_head(&rc->rc_removed, ref);
176                                 rc->rc_removed_count++;
177                                 if (rc->rc_removed_count > reference_history) {
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
200 refcount_remove(refcount_t *rc, void *holder)
201 {
202         return (refcount_remove_many(rc, 1, holder));
203 }
204
205 void
206 refcount_transfer(refcount_t *dst, refcount_t *src)
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
236 void
237 refcount_transfer_ownership(refcount_t *rc, void *current_holder,
238     void *new_holder)
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)) {
251                 if (ref->ref_holder == current_holder) {
252                         ref->ref_holder = new_holder;
253                         found = B_TRUE;
254                         break;
255                 }
256         }
257         ASSERT(found);
258         mutex_exit(&rc->rc_mtx);
259 }
260
261 /*
262  * If tracking is enabled, return true if a reference exists that matches
263  * the "holder" tag. If tracking is disabled, then return true if a reference
264  * might be held.
265  */
266 boolean_t
267 refcount_held(refcount_t *rc, void *holder)
268 {
269         reference_t *ref;
270
271         mutex_enter(&rc->rc_mtx);
272
273         if (!rc->rc_tracked) {
274                 mutex_exit(&rc->rc_mtx);
275                 return (rc->rc_count > 0);
276         }
277
278         for (ref = list_head(&rc->rc_list); ref;
279             ref = list_next(&rc->rc_list, ref)) {
280                 if (ref->ref_holder == holder) {
281                         mutex_exit(&rc->rc_mtx);
282                         return (B_TRUE);
283                 }
284         }
285         mutex_exit(&rc->rc_mtx);
286         return (B_FALSE);
287 }
288
289 /*
290  * If tracking is enabled, return true if a reference does not exist that
291  * matches the "holder" tag. If tracking is disabled, always return true
292  * since the reference might not be held.
293  */
294 boolean_t
295 refcount_not_held(refcount_t *rc, void *holder)
296 {
297         reference_t *ref;
298
299         mutex_enter(&rc->rc_mtx);
300
301         if (!rc->rc_tracked) {
302                 mutex_exit(&rc->rc_mtx);
303                 return (B_TRUE);
304         }
305
306         for (ref = list_head(&rc->rc_list); ref;
307             ref = list_next(&rc->rc_list, ref)) {
308                 if (ref->ref_holder == holder) {
309                         mutex_exit(&rc->rc_mtx);
310                         return (B_FALSE);
311                 }
312         }
313         mutex_exit(&rc->rc_mtx);
314         return (B_TRUE);
315 }
316 #endif  /* ZFS_DEBUG */