xref: /illumos-gate/usr/src/uts/common/fs/zfs/refcount.c (revision fa9e4066f08beec538e775443c5be79dd423fcab)
1*fa9e4066Sahrens /*
2*fa9e4066Sahrens  * CDDL HEADER START
3*fa9e4066Sahrens  *
4*fa9e4066Sahrens  * The contents of this file are subject to the terms of the
5*fa9e4066Sahrens  * Common Development and Distribution License, Version 1.0 only
6*fa9e4066Sahrens  * (the "License").  You may not use this file except in compliance
7*fa9e4066Sahrens  * with the License.
8*fa9e4066Sahrens  *
9*fa9e4066Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*fa9e4066Sahrens  * or http://www.opensolaris.org/os/licensing.
11*fa9e4066Sahrens  * See the License for the specific language governing permissions
12*fa9e4066Sahrens  * and limitations under the License.
13*fa9e4066Sahrens  *
14*fa9e4066Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
15*fa9e4066Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*fa9e4066Sahrens  * If applicable, add the following below this CDDL HEADER, with the
17*fa9e4066Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
18*fa9e4066Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
19*fa9e4066Sahrens  *
20*fa9e4066Sahrens  * CDDL HEADER END
21*fa9e4066Sahrens  */
22*fa9e4066Sahrens /*
23*fa9e4066Sahrens  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*fa9e4066Sahrens  * Use is subject to license terms.
25*fa9e4066Sahrens  */
26*fa9e4066Sahrens 
27*fa9e4066Sahrens #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*fa9e4066Sahrens 
29*fa9e4066Sahrens #include <sys/zfs_context.h>
30*fa9e4066Sahrens #include <sys/refcount.h>
31*fa9e4066Sahrens 
32*fa9e4066Sahrens #if defined(DEBUG) || !defined(_KERNEL)
33*fa9e4066Sahrens 
34*fa9e4066Sahrens #ifdef _KERNEL
35*fa9e4066Sahrens int reference_tracking_enable = FALSE; /* runs out of memory too easily */
36*fa9e4066Sahrens #else
37*fa9e4066Sahrens int reference_tracking_enable = TRUE;
38*fa9e4066Sahrens #endif
39*fa9e4066Sahrens int reference_history = 4; /* tunable */
40*fa9e4066Sahrens 
41*fa9e4066Sahrens static kmem_cache_t *reference_cache;
42*fa9e4066Sahrens static kmem_cache_t *reference_history_cache;
43*fa9e4066Sahrens 
44*fa9e4066Sahrens void
45*fa9e4066Sahrens refcount_init(void)
46*fa9e4066Sahrens {
47*fa9e4066Sahrens 	reference_cache = kmem_cache_create("reference_cache",
48*fa9e4066Sahrens 	    sizeof (reference_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
49*fa9e4066Sahrens 
50*fa9e4066Sahrens 	reference_history_cache = kmem_cache_create("reference_history_cache",
51*fa9e4066Sahrens 	    sizeof (uint64_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
52*fa9e4066Sahrens }
53*fa9e4066Sahrens 
54*fa9e4066Sahrens void
55*fa9e4066Sahrens refcount_fini(void)
56*fa9e4066Sahrens {
57*fa9e4066Sahrens 	kmem_cache_destroy(reference_cache);
58*fa9e4066Sahrens 	kmem_cache_destroy(reference_history_cache);
59*fa9e4066Sahrens }
60*fa9e4066Sahrens 
61*fa9e4066Sahrens void
62*fa9e4066Sahrens refcount_create(refcount_t *rc)
63*fa9e4066Sahrens {
64*fa9e4066Sahrens 	list_create(&rc->rc_list, sizeof (reference_t),
65*fa9e4066Sahrens 	    offsetof(reference_t, ref_link));
66*fa9e4066Sahrens 	list_create(&rc->rc_removed, sizeof (reference_t),
67*fa9e4066Sahrens 	    offsetof(reference_t, ref_link));
68*fa9e4066Sahrens 	mutex_init(&rc->rc_mtx, NULL, MUTEX_DEFAULT, NULL);
69*fa9e4066Sahrens }
70*fa9e4066Sahrens 
71*fa9e4066Sahrens void
72*fa9e4066Sahrens refcount_destroy_many(refcount_t *rc, uint64_t number)
73*fa9e4066Sahrens {
74*fa9e4066Sahrens 	reference_t *ref;
75*fa9e4066Sahrens 
76*fa9e4066Sahrens 	ASSERT(rc->rc_count == number);
77*fa9e4066Sahrens 	while (ref = list_head(&rc->rc_list)) {
78*fa9e4066Sahrens 		list_remove(&rc->rc_list, ref);
79*fa9e4066Sahrens 		kmem_cache_free(reference_cache, ref);
80*fa9e4066Sahrens 	}
81*fa9e4066Sahrens 	list_destroy(&rc->rc_list);
82*fa9e4066Sahrens 
83*fa9e4066Sahrens 	while (ref = list_head(&rc->rc_removed)) {
84*fa9e4066Sahrens 		list_remove(&rc->rc_removed, ref);
85*fa9e4066Sahrens 		kmem_cache_free(reference_history_cache, ref->ref_removed);
86*fa9e4066Sahrens 		kmem_cache_free(reference_cache, ref);
87*fa9e4066Sahrens 	}
88*fa9e4066Sahrens 	list_destroy(&rc->rc_removed);
89*fa9e4066Sahrens 	mutex_destroy(&rc->rc_mtx);
90*fa9e4066Sahrens }
91*fa9e4066Sahrens 
92*fa9e4066Sahrens void
93*fa9e4066Sahrens refcount_destroy(refcount_t *rc)
94*fa9e4066Sahrens {
95*fa9e4066Sahrens 	refcount_destroy_many(rc, 0);
96*fa9e4066Sahrens }
97*fa9e4066Sahrens 
98*fa9e4066Sahrens int
99*fa9e4066Sahrens refcount_is_zero(refcount_t *rc)
100*fa9e4066Sahrens {
101*fa9e4066Sahrens 	ASSERT(rc->rc_count >= 0);
102*fa9e4066Sahrens 	return (rc->rc_count == 0);
103*fa9e4066Sahrens }
104*fa9e4066Sahrens 
105*fa9e4066Sahrens int64_t
106*fa9e4066Sahrens refcount_count(refcount_t *rc)
107*fa9e4066Sahrens {
108*fa9e4066Sahrens 	ASSERT(rc->rc_count >= 0);
109*fa9e4066Sahrens 	return (rc->rc_count);
110*fa9e4066Sahrens }
111*fa9e4066Sahrens 
112*fa9e4066Sahrens int64_t
113*fa9e4066Sahrens refcount_add_many(refcount_t *rc, uint64_t number, void *holder)
114*fa9e4066Sahrens {
115*fa9e4066Sahrens 	reference_t *ref;
116*fa9e4066Sahrens 	int64_t count;
117*fa9e4066Sahrens 
118*fa9e4066Sahrens 	if (reference_tracking_enable) {
119*fa9e4066Sahrens 		ref = kmem_cache_alloc(reference_cache, KM_SLEEP);
120*fa9e4066Sahrens 		ref->ref_holder = holder;
121*fa9e4066Sahrens 		ref->ref_number = number;
122*fa9e4066Sahrens 	}
123*fa9e4066Sahrens 	mutex_enter(&rc->rc_mtx);
124*fa9e4066Sahrens 	ASSERT(rc->rc_count >= 0);
125*fa9e4066Sahrens 	if (reference_tracking_enable)
126*fa9e4066Sahrens 		list_insert_head(&rc->rc_list, ref);
127*fa9e4066Sahrens 	rc->rc_count += number;
128*fa9e4066Sahrens 	count = rc->rc_count;
129*fa9e4066Sahrens 	mutex_exit(&rc->rc_mtx);
130*fa9e4066Sahrens 
131*fa9e4066Sahrens 	return (count);
132*fa9e4066Sahrens }
133*fa9e4066Sahrens 
134*fa9e4066Sahrens int64_t
135*fa9e4066Sahrens refcount_add(refcount_t *rc, void *holder)
136*fa9e4066Sahrens {
137*fa9e4066Sahrens 	return (refcount_add_many(rc, 1, holder));
138*fa9e4066Sahrens }
139*fa9e4066Sahrens 
140*fa9e4066Sahrens int64_t
141*fa9e4066Sahrens refcount_remove_many(refcount_t *rc, uint64_t number, void *holder)
142*fa9e4066Sahrens {
143*fa9e4066Sahrens 	reference_t *ref;
144*fa9e4066Sahrens 	int64_t count;
145*fa9e4066Sahrens 
146*fa9e4066Sahrens 	mutex_enter(&rc->rc_mtx);
147*fa9e4066Sahrens 	ASSERT(rc->rc_count >= number);
148*fa9e4066Sahrens 
149*fa9e4066Sahrens 	if (!reference_tracking_enable) {
150*fa9e4066Sahrens 		rc->rc_count -= number;
151*fa9e4066Sahrens 		count = rc->rc_count;
152*fa9e4066Sahrens 		mutex_exit(&rc->rc_mtx);
153*fa9e4066Sahrens 		return (count);
154*fa9e4066Sahrens 	}
155*fa9e4066Sahrens 
156*fa9e4066Sahrens 	for (ref = list_head(&rc->rc_list); ref;
157*fa9e4066Sahrens 	    ref = list_next(&rc->rc_list, ref)) {
158*fa9e4066Sahrens 		if (ref->ref_holder == holder && ref->ref_number == number) {
159*fa9e4066Sahrens 			list_remove(&rc->rc_list, ref);
160*fa9e4066Sahrens 			if (reference_history > 0) {
161*fa9e4066Sahrens 				ref->ref_removed =
162*fa9e4066Sahrens 				    kmem_cache_alloc(reference_history_cache,
163*fa9e4066Sahrens 				    KM_SLEEP);
164*fa9e4066Sahrens 				list_insert_head(&rc->rc_removed, ref);
165*fa9e4066Sahrens 				rc->rc_removed_count++;
166*fa9e4066Sahrens 				if (rc->rc_removed_count >= reference_history) {
167*fa9e4066Sahrens 					ref = list_tail(&rc->rc_removed);
168*fa9e4066Sahrens 					list_remove(&rc->rc_removed, ref);
169*fa9e4066Sahrens 					kmem_cache_free(reference_history_cache,
170*fa9e4066Sahrens 					    ref->ref_removed);
171*fa9e4066Sahrens 					kmem_cache_free(reference_cache, ref);
172*fa9e4066Sahrens 					rc->rc_removed_count--;
173*fa9e4066Sahrens 				}
174*fa9e4066Sahrens 			} else {
175*fa9e4066Sahrens 				kmem_cache_free(reference_cache, ref);
176*fa9e4066Sahrens 			}
177*fa9e4066Sahrens 			rc->rc_count -= number;
178*fa9e4066Sahrens 			count = rc->rc_count;
179*fa9e4066Sahrens 			mutex_exit(&rc->rc_mtx);
180*fa9e4066Sahrens 			return (count);
181*fa9e4066Sahrens 		}
182*fa9e4066Sahrens 	}
183*fa9e4066Sahrens 	panic("No such hold %p on refcount %llx", holder,
184*fa9e4066Sahrens 	    (u_longlong_t)(uintptr_t)rc);
185*fa9e4066Sahrens 	return (-1);
186*fa9e4066Sahrens }
187*fa9e4066Sahrens 
188*fa9e4066Sahrens int64_t
189*fa9e4066Sahrens refcount_remove(refcount_t *rc, void *holder)
190*fa9e4066Sahrens {
191*fa9e4066Sahrens 	return (refcount_remove_many(rc, 1, holder));
192*fa9e4066Sahrens }
193*fa9e4066Sahrens 
194*fa9e4066Sahrens #endif
195