1789Sahrens /*
2789Sahrens  * CDDL HEADER START
3789Sahrens  *
4789Sahrens  * The contents of this file are subject to the terms of the
5*4787Sahrens  * Common Development and Distribution License (the "License").
6*4787Sahrens  * You may not use this file except in compliance with the License.
7789Sahrens  *
8789Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9789Sahrens  * or http://www.opensolaris.org/os/licensing.
10789Sahrens  * See the License for the specific language governing permissions
11789Sahrens  * and limitations under the License.
12789Sahrens  *
13789Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
14789Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15789Sahrens  * If applicable, add the following below this CDDL HEADER, with the
16789Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
17789Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
18789Sahrens  *
19789Sahrens  * CDDL HEADER END
20789Sahrens  */
21789Sahrens /*
22*4787Sahrens  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23789Sahrens  * Use is subject to license terms.
24789Sahrens  */
25789Sahrens 
26789Sahrens #pragma ident	"%Z%%M%	%I%	%E% SMI"
27789Sahrens 
28789Sahrens #include <sys/zfs_context.h>
29789Sahrens #include <sys/refcount.h>
30789Sahrens 
31789Sahrens #if defined(DEBUG) || !defined(_KERNEL)
32789Sahrens 
33789Sahrens #ifdef _KERNEL
34789Sahrens int reference_tracking_enable = FALSE; /* runs out of memory too easily */
35789Sahrens #else
36789Sahrens int reference_tracking_enable = TRUE;
37789Sahrens #endif
38789Sahrens int reference_history = 4; /* tunable */
39789Sahrens 
40789Sahrens static kmem_cache_t *reference_cache;
41789Sahrens static kmem_cache_t *reference_history_cache;
42789Sahrens 
43789Sahrens void
44789Sahrens refcount_init(void)
45789Sahrens {
46789Sahrens 	reference_cache = kmem_cache_create("reference_cache",
47789Sahrens 	    sizeof (reference_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
48789Sahrens 
49789Sahrens 	reference_history_cache = kmem_cache_create("reference_history_cache",
50789Sahrens 	    sizeof (uint64_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
51789Sahrens }
52789Sahrens 
53789Sahrens void
54789Sahrens refcount_fini(void)
55789Sahrens {
56789Sahrens 	kmem_cache_destroy(reference_cache);
57789Sahrens 	kmem_cache_destroy(reference_history_cache);
58789Sahrens }
59789Sahrens 
60789Sahrens void
61789Sahrens refcount_create(refcount_t *rc)
62789Sahrens {
63*4787Sahrens 	mutex_init(&rc->rc_mtx, NULL, MUTEX_DEFAULT, NULL);
64789Sahrens 	list_create(&rc->rc_list, sizeof (reference_t),
65789Sahrens 	    offsetof(reference_t, ref_link));
66789Sahrens 	list_create(&rc->rc_removed, sizeof (reference_t),
67789Sahrens 	    offsetof(reference_t, ref_link));
68*4787Sahrens 	rc->rc_count = 0;
69*4787Sahrens 	rc->rc_removed_count = 0;
70789Sahrens }
71789Sahrens 
72789Sahrens void
73789Sahrens refcount_destroy_many(refcount_t *rc, uint64_t number)
74789Sahrens {
75789Sahrens 	reference_t *ref;
76789Sahrens 
77789Sahrens 	ASSERT(rc->rc_count == number);
78789Sahrens 	while (ref = list_head(&rc->rc_list)) {
79789Sahrens 		list_remove(&rc->rc_list, ref);
80789Sahrens 		kmem_cache_free(reference_cache, ref);
81789Sahrens 	}
82789Sahrens 	list_destroy(&rc->rc_list);
83789Sahrens 
84789Sahrens 	while (ref = list_head(&rc->rc_removed)) {
85789Sahrens 		list_remove(&rc->rc_removed, ref);
86789Sahrens 		kmem_cache_free(reference_history_cache, ref->ref_removed);
87789Sahrens 		kmem_cache_free(reference_cache, ref);
88789Sahrens 	}
89789Sahrens 	list_destroy(&rc->rc_removed);
90789Sahrens 	mutex_destroy(&rc->rc_mtx);
91789Sahrens }
92789Sahrens 
93789Sahrens void
94789Sahrens refcount_destroy(refcount_t *rc)
95789Sahrens {
96789Sahrens 	refcount_destroy_many(rc, 0);
97789Sahrens }
98789Sahrens 
99789Sahrens int
100789Sahrens refcount_is_zero(refcount_t *rc)
101789Sahrens {
102789Sahrens 	ASSERT(rc->rc_count >= 0);
103789Sahrens 	return (rc->rc_count == 0);
104789Sahrens }
105789Sahrens 
106789Sahrens int64_t
107789Sahrens refcount_count(refcount_t *rc)
108789Sahrens {
109789Sahrens 	ASSERT(rc->rc_count >= 0);
110789Sahrens 	return (rc->rc_count);
111789Sahrens }
112789Sahrens 
113789Sahrens int64_t
114789Sahrens refcount_add_many(refcount_t *rc, uint64_t number, void *holder)
115789Sahrens {
116789Sahrens 	reference_t *ref;
117789Sahrens 	int64_t count;
118789Sahrens 
119789Sahrens 	if (reference_tracking_enable) {
120789Sahrens 		ref = kmem_cache_alloc(reference_cache, KM_SLEEP);
121789Sahrens 		ref->ref_holder = holder;
122789Sahrens 		ref->ref_number = number;
123789Sahrens 	}
124789Sahrens 	mutex_enter(&rc->rc_mtx);
125789Sahrens 	ASSERT(rc->rc_count >= 0);
126789Sahrens 	if (reference_tracking_enable)
127789Sahrens 		list_insert_head(&rc->rc_list, ref);
128789Sahrens 	rc->rc_count += number;
129789Sahrens 	count = rc->rc_count;
130789Sahrens 	mutex_exit(&rc->rc_mtx);
131789Sahrens 
132789Sahrens 	return (count);
133789Sahrens }
134789Sahrens 
135789Sahrens int64_t
136789Sahrens refcount_add(refcount_t *rc, void *holder)
137789Sahrens {
138789Sahrens 	return (refcount_add_many(rc, 1, holder));
139789Sahrens }
140789Sahrens 
141789Sahrens int64_t
142789Sahrens refcount_remove_many(refcount_t *rc, uint64_t number, void *holder)
143789Sahrens {
144789Sahrens 	reference_t *ref;
145789Sahrens 	int64_t count;
146789Sahrens 
147789Sahrens 	mutex_enter(&rc->rc_mtx);
148789Sahrens 	ASSERT(rc->rc_count >= number);
149789Sahrens 
150789Sahrens 	if (!reference_tracking_enable) {
151789Sahrens 		rc->rc_count -= number;
152789Sahrens 		count = rc->rc_count;
153789Sahrens 		mutex_exit(&rc->rc_mtx);
154789Sahrens 		return (count);
155789Sahrens 	}
156789Sahrens 
157789Sahrens 	for (ref = list_head(&rc->rc_list); ref;
158789Sahrens 	    ref = list_next(&rc->rc_list, ref)) {
159789Sahrens 		if (ref->ref_holder == holder && ref->ref_number == number) {
160789Sahrens 			list_remove(&rc->rc_list, ref);
161789Sahrens 			if (reference_history > 0) {
162789Sahrens 				ref->ref_removed =
163789Sahrens 				    kmem_cache_alloc(reference_history_cache,
164789Sahrens 				    KM_SLEEP);
165789Sahrens 				list_insert_head(&rc->rc_removed, ref);
166789Sahrens 				rc->rc_removed_count++;
167789Sahrens 				if (rc->rc_removed_count >= reference_history) {
168789Sahrens 					ref = list_tail(&rc->rc_removed);
169789Sahrens 					list_remove(&rc->rc_removed, ref);
170789Sahrens 					kmem_cache_free(reference_history_cache,
171789Sahrens 					    ref->ref_removed);
172789Sahrens 					kmem_cache_free(reference_cache, ref);
173789Sahrens 					rc->rc_removed_count--;
174789Sahrens 				}
175789Sahrens 			} else {
176789Sahrens 				kmem_cache_free(reference_cache, ref);
177789Sahrens 			}
178789Sahrens 			rc->rc_count -= number;
179789Sahrens 			count = rc->rc_count;
180789Sahrens 			mutex_exit(&rc->rc_mtx);
181789Sahrens 			return (count);
182789Sahrens 		}
183789Sahrens 	}
184789Sahrens 	panic("No such hold %p on refcount %llx", holder,
185789Sahrens 	    (u_longlong_t)(uintptr_t)rc);
186789Sahrens 	return (-1);
187789Sahrens }
188789Sahrens 
189789Sahrens int64_t
190789Sahrens refcount_remove(refcount_t *rc, void *holder)
191789Sahrens {
192789Sahrens 	return (refcount_remove_many(rc, 1, holder));
193789Sahrens }
194789Sahrens 
195789Sahrens #endif
196