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