]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/cddl/contrib/opensolaris/uts/common/fs/zfs/refcount.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25
26 #pragma ident   "%Z%%M% %I%     %E% SMI"
27
28 #include <sys/zfs_context.h>
29 #include <sys/refcount.h>
30
31 #if defined(DEBUG) || !defined(_KERNEL)
32
33 #ifdef _KERNEL
34 int reference_tracking_enable = FALSE; /* runs out of memory too easily */
35 #else
36 int reference_tracking_enable = TRUE;
37 #endif
38 int reference_history = 4; /* tunable */
39
40 static kmem_cache_t *reference_cache;
41 static kmem_cache_t *reference_history_cache;
42
43 void
44 refcount_sysinit(void)
45 {
46         reference_cache = kmem_cache_create("reference_cache",
47             sizeof (reference_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
48
49         reference_history_cache = kmem_cache_create("reference_history_cache",
50             sizeof (uint64_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
51 }
52
53 void
54 refcount_fini(void)
55 {
56         kmem_cache_destroy(reference_cache);
57         kmem_cache_destroy(reference_history_cache);
58 }
59
60 void
61 refcount_create(refcount_t *rc)
62 {
63         mutex_init(&rc->rc_mtx, NULL, MUTEX_DEFAULT, NULL);
64         list_create(&rc->rc_list, sizeof (reference_t),
65             offsetof(reference_t, ref_link));
66         list_create(&rc->rc_removed, sizeof (reference_t),
67             offsetof(reference_t, ref_link));
68         rc->rc_count = 0;
69         rc->rc_removed_count = 0;
70 }
71
72 void
73 refcount_destroy_many(refcount_t *rc, uint64_t number)
74 {
75         reference_t *ref;
76
77         ASSERT(rc->rc_count == number);
78         while (ref = list_head(&rc->rc_list)) {
79                 list_remove(&rc->rc_list, ref);
80                 kmem_cache_free(reference_cache, ref);
81         }
82         list_destroy(&rc->rc_list);
83
84         while (ref = list_head(&rc->rc_removed)) {
85                 list_remove(&rc->rc_removed, ref);
86                 kmem_cache_free(reference_history_cache, ref->ref_removed);
87                 kmem_cache_free(reference_cache, ref);
88         }
89         list_destroy(&rc->rc_removed);
90         mutex_destroy(&rc->rc_mtx);
91 }
92
93 void
94 refcount_destroy(refcount_t *rc)
95 {
96         refcount_destroy_many(rc, 0);
97 }
98
99 int
100 refcount_is_zero(refcount_t *rc)
101 {
102         ASSERT(rc->rc_count >= 0);
103         return (rc->rc_count == 0);
104 }
105
106 int64_t
107 refcount_count(refcount_t *rc)
108 {
109         ASSERT(rc->rc_count >= 0);
110         return (rc->rc_count);
111 }
112
113 int64_t
114 refcount_add_many(refcount_t *rc, uint64_t number, void *holder)
115 {
116         reference_t *ref;
117         int64_t count;
118
119         if (reference_tracking_enable) {
120                 ref = kmem_cache_alloc(reference_cache, KM_SLEEP);
121                 ref->ref_holder = holder;
122                 ref->ref_number = number;
123         }
124         mutex_enter(&rc->rc_mtx);
125         ASSERT(rc->rc_count >= 0);
126         if (reference_tracking_enable)
127                 list_insert_head(&rc->rc_list, ref);
128         rc->rc_count += number;
129         count = rc->rc_count;
130         mutex_exit(&rc->rc_mtx);
131
132         return (count);
133 }
134
135 int64_t
136 refcount_add(refcount_t *rc, void *holder)
137 {
138         return (refcount_add_many(rc, 1, holder));
139 }
140
141 int64_t
142 refcount_remove_many(refcount_t *rc, uint64_t number, void *holder)
143 {
144         reference_t *ref;
145         int64_t count;
146
147         mutex_enter(&rc->rc_mtx);
148         ASSERT(rc->rc_count >= number);
149
150         if (!reference_tracking_enable) {
151                 rc->rc_count -= number;
152                 count = rc->rc_count;
153                 mutex_exit(&rc->rc_mtx);
154                 return (count);
155         }
156
157         for (ref = list_head(&rc->rc_list); ref;
158             ref = list_next(&rc->rc_list, ref)) {
159                 if (ref->ref_holder == holder && ref->ref_number == number) {
160                         list_remove(&rc->rc_list, ref);
161                         if (reference_history > 0) {
162                                 ref->ref_removed =
163                                     kmem_cache_alloc(reference_history_cache,
164                                     KM_SLEEP);
165                                 list_insert_head(&rc->rc_removed, ref);
166                                 rc->rc_removed_count++;
167                                 if (rc->rc_removed_count >= reference_history) {
168                                         ref = list_tail(&rc->rc_removed);
169                                         list_remove(&rc->rc_removed, ref);
170                                         kmem_cache_free(reference_history_cache,
171                                             ref->ref_removed);
172                                         kmem_cache_free(reference_cache, ref);
173                                         rc->rc_removed_count--;
174                                 }
175                         } else {
176                                 kmem_cache_free(reference_cache, ref);
177                         }
178                         rc->rc_count -= number;
179                         count = rc->rc_count;
180                         mutex_exit(&rc->rc_mtx);
181                         return (count);
182                 }
183         }
184         panic("No such hold %p on refcount %llx", holder,
185             (u_longlong_t)(uintptr_t)rc);
186         return (-1);
187 }
188
189 int64_t
190 refcount_remove(refcount_t *rc, void *holder)
191 {
192         return (refcount_remove_many(rc, 1, holder));
193 }
194
195 #endif