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