1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2015 Joyent, Inc.
14 */
15
16 #include <sys/refhash.h>
17 #include <sys/sysmacros.h>
18 #include <sys/types.h>
19 #include <sys/kmem.h>
20 #include <sys/list.h>
21 #include <sys/ddi.h>
22
23 #define obj_to_link(_h, _o) \
24 ((refhash_link_t *)(((char *)(_o)) + (_h)->rh_link_off))
25 #define link_to_obj(_h, _l) \
26 ((void *)(((char *)(_l)) - (_h)->rh_link_off))
27 #define obj_to_tag(_h, _o) \
28 ((void *)(((char *)(_o)) + (_h)->rh_tag_off))
29
30 refhash_t *
refhash_create(uint_t bucket_count,refhash_hash_f hash,refhash_cmp_f cmp,refhash_dtor_f dtor,size_t obj_size,size_t link_off,size_t tag_off,int km_flags)31 refhash_create(uint_t bucket_count, refhash_hash_f hash,
32 refhash_cmp_f cmp, refhash_dtor_f dtor, size_t obj_size, size_t link_off,
33 size_t tag_off, int km_flags)
34 {
35 refhash_t *hp;
36 uint_t i;
37
38 hp = kmem_alloc(sizeof (refhash_t), km_flags);
39 if (hp == NULL)
40 return (NULL);
41 hp->rh_buckets = kmem_zalloc(bucket_count * sizeof (list_t), km_flags);
42 if (hp->rh_buckets == NULL) {
43 kmem_free(hp, sizeof (refhash_t));
44 return (NULL);
45 }
46 hp->rh_bucket_count = bucket_count;
47
48 for (i = 0; i < bucket_count; i++) {
49 list_create(&hp->rh_buckets[i], sizeof (refhash_link_t),
50 offsetof(refhash_link_t, rhl_chain_link));
51 }
52 list_create(&hp->rh_objs, sizeof (refhash_link_t),
53 offsetof(refhash_link_t, rhl_global_link));
54
55 hp->rh_obj_size = obj_size;
56 hp->rh_link_off = link_off;
57 hp->rh_tag_off = tag_off;
58 hp->rh_hash = hash;
59 hp->rh_cmp = cmp;
60 hp->rh_dtor = dtor;
61
62 return (hp);
63 }
64
65 void
refhash_destroy(refhash_t * hp)66 refhash_destroy(refhash_t *hp)
67 {
68 ASSERT(list_is_empty(&hp->rh_objs));
69
70 kmem_free(hp->rh_buckets, hp->rh_bucket_count * sizeof (list_t));
71 kmem_free(hp, sizeof (refhash_t));
72 }
73
74 void
refhash_insert(refhash_t * hp,void * op)75 refhash_insert(refhash_t *hp, void *op)
76 {
77 uint_t bucket;
78 refhash_link_t *lp = obj_to_link(hp, op);
79
80 bucket = hp->rh_hash(obj_to_tag(hp, op)) % hp->rh_bucket_count;
81 list_link_init(&lp->rhl_chain_link);
82 list_link_init(&lp->rhl_global_link);
83 lp->rhl_flags = 0;
84 lp->rhl_refcnt = 0;
85 list_insert_tail(&hp->rh_buckets[bucket], lp);
86 list_insert_tail(&hp->rh_objs, lp);
87 }
88
89 static void
refhash_delete(refhash_t * hp,void * op)90 refhash_delete(refhash_t *hp, void *op)
91 {
92 refhash_link_t *lp = obj_to_link(hp, op);
93 uint_t bucket;
94
95 bucket = hp->rh_hash(obj_to_tag(hp, op)) % hp->rh_bucket_count;
96 list_remove(&hp->rh_buckets[bucket], lp);
97 list_remove(&hp->rh_objs, lp);
98 hp->rh_dtor(op);
99 }
100
101 void
refhash_remove(refhash_t * hp,void * op)102 refhash_remove(refhash_t *hp, void *op)
103 {
104 refhash_link_t *lp = obj_to_link(hp, op);
105
106 if (lp->rhl_refcnt > 0) {
107 lp->rhl_flags |= RHL_F_DEAD;
108 } else {
109 refhash_delete(hp, op);
110 }
111 }
112
113 void *
refhash_lookup(refhash_t * hp,const void * tp)114 refhash_lookup(refhash_t *hp, const void *tp)
115 {
116 uint_t bucket;
117 refhash_link_t *lp;
118 void *op;
119
120 bucket = hp->rh_hash(tp) % hp->rh_bucket_count;
121 for (lp = list_head(&hp->rh_buckets[bucket]); lp != NULL;
122 lp = list_next(&hp->rh_buckets[bucket], lp)) {
123 op = link_to_obj(hp, lp);
124 if (hp->rh_cmp(obj_to_tag(hp, op), tp) == 0 &&
125 !(lp->rhl_flags & RHL_F_DEAD)) {
126 return (op);
127 }
128 }
129
130 return (NULL);
131 }
132
133 void *
refhash_linear_search(refhash_t * hp,refhash_eval_f eval,void * arg)134 refhash_linear_search(refhash_t *hp, refhash_eval_f eval, void *arg)
135 {
136 void *op;
137 refhash_link_t *lp;
138
139 for (lp = list_head(&hp->rh_objs); lp != NULL;
140 lp = list_next(&hp->rh_objs, lp)) {
141 op = link_to_obj(hp, lp);
142 if (eval(op, arg) == 0)
143 return (op);
144 }
145
146 return (NULL);
147 }
148
149 void
refhash_hold(refhash_t * hp,void * op)150 refhash_hold(refhash_t *hp, void *op)
151 {
152 refhash_link_t *lp = obj_to_link(hp, op);
153
154 ++lp->rhl_refcnt;
155 }
156
157 void
refhash_rele(refhash_t * hp,void * op)158 refhash_rele(refhash_t *hp, void *op)
159 {
160 refhash_link_t *lp = obj_to_link(hp, op);
161
162 ASSERT(lp->rhl_refcnt > 0);
163
164 if (--lp->rhl_refcnt == 0 && (lp->rhl_flags & RHL_F_DEAD))
165 refhash_remove(hp, op);
166 }
167
168 void *
refhash_first(refhash_t * hp)169 refhash_first(refhash_t *hp)
170 {
171 refhash_link_t *lp;
172
173 lp = list_head(&hp->rh_objs);
174 if (lp == NULL)
175 return (NULL);
176
177 ++lp->rhl_refcnt;
178
179 return (link_to_obj(hp, lp));
180 }
181
182 void *
refhash_next(refhash_t * hp,void * op)183 refhash_next(refhash_t *hp, void *op)
184 {
185 refhash_link_t *lp;
186
187 lp = obj_to_link(hp, op);
188 while ((lp = list_next(&hp->rh_objs, lp)) != NULL) {
189 if (!(lp->rhl_flags & RHL_F_DEAD))
190 break;
191 }
192
193 refhash_rele(hp, op);
194 if (lp == NULL)
195 return (NULL);
196
197 ++lp->rhl_refcnt;
198
199 return (link_to_obj(hp, lp));
200 }
201
202 boolean_t
refhash_obj_valid(refhash_t * hp,const void * op)203 refhash_obj_valid(refhash_t *hp, const void *op)
204 {
205 const refhash_link_t *lp = obj_to_link(hp, op);
206
207 return ((lp->rhl_flags & RHL_F_DEAD) != 0);
208 }
209