]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - module/zfs/refcount.c
Implement Redacted Send/Receive
[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 zfs_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 zfs_refcount_fini(void)
52 {
53         kmem_cache_destroy(reference_cache);
54         kmem_cache_destroy(reference_history_cache);
55 }
56
57 void
58 zfs_refcount_create(zfs_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 zfs_refcount_create_tracked(zfs_refcount_t *rc)
72 {
73         zfs_refcount_create(rc);
74         rc->rc_tracked = B_TRUE;
75 }
76
77 void
78 zfs_refcount_create_untracked(zfs_refcount_t *rc)
79 {
80         zfs_refcount_create(rc);
81         rc->rc_tracked = B_FALSE;
82 }
83
84 void
85 zfs_refcount_destroy_many(zfs_refcount_t *rc, uint64_t number)
86 {
87         reference_t *ref;
88
89         ASSERT3U(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 zfs_refcount_destroy(zfs_refcount_t *rc)
107 {
108         zfs_refcount_destroy_many(rc, 0);
109 }
110
111 int
112 zfs_refcount_is_zero(zfs_refcount_t *rc)
113 {
114         return (rc->rc_count == 0);
115 }
116
117 int64_t
118 zfs_refcount_count(zfs_refcount_t *rc)
119 {
120         return (rc->rc_count);
121 }
122
123 int64_t
124 zfs_refcount_add_many(zfs_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         ASSERT3U(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(zfs_refcount_t *rc, void *holder)
147 {
148         return (zfs_refcount_add_many(rc, 1, holder));
149 }
150
151 int64_t
152 zfs_refcount_remove_many(zfs_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         ASSERT3U(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 zfs_refcount_remove(zfs_refcount_t *rc, void *holder)
201 {
202         return (zfs_refcount_remove_many(rc, 1, holder));
203 }
204
205 void
206 zfs_refcount_transfer(zfs_refcount_t *dst, zfs_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 zfs_refcount_transfer_ownership_many(zfs_refcount_t *rc, uint64_t number,
238     void *current_holder, 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_number == number) {
253                         ref->ref_holder = new_holder;
254                         found = B_TRUE;
255                         break;
256                 }
257         }
258         ASSERT(found);
259         mutex_exit(&rc->rc_mtx);
260 }
261
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
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
276 zfs_refcount_held(zfs_refcount_t *rc, void *holder)
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
304 zfs_refcount_not_held(zfs_refcount_t *rc, void *holder)
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 }
325 #endif  /* ZFS_DEBUG */