xref: /illumos-gate/usr/src/uts/common/fs/zfs/refcount.c (revision dcbf3bd6a1f1360fc1afcee9e22c6dcff7844bf2)
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.
233b2aab18SMatthew 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
363b2aab18SMatthew 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
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
52fa9e4066Sahrens refcount_fini(void)
53fa9e4066Sahrens {
54fa9e4066Sahrens 	kmem_cache_destroy(reference_cache);
55fa9e4066Sahrens 	kmem_cache_destroy(reference_history_cache);
56fa9e4066Sahrens }
57fa9e4066Sahrens 
58fa9e4066Sahrens void
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;
683b2aab18SMatthew Ahrens 	rc->rc_tracked = reference_tracking_enable;
693b2aab18SMatthew Ahrens }
703b2aab18SMatthew Ahrens 
713b2aab18SMatthew Ahrens void
723b2aab18SMatthew Ahrens refcount_create_untracked(refcount_t *rc)
733b2aab18SMatthew Ahrens {
743b2aab18SMatthew Ahrens 	refcount_create(rc);
753b2aab18SMatthew Ahrens 	rc->rc_tracked = B_FALSE;
76fa9e4066Sahrens }
77fa9e4066Sahrens 
78fa9e4066Sahrens void
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
100fa9e4066Sahrens refcount_destroy(refcount_t *rc)
101fa9e4066Sahrens {
102fa9e4066Sahrens 	refcount_destroy_many(rc, 0);
103fa9e4066Sahrens }
104fa9e4066Sahrens 
105fa9e4066Sahrens int
106fa9e4066Sahrens refcount_is_zero(refcount_t *rc)
107fa9e4066Sahrens {
108fa9e4066Sahrens 	return (rc->rc_count == 0);
109fa9e4066Sahrens }
110fa9e4066Sahrens 
111fa9e4066Sahrens int64_t
112fa9e4066Sahrens refcount_count(refcount_t *rc)
113fa9e4066Sahrens {
114fa9e4066Sahrens 	return (rc->rc_count);
115fa9e4066Sahrens }
116fa9e4066Sahrens 
117fa9e4066Sahrens int64_t
118fa9e4066Sahrens refcount_add_many(refcount_t *rc, uint64_t number, void *holder)
119fa9e4066Sahrens {
120d5285caeSGeorge Wilson 	reference_t *ref = NULL;
121fa9e4066Sahrens 	int64_t count;
122fa9e4066Sahrens 
1233b2aab18SMatthew 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);
1303b2aab18SMatthew 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
140fa9e4066Sahrens refcount_add(refcount_t *rc, void *holder)
141fa9e4066Sahrens {
142fa9e4066Sahrens 	return (refcount_add_many(rc, 1, holder));
143fa9e4066Sahrens }
144fa9e4066Sahrens 
145fa9e4066Sahrens int64_t
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 
1543b2aab18SMatthew 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++;
1713b2aab18SMatthew 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
194fa9e4066Sahrens refcount_remove(refcount_t *rc, void *holder)
195fa9e4066Sahrens {
196fa9e4066Sahrens 	return (refcount_remove_many(rc, 1, holder));
197fa9e4066Sahrens }
198fa9e4066Sahrens 
199744947dcSTom Erickson void
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 
230*dcbf3bd6SGeorge Wilson void
231*dcbf3bd6SGeorge Wilson refcount_transfer_ownership(refcount_t *rc, void *current_holder,
232*dcbf3bd6SGeorge Wilson     void *new_holder)
233*dcbf3bd6SGeorge Wilson {
234*dcbf3bd6SGeorge Wilson 	reference_t *ref;
235*dcbf3bd6SGeorge Wilson 	boolean_t found = B_FALSE;
236*dcbf3bd6SGeorge Wilson 
237*dcbf3bd6SGeorge Wilson 	mutex_enter(&rc->rc_mtx);
238*dcbf3bd6SGeorge Wilson 	if (!rc->rc_tracked) {
239*dcbf3bd6SGeorge Wilson 		mutex_exit(&rc->rc_mtx);
240*dcbf3bd6SGeorge Wilson 		return;
241*dcbf3bd6SGeorge Wilson 	}
242*dcbf3bd6SGeorge Wilson 
243*dcbf3bd6SGeorge Wilson 	for (ref = list_head(&rc->rc_list); ref;
244*dcbf3bd6SGeorge Wilson 	    ref = list_next(&rc->rc_list, ref)) {
245*dcbf3bd6SGeorge Wilson 		if (ref->ref_holder == current_holder) {
246*dcbf3bd6SGeorge Wilson 			ref->ref_holder = new_holder;
247*dcbf3bd6SGeorge Wilson 			found = B_TRUE;
248*dcbf3bd6SGeorge Wilson 			break;
249*dcbf3bd6SGeorge Wilson 		}
250*dcbf3bd6SGeorge Wilson 	}
251*dcbf3bd6SGeorge Wilson 	ASSERT(found);
252*dcbf3bd6SGeorge Wilson 	mutex_exit(&rc->rc_mtx);
253*dcbf3bd6SGeorge Wilson }
254744947dcSTom Erickson #endif	/* ZFS_DEBUG */
255