xref: /illumos-gate/usr/src/uts/common/fs/zfs/refcount.c (revision eb633035c80613ec93d62f90482837adaaf21a0a)
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.
230f7643c7SGeorge Wilson  * Copyright (c) 2012, 2015 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
42e914ace2STim Schumacher zfs_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
52e914ace2STim Schumacher zfs_refcount_fini(void)
53fa9e4066Sahrens {
54fa9e4066Sahrens 	kmem_cache_destroy(reference_cache);
55fa9e4066Sahrens 	kmem_cache_destroy(reference_history_cache);
56fa9e4066Sahrens }
57fa9e4066Sahrens 
58fa9e4066Sahrens void
59e914ace2STim Schumacher zfs_refcount_create(zfs_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 
710f7643c7SGeorge Wilson void
72e914ace2STim Schumacher zfs_refcount_create_tracked(zfs_refcount_t *rc)
730f7643c7SGeorge Wilson {
74e914ace2STim Schumacher 	zfs_refcount_create(rc);
750f7643c7SGeorge Wilson 	rc->rc_tracked = B_TRUE;
760f7643c7SGeorge Wilson }
770f7643c7SGeorge Wilson 
783b2aab18SMatthew Ahrens void
79e914ace2STim Schumacher zfs_refcount_create_untracked(zfs_refcount_t *rc)
803b2aab18SMatthew Ahrens {
81e914ace2STim Schumacher 	zfs_refcount_create(rc);
823b2aab18SMatthew Ahrens 	rc->rc_tracked = B_FALSE;
83fa9e4066Sahrens }
84fa9e4066Sahrens 
85fa9e4066Sahrens void
86e914ace2STim Schumacher zfs_refcount_destroy_many(zfs_refcount_t *rc, uint64_t number)
87fa9e4066Sahrens {
88fa9e4066Sahrens 	reference_t *ref;
89fa9e4066Sahrens 
90fa9e4066Sahrens 	ASSERT(rc->rc_count == number);
91fa9e4066Sahrens 	while (ref = list_head(&rc->rc_list)) {
92fa9e4066Sahrens 		list_remove(&rc->rc_list, ref);
93fa9e4066Sahrens 		kmem_cache_free(reference_cache, ref);
94fa9e4066Sahrens 	}
95fa9e4066Sahrens 	list_destroy(&rc->rc_list);
96fa9e4066Sahrens 
97fa9e4066Sahrens 	while (ref = list_head(&rc->rc_removed)) {
98fa9e4066Sahrens 		list_remove(&rc->rc_removed, ref);
99fa9e4066Sahrens 		kmem_cache_free(reference_history_cache, ref->ref_removed);
100fa9e4066Sahrens 		kmem_cache_free(reference_cache, ref);
101fa9e4066Sahrens 	}
102fa9e4066Sahrens 	list_destroy(&rc->rc_removed);
103fa9e4066Sahrens 	mutex_destroy(&rc->rc_mtx);
104fa9e4066Sahrens }
105fa9e4066Sahrens 
106fa9e4066Sahrens void
107e914ace2STim Schumacher zfs_refcount_destroy(zfs_refcount_t *rc)
108fa9e4066Sahrens {
109e914ace2STim Schumacher 	zfs_refcount_destroy_many(rc, 0);
110fa9e4066Sahrens }
111fa9e4066Sahrens 
112fa9e4066Sahrens int
113e914ace2STim Schumacher zfs_refcount_is_zero(zfs_refcount_t *rc)
114fa9e4066Sahrens {
115fa9e4066Sahrens 	return (rc->rc_count == 0);
116fa9e4066Sahrens }
117fa9e4066Sahrens 
118fa9e4066Sahrens int64_t
119e914ace2STim Schumacher zfs_refcount_count(zfs_refcount_t *rc)
120fa9e4066Sahrens {
121fa9e4066Sahrens 	return (rc->rc_count);
122fa9e4066Sahrens }
123fa9e4066Sahrens 
124fa9e4066Sahrens int64_t
125e914ace2STim Schumacher zfs_refcount_add_many(zfs_refcount_t *rc, uint64_t number, void *holder)
126fa9e4066Sahrens {
127d5285caeSGeorge Wilson 	reference_t *ref = NULL;
128fa9e4066Sahrens 	int64_t count;
129fa9e4066Sahrens 
1303b2aab18SMatthew Ahrens 	if (rc->rc_tracked) {
131fa9e4066Sahrens 		ref = kmem_cache_alloc(reference_cache, KM_SLEEP);
132fa9e4066Sahrens 		ref->ref_holder = holder;
133fa9e4066Sahrens 		ref->ref_number = number;
134fa9e4066Sahrens 	}
135fa9e4066Sahrens 	mutex_enter(&rc->rc_mtx);
136fa9e4066Sahrens 	ASSERT(rc->rc_count >= 0);
1373b2aab18SMatthew Ahrens 	if (rc->rc_tracked)
138fa9e4066Sahrens 		list_insert_head(&rc->rc_list, ref);
139fa9e4066Sahrens 	rc->rc_count += number;
140fa9e4066Sahrens 	count = rc->rc_count;
141fa9e4066Sahrens 	mutex_exit(&rc->rc_mtx);
142fa9e4066Sahrens 
143fa9e4066Sahrens 	return (count);
144fa9e4066Sahrens }
145fa9e4066Sahrens 
146fa9e4066Sahrens int64_t
147e914ace2STim Schumacher zfs_refcount_add(zfs_refcount_t *rc, void *holder)
148fa9e4066Sahrens {
149e914ace2STim Schumacher 	return (zfs_refcount_add_many(rc, 1, holder));
150fa9e4066Sahrens }
151fa9e4066Sahrens 
152fa9e4066Sahrens int64_t
153e914ace2STim Schumacher zfs_refcount_remove_many(zfs_refcount_t *rc, uint64_t number, void *holder)
154fa9e4066Sahrens {
155fa9e4066Sahrens 	reference_t *ref;
156fa9e4066Sahrens 	int64_t count;
157fa9e4066Sahrens 
158fa9e4066Sahrens 	mutex_enter(&rc->rc_mtx);
159fa9e4066Sahrens 	ASSERT(rc->rc_count >= number);
160fa9e4066Sahrens 
1613b2aab18SMatthew Ahrens 	if (!rc->rc_tracked) {
162fa9e4066Sahrens 		rc->rc_count -= number;
163fa9e4066Sahrens 		count = rc->rc_count;
164fa9e4066Sahrens 		mutex_exit(&rc->rc_mtx);
165fa9e4066Sahrens 		return (count);
166fa9e4066Sahrens 	}
167fa9e4066Sahrens 
168fa9e4066Sahrens 	for (ref = list_head(&rc->rc_list); ref;
169fa9e4066Sahrens 	    ref = list_next(&rc->rc_list, ref)) {
170fa9e4066Sahrens 		if (ref->ref_holder == holder && ref->ref_number == number) {
171fa9e4066Sahrens 			list_remove(&rc->rc_list, ref);
172fa9e4066Sahrens 			if (reference_history > 0) {
173fa9e4066Sahrens 				ref->ref_removed =
174fa9e4066Sahrens 				    kmem_cache_alloc(reference_history_cache,
175fa9e4066Sahrens 				    KM_SLEEP);
176fa9e4066Sahrens 				list_insert_head(&rc->rc_removed, ref);
177fa9e4066Sahrens 				rc->rc_removed_count++;
1783b2aab18SMatthew Ahrens 				if (rc->rc_removed_count > reference_history) {
179fa9e4066Sahrens 					ref = list_tail(&rc->rc_removed);
180fa9e4066Sahrens 					list_remove(&rc->rc_removed, ref);
181fa9e4066Sahrens 					kmem_cache_free(reference_history_cache,
182fa9e4066Sahrens 					    ref->ref_removed);
183fa9e4066Sahrens 					kmem_cache_free(reference_cache, ref);
184fa9e4066Sahrens 					rc->rc_removed_count--;
185fa9e4066Sahrens 				}
186fa9e4066Sahrens 			} else {
187fa9e4066Sahrens 				kmem_cache_free(reference_cache, ref);
188fa9e4066Sahrens 			}
189fa9e4066Sahrens 			rc->rc_count -= number;
190fa9e4066Sahrens 			count = rc->rc_count;
191fa9e4066Sahrens 			mutex_exit(&rc->rc_mtx);
192fa9e4066Sahrens 			return (count);
193fa9e4066Sahrens 		}
194fa9e4066Sahrens 	}
195fa9e4066Sahrens 	panic("No such hold %p on refcount %llx", holder,
196fa9e4066Sahrens 	    (u_longlong_t)(uintptr_t)rc);
197fa9e4066Sahrens 	return (-1);
198fa9e4066Sahrens }
199fa9e4066Sahrens 
200fa9e4066Sahrens int64_t
201e914ace2STim Schumacher zfs_refcount_remove(zfs_refcount_t *rc, void *holder)
202fa9e4066Sahrens {
203e914ace2STim Schumacher 	return (zfs_refcount_remove_many(rc, 1, holder));
204fa9e4066Sahrens }
205fa9e4066Sahrens 
206744947dcSTom Erickson void
207e914ace2STim Schumacher zfs_refcount_transfer(zfs_refcount_t *dst, zfs_refcount_t *src)
208744947dcSTom Erickson {
209744947dcSTom Erickson 	int64_t count, removed_count;
210744947dcSTom Erickson 	list_t list, removed;
211744947dcSTom Erickson 
212744947dcSTom Erickson 	list_create(&list, sizeof (reference_t),
213744947dcSTom Erickson 	    offsetof(reference_t, ref_link));
214744947dcSTom Erickson 	list_create(&removed, sizeof (reference_t),
215744947dcSTom Erickson 	    offsetof(reference_t, ref_link));
216744947dcSTom Erickson 
217744947dcSTom Erickson 	mutex_enter(&src->rc_mtx);
218744947dcSTom Erickson 	count = src->rc_count;
219744947dcSTom Erickson 	removed_count = src->rc_removed_count;
220744947dcSTom Erickson 	src->rc_count = 0;
221744947dcSTom Erickson 	src->rc_removed_count = 0;
222744947dcSTom Erickson 	list_move_tail(&list, &src->rc_list);
223744947dcSTom Erickson 	list_move_tail(&removed, &src->rc_removed);
224744947dcSTom Erickson 	mutex_exit(&src->rc_mtx);
225744947dcSTom Erickson 
226744947dcSTom Erickson 	mutex_enter(&dst->rc_mtx);
227744947dcSTom Erickson 	dst->rc_count += count;
228744947dcSTom Erickson 	dst->rc_removed_count += removed_count;
229744947dcSTom Erickson 	list_move_tail(&dst->rc_list, &list);
230744947dcSTom Erickson 	list_move_tail(&dst->rc_removed, &removed);
231744947dcSTom Erickson 	mutex_exit(&dst->rc_mtx);
232744947dcSTom Erickson 
233744947dcSTom Erickson 	list_destroy(&list);
234744947dcSTom Erickson 	list_destroy(&removed);
235744947dcSTom Erickson }
236744947dcSTom Erickson 
237*eb633035STom Caputi /* ARGSUSED */
238dcbf3bd6SGeorge Wilson void
239*eb633035STom Caputi zfs_refcount_transfer_ownership_many(zfs_refcount_t *rc, uint64_t number,
240*eb633035STom Caputi     void *current_holder, void *new_holder)
241dcbf3bd6SGeorge Wilson {
242dcbf3bd6SGeorge Wilson 	reference_t *ref;
243dcbf3bd6SGeorge Wilson 	boolean_t found = B_FALSE;
244dcbf3bd6SGeorge Wilson 
245dcbf3bd6SGeorge Wilson 	mutex_enter(&rc->rc_mtx);
246dcbf3bd6SGeorge Wilson 	if (!rc->rc_tracked) {
247dcbf3bd6SGeorge Wilson 		mutex_exit(&rc->rc_mtx);
248dcbf3bd6SGeorge Wilson 		return;
249dcbf3bd6SGeorge Wilson 	}
250dcbf3bd6SGeorge Wilson 
251dcbf3bd6SGeorge Wilson 	for (ref = list_head(&rc->rc_list); ref;
252dcbf3bd6SGeorge Wilson 	    ref = list_next(&rc->rc_list, ref)) {
253*eb633035STom Caputi 		if (ref->ref_holder == current_holder &&
254*eb633035STom Caputi 		    ref->ref_number == number) {
255dcbf3bd6SGeorge Wilson 			ref->ref_holder = new_holder;
256dcbf3bd6SGeorge Wilson 			found = B_TRUE;
257dcbf3bd6SGeorge Wilson 			break;
258dcbf3bd6SGeorge Wilson 		}
259dcbf3bd6SGeorge Wilson 	}
260dcbf3bd6SGeorge Wilson 	ASSERT(found);
261dcbf3bd6SGeorge Wilson 	mutex_exit(&rc->rc_mtx);
262dcbf3bd6SGeorge Wilson }
2630f7643c7SGeorge Wilson 
264*eb633035STom Caputi void
265*eb633035STom Caputi zfs_refcount_transfer_ownership(zfs_refcount_t *rc, void *current_holder,
266*eb633035STom Caputi     void *new_holder)
267*eb633035STom Caputi {
268*eb633035STom Caputi 	zfs_refcount_transfer_ownership_many(rc, 1, current_holder,
269*eb633035STom Caputi 	    new_holder);
270*eb633035STom Caputi }
271*eb633035STom Caputi 
2720f7643c7SGeorge Wilson /*
2730f7643c7SGeorge Wilson  * If tracking is enabled, return true if a reference exists that matches
2740f7643c7SGeorge Wilson  * the "holder" tag. If tracking is disabled, then return true if a reference
2750f7643c7SGeorge Wilson  * might be held.
2760f7643c7SGeorge Wilson  */
2770f7643c7SGeorge Wilson boolean_t
278e914ace2STim Schumacher zfs_refcount_held(zfs_refcount_t *rc, void *holder)
2790f7643c7SGeorge Wilson {
2800f7643c7SGeorge Wilson 	reference_t *ref;
2810f7643c7SGeorge Wilson 
2820f7643c7SGeorge Wilson 	mutex_enter(&rc->rc_mtx);
2830f7643c7SGeorge Wilson 
2840f7643c7SGeorge Wilson 	if (!rc->rc_tracked) {
2850f7643c7SGeorge Wilson 		mutex_exit(&rc->rc_mtx);
2860f7643c7SGeorge Wilson 		return (rc->rc_count > 0);
2870f7643c7SGeorge Wilson 	}
2880f7643c7SGeorge Wilson 
2890f7643c7SGeorge Wilson 	for (ref = list_head(&rc->rc_list); ref;
2900f7643c7SGeorge Wilson 	    ref = list_next(&rc->rc_list, ref)) {
2910f7643c7SGeorge Wilson 		if (ref->ref_holder == holder) {
2920f7643c7SGeorge Wilson 			mutex_exit(&rc->rc_mtx);
2930f7643c7SGeorge Wilson 			return (B_TRUE);
2940f7643c7SGeorge Wilson 		}
2950f7643c7SGeorge Wilson 	}
2960f7643c7SGeorge Wilson 	mutex_exit(&rc->rc_mtx);
2970f7643c7SGeorge Wilson 	return (B_FALSE);
2980f7643c7SGeorge Wilson }
2990f7643c7SGeorge Wilson 
3000f7643c7SGeorge Wilson /*
3010f7643c7SGeorge Wilson  * If tracking is enabled, return true if a reference does not exist that
3020f7643c7SGeorge Wilson  * matches the "holder" tag. If tracking is disabled, always return true
3030f7643c7SGeorge Wilson  * since the reference might not be held.
3040f7643c7SGeorge Wilson  */
3050f7643c7SGeorge Wilson boolean_t
306e914ace2STim Schumacher zfs_refcount_not_held(zfs_refcount_t *rc, void *holder)
3070f7643c7SGeorge Wilson {
3080f7643c7SGeorge Wilson 	reference_t *ref;
3090f7643c7SGeorge Wilson 
3100f7643c7SGeorge Wilson 	mutex_enter(&rc->rc_mtx);
3110f7643c7SGeorge Wilson 
3120f7643c7SGeorge Wilson 	if (!rc->rc_tracked) {
3130f7643c7SGeorge Wilson 		mutex_exit(&rc->rc_mtx);
3140f7643c7SGeorge Wilson 		return (B_TRUE);
3150f7643c7SGeorge Wilson 	}
3160f7643c7SGeorge Wilson 
3170f7643c7SGeorge Wilson 	for (ref = list_head(&rc->rc_list); ref;
3180f7643c7SGeorge Wilson 	    ref = list_next(&rc->rc_list, ref)) {
3190f7643c7SGeorge Wilson 		if (ref->ref_holder == holder) {
3200f7643c7SGeorge Wilson 			mutex_exit(&rc->rc_mtx);
3210f7643c7SGeorge Wilson 			return (B_FALSE);
3220f7643c7SGeorge Wilson 		}
3230f7643c7SGeorge Wilson 	}
3240f7643c7SGeorge Wilson 	mutex_exit(&rc->rc_mtx);
3250f7643c7SGeorge Wilson 	return (B_TRUE);
3260f7643c7SGeorge Wilson }
327744947dcSTom Erickson #endif	/* ZFS_DEBUG */
328