1fa9e4066Sahrens /*
2fa9e4066Sahrens * CDDL HEADER START
3fa9e4066Sahrens *
4fa9e4066Sahrens * The contents of this file are subject to the terms of the
591ebeef5Sahrens * Common Development and Distribution License (the "License").
691ebeef5Sahrens * You may not use this file except in compliance with the License.
7fa9e4066Sahrens *
8fa9e4066Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9fa9e4066Sahrens * or http://www.opensolaris.org/os/licensing.
10fa9e4066Sahrens * See the License for the specific language governing permissions
11fa9e4066Sahrens * and limitations under the License.
12fa9e4066Sahrens *
13fa9e4066Sahrens * When distributing Covered Code, include this CDDL HEADER in each
14fa9e4066Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15fa9e4066Sahrens * If applicable, add the following below this CDDL HEADER, with the
16fa9e4066Sahrens * fields enclosed by brackets "[]" replaced with your own identifying
17fa9e4066Sahrens * information: Portions Copyright [yyyy] [name of copyright owner]
18fa9e4066Sahrens *
19fa9e4066Sahrens * CDDL HEADER END
20fa9e4066Sahrens */
21fa9e4066Sahrens /*
223f9d6ad7SLin Ling * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23*3b2aab18SMatthew Ahrens * Copyright (c) 2012 by Delphix. All rights reserved.
24fa9e4066Sahrens */
25fa9e4066Sahrens
26fa9e4066Sahrens #include <sys/zfs_context.h>
27fa9e4066Sahrens #include <sys/refcount.h>
28fa9e4066Sahrens
29744947dcSTom Erickson #ifdef ZFS_DEBUG
30fa9e4066Sahrens
31fa9e4066Sahrens #ifdef _KERNEL
32fa9e4066Sahrens int reference_tracking_enable = FALSE; /* runs out of memory too easily */
33fa9e4066Sahrens #else
34fa9e4066Sahrens int reference_tracking_enable = TRUE;
35fa9e4066Sahrens #endif
36*3b2aab18SMatthew Ahrens int reference_history = 3; /* tunable */
37fa9e4066Sahrens
38fa9e4066Sahrens static kmem_cache_t *reference_cache;
39fa9e4066Sahrens static kmem_cache_t *reference_history_cache;
40fa9e4066Sahrens
41fa9e4066Sahrens void
refcount_init(void)42fa9e4066Sahrens refcount_init(void)
43fa9e4066Sahrens {
44fa9e4066Sahrens reference_cache = kmem_cache_create("reference_cache",
45fa9e4066Sahrens sizeof (reference_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
46fa9e4066Sahrens
47fa9e4066Sahrens reference_history_cache = kmem_cache_create("reference_history_cache",
48fa9e4066Sahrens sizeof (uint64_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
49fa9e4066Sahrens }
50fa9e4066Sahrens
51fa9e4066Sahrens void
refcount_fini(void)52fa9e4066Sahrens refcount_fini(void)
53fa9e4066Sahrens {
54fa9e4066Sahrens kmem_cache_destroy(reference_cache);
55fa9e4066Sahrens kmem_cache_destroy(reference_history_cache);
56fa9e4066Sahrens }
57fa9e4066Sahrens
58fa9e4066Sahrens void
refcount_create(refcount_t * rc)59fa9e4066Sahrens refcount_create(refcount_t *rc)
60fa9e4066Sahrens {
6191ebeef5Sahrens mutex_init(&rc->rc_mtx, NULL, MUTEX_DEFAULT, NULL);
62fa9e4066Sahrens list_create(&rc->rc_list, sizeof (reference_t),
63fa9e4066Sahrens offsetof(reference_t, ref_link));
64fa9e4066Sahrens list_create(&rc->rc_removed, sizeof (reference_t),
65fa9e4066Sahrens offsetof(reference_t, ref_link));
6691ebeef5Sahrens rc->rc_count = 0;
6791ebeef5Sahrens rc->rc_removed_count = 0;
68*3b2aab18SMatthew Ahrens rc->rc_tracked = reference_tracking_enable;
69*3b2aab18SMatthew Ahrens }
70*3b2aab18SMatthew Ahrens
71*3b2aab18SMatthew Ahrens void
refcount_create_untracked(refcount_t * rc)72*3b2aab18SMatthew Ahrens refcount_create_untracked(refcount_t *rc)
73*3b2aab18SMatthew Ahrens {
74*3b2aab18SMatthew Ahrens refcount_create(rc);
75*3b2aab18SMatthew Ahrens rc->rc_tracked = B_FALSE;
76fa9e4066Sahrens }
77fa9e4066Sahrens
78fa9e4066Sahrens void
refcount_destroy_many(refcount_t * rc,uint64_t number)79fa9e4066Sahrens refcount_destroy_many(refcount_t *rc, uint64_t number)
80fa9e4066Sahrens {
81fa9e4066Sahrens reference_t *ref;
82fa9e4066Sahrens
83fa9e4066Sahrens ASSERT(rc->rc_count == number);
84fa9e4066Sahrens while (ref = list_head(&rc->rc_list)) {
85fa9e4066Sahrens list_remove(&rc->rc_list, ref);
86fa9e4066Sahrens kmem_cache_free(reference_cache, ref);
87fa9e4066Sahrens }
88fa9e4066Sahrens list_destroy(&rc->rc_list);
89fa9e4066Sahrens
90fa9e4066Sahrens while (ref = list_head(&rc->rc_removed)) {
91fa9e4066Sahrens list_remove(&rc->rc_removed, ref);
92fa9e4066Sahrens kmem_cache_free(reference_history_cache, ref->ref_removed);
93fa9e4066Sahrens kmem_cache_free(reference_cache, ref);
94fa9e4066Sahrens }
95fa9e4066Sahrens list_destroy(&rc->rc_removed);
96fa9e4066Sahrens mutex_destroy(&rc->rc_mtx);
97fa9e4066Sahrens }
98fa9e4066Sahrens
99fa9e4066Sahrens void
refcount_destroy(refcount_t * rc)100fa9e4066Sahrens refcount_destroy(refcount_t *rc)
101fa9e4066Sahrens {
102fa9e4066Sahrens refcount_destroy_many(rc, 0);
103fa9e4066Sahrens }
104fa9e4066Sahrens
105fa9e4066Sahrens int
refcount_is_zero(refcount_t * rc)106fa9e4066Sahrens refcount_is_zero(refcount_t *rc)
107fa9e4066Sahrens {
108fa9e4066Sahrens return (rc->rc_count == 0);
109fa9e4066Sahrens }
110fa9e4066Sahrens
111fa9e4066Sahrens int64_t
refcount_count(refcount_t * rc)112fa9e4066Sahrens refcount_count(refcount_t *rc)
113fa9e4066Sahrens {
114fa9e4066Sahrens return (rc->rc_count);
115fa9e4066Sahrens }
116fa9e4066Sahrens
117fa9e4066Sahrens int64_t
refcount_add_many(refcount_t * rc,uint64_t number,void * holder)118fa9e4066Sahrens refcount_add_many(refcount_t *rc, uint64_t number, void *holder)
119fa9e4066Sahrens {
120d5285caeSGeorge Wilson reference_t *ref = NULL;
121fa9e4066Sahrens int64_t count;
122fa9e4066Sahrens
123*3b2aab18SMatthew Ahrens if (rc->rc_tracked) {
124fa9e4066Sahrens ref = kmem_cache_alloc(reference_cache, KM_SLEEP);
125fa9e4066Sahrens ref->ref_holder = holder;
126fa9e4066Sahrens ref->ref_number = number;
127fa9e4066Sahrens }
128fa9e4066Sahrens mutex_enter(&rc->rc_mtx);
129fa9e4066Sahrens ASSERT(rc->rc_count >= 0);
130*3b2aab18SMatthew Ahrens if (rc->rc_tracked)
131fa9e4066Sahrens list_insert_head(&rc->rc_list, ref);
132fa9e4066Sahrens rc->rc_count += number;
133fa9e4066Sahrens count = rc->rc_count;
134fa9e4066Sahrens mutex_exit(&rc->rc_mtx);
135fa9e4066Sahrens
136fa9e4066Sahrens return (count);
137fa9e4066Sahrens }
138fa9e4066Sahrens
139fa9e4066Sahrens int64_t
refcount_add(refcount_t * rc,void * holder)140fa9e4066Sahrens refcount_add(refcount_t *rc, void *holder)
141fa9e4066Sahrens {
142fa9e4066Sahrens return (refcount_add_many(rc, 1, holder));
143fa9e4066Sahrens }
144fa9e4066Sahrens
145fa9e4066Sahrens int64_t
refcount_remove_many(refcount_t * rc,uint64_t number,void * holder)146fa9e4066Sahrens refcount_remove_many(refcount_t *rc, uint64_t number, void *holder)
147fa9e4066Sahrens {
148fa9e4066Sahrens reference_t *ref;
149fa9e4066Sahrens int64_t count;
150fa9e4066Sahrens
151fa9e4066Sahrens mutex_enter(&rc->rc_mtx);
152fa9e4066Sahrens ASSERT(rc->rc_count >= number);
153fa9e4066Sahrens
154*3b2aab18SMatthew Ahrens if (!rc->rc_tracked) {
155fa9e4066Sahrens rc->rc_count -= number;
156fa9e4066Sahrens count = rc->rc_count;
157fa9e4066Sahrens mutex_exit(&rc->rc_mtx);
158fa9e4066Sahrens return (count);
159fa9e4066Sahrens }
160fa9e4066Sahrens
161fa9e4066Sahrens for (ref = list_head(&rc->rc_list); ref;
162fa9e4066Sahrens ref = list_next(&rc->rc_list, ref)) {
163fa9e4066Sahrens if (ref->ref_holder == holder && ref->ref_number == number) {
164fa9e4066Sahrens list_remove(&rc->rc_list, ref);
165fa9e4066Sahrens if (reference_history > 0) {
166fa9e4066Sahrens ref->ref_removed =
167fa9e4066Sahrens kmem_cache_alloc(reference_history_cache,
168fa9e4066Sahrens KM_SLEEP);
169fa9e4066Sahrens list_insert_head(&rc->rc_removed, ref);
170fa9e4066Sahrens rc->rc_removed_count++;
171*3b2aab18SMatthew Ahrens if (rc->rc_removed_count > reference_history) {
172fa9e4066Sahrens ref = list_tail(&rc->rc_removed);
173fa9e4066Sahrens list_remove(&rc->rc_removed, ref);
174fa9e4066Sahrens kmem_cache_free(reference_history_cache,
175fa9e4066Sahrens ref->ref_removed);
176fa9e4066Sahrens kmem_cache_free(reference_cache, ref);
177fa9e4066Sahrens rc->rc_removed_count--;
178fa9e4066Sahrens }
179fa9e4066Sahrens } else {
180fa9e4066Sahrens kmem_cache_free(reference_cache, ref);
181fa9e4066Sahrens }
182fa9e4066Sahrens rc->rc_count -= number;
183fa9e4066Sahrens count = rc->rc_count;
184fa9e4066Sahrens mutex_exit(&rc->rc_mtx);
185fa9e4066Sahrens return (count);
186fa9e4066Sahrens }
187fa9e4066Sahrens }
188fa9e4066Sahrens panic("No such hold %p on refcount %llx", holder,
189fa9e4066Sahrens (u_longlong_t)(uintptr_t)rc);
190fa9e4066Sahrens return (-1);
191fa9e4066Sahrens }
192fa9e4066Sahrens
193fa9e4066Sahrens int64_t
refcount_remove(refcount_t * rc,void * holder)194fa9e4066Sahrens refcount_remove(refcount_t *rc, void *holder)
195fa9e4066Sahrens {
196fa9e4066Sahrens return (refcount_remove_many(rc, 1, holder));
197fa9e4066Sahrens }
198fa9e4066Sahrens
199744947dcSTom Erickson void
refcount_transfer(refcount_t * dst,refcount_t * src)200744947dcSTom Erickson refcount_transfer(refcount_t *dst, refcount_t *src)
201744947dcSTom Erickson {
202744947dcSTom Erickson int64_t count, removed_count;
203744947dcSTom Erickson list_t list, removed;
204744947dcSTom Erickson
205744947dcSTom Erickson list_create(&list, sizeof (reference_t),
206744947dcSTom Erickson offsetof(reference_t, ref_link));
207744947dcSTom Erickson list_create(&removed, sizeof (reference_t),
208744947dcSTom Erickson offsetof(reference_t, ref_link));
209744947dcSTom Erickson
210744947dcSTom Erickson mutex_enter(&src->rc_mtx);
211744947dcSTom Erickson count = src->rc_count;
212744947dcSTom Erickson removed_count = src->rc_removed_count;
213744947dcSTom Erickson src->rc_count = 0;
214744947dcSTom Erickson src->rc_removed_count = 0;
215744947dcSTom Erickson list_move_tail(&list, &src->rc_list);
216744947dcSTom Erickson list_move_tail(&removed, &src->rc_removed);
217744947dcSTom Erickson mutex_exit(&src->rc_mtx);
218744947dcSTom Erickson
219744947dcSTom Erickson mutex_enter(&dst->rc_mtx);
220744947dcSTom Erickson dst->rc_count += count;
221744947dcSTom Erickson dst->rc_removed_count += removed_count;
222744947dcSTom Erickson list_move_tail(&dst->rc_list, &list);
223744947dcSTom Erickson list_move_tail(&dst->rc_removed, &removed);
224744947dcSTom Erickson mutex_exit(&dst->rc_mtx);
225744947dcSTom Erickson
226744947dcSTom Erickson list_destroy(&list);
227744947dcSTom Erickson list_destroy(&removed);
228744947dcSTom Erickson }
229744947dcSTom Erickson
230744947dcSTom Erickson #endif /* ZFS_DEBUG */
231