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 2014 Joyent, Inc. All rights reserved.
14 */
15
16 #include <sys/scsi/adapters/mpt_sas/mptsas_hash.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 #ifdef lint
24 extern refhash_link_t *obj_to_link(refhash_t *, void *);
25 extern void *link_to_obj(refhash_t *, refhash_link_t *);
26 extern void *obj_to_tag(refhash_t *, void *);
27 #else
28 #define obj_to_link(_h, _o) \
29 ((refhash_link_t *)(((char *)(_o)) + (_h)->rh_link_off))
30 #define link_to_obj(_h, _l) \
31 ((void *)(((char *)(_l)) - (_h)->rh_link_off))
32 #define obj_to_tag(_h, _o) \
33 ((void *)(((char *)(_o)) + (_h)->rh_tag_off))
34 #endif
35
36 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)37 refhash_create(uint_t bucket_count, refhash_hash_f hash,
38 refhash_cmp_f cmp, refhash_dtor_f dtor, size_t obj_size, size_t link_off,
39 size_t tag_off, int km_flags)
40 {
41 refhash_t *hp;
42 uint_t i;
43
44 hp = kmem_alloc(sizeof (refhash_t), km_flags);
45 if (hp == NULL)
46 return (NULL);
47 hp->rh_buckets = kmem_zalloc(bucket_count * sizeof (list_t), km_flags);
48 if (hp->rh_buckets == NULL) {
49 kmem_free(hp, sizeof (refhash_t));
50 return (NULL);
51 }
52 hp->rh_bucket_count = bucket_count;
53
54 for (i = 0; i < bucket_count; i++) {
55 list_create(&hp->rh_buckets[i], sizeof (refhash_link_t),
56 offsetof(refhash_link_t, rhl_chain_link));
57 }
58 list_create(&hp->rh_objs, sizeof (refhash_link_t),
59 offsetof(refhash_link_t, rhl_global_link));
60
61 hp->rh_obj_size = obj_size;
62 hp->rh_link_off = link_off;
63 hp->rh_tag_off = tag_off;
64 hp->rh_hash = hash;
65 hp->rh_cmp = cmp;
66 hp->rh_dtor = dtor;
67
68 return (hp);
69 }
70
71 void
refhash_destroy(refhash_t * hp)72 refhash_destroy(refhash_t *hp)
73 {
74 ASSERT(list_is_empty(&hp->rh_objs));
75
76 kmem_free(hp->rh_buckets, hp->rh_bucket_count * sizeof (list_t));
77 kmem_free(hp, sizeof (refhash_t));
78 }
79
80 void
refhash_insert(refhash_t * hp,void * op)81 refhash_insert(refhash_t *hp, void *op)
82 {
83 uint_t bucket;
84 refhash_link_t *lp = obj_to_link(hp, op);
85
86 bucket = hp->rh_hash(obj_to_tag(hp, op)) % hp->rh_bucket_count;
87 list_link_init(&lp->rhl_chain_link);
88 list_link_init(&lp->rhl_global_link);
89 lp->rhl_flags = 0;
90 lp->rhl_refcnt = 0;
91 list_insert_tail(&hp->rh_buckets[bucket], lp);
92 list_insert_tail(&hp->rh_objs, lp);
93 }
94
95 static void
refhash_delete(refhash_t * hp,void * op)96 refhash_delete(refhash_t *hp, void *op)
97 {
98 refhash_link_t *lp = obj_to_link(hp, op);
99 uint_t bucket;
100
101 bucket = hp->rh_hash(obj_to_tag(hp, op)) % hp->rh_bucket_count;
102 list_remove(&hp->rh_buckets[bucket], lp);
103 list_remove(&hp->rh_objs, lp);
104 hp->rh_dtor(op);
105 }
106
107 void
refhash_remove(refhash_t * hp,void * op)108 refhash_remove(refhash_t *hp, void *op)
109 {
110 refhash_link_t *lp = obj_to_link(hp, op);
111
112 if (lp->rhl_refcnt > 0) {
113 lp->rhl_flags |= RHL_F_DEAD;
114 } else {
115 refhash_delete(hp, op);
116 }
117 }
118
119 void *
refhash_lookup(refhash_t * hp,const void * tp)120 refhash_lookup(refhash_t *hp, const void *tp)
121 {
122 uint_t bucket;
123 refhash_link_t *lp;
124 void *op;
125
126 bucket = hp->rh_hash(tp) % hp->rh_bucket_count;
127 for (lp = list_head(&hp->rh_buckets[bucket]); lp != NULL;
128 lp = list_next(&hp->rh_buckets[bucket], lp)) {
129 op = link_to_obj(hp, lp);
130 if (hp->rh_cmp(obj_to_tag(hp, op), tp) == 0 &&
131 !(lp->rhl_flags & RHL_F_DEAD)) {
132 return (op);
133 }
134 }
135
136 return (NULL);
137 }
138
139 void *
refhash_linear_search(refhash_t * hp,refhash_eval_f eval,void * arg)140 refhash_linear_search(refhash_t *hp, refhash_eval_f eval, void *arg)
141 {
142 void *op;
143 refhash_link_t *lp;
144
145 for (lp = list_head(&hp->rh_objs); lp != NULL;
146 lp = list_next(&hp->rh_objs, lp)) {
147 op = link_to_obj(hp, lp);
148 if (eval(op, arg) == 0)
149 return (op);
150 }
151
152 return (NULL);
153 }
154
155 void
refhash_hold(refhash_t * hp,void * op)156 refhash_hold(refhash_t *hp, void *op)
157 {
158 refhash_link_t *lp = obj_to_link(hp, op);
159
160 ++lp->rhl_refcnt;
161 }
162
163 void
refhash_rele(refhash_t * hp,void * op)164 refhash_rele(refhash_t *hp, void *op)
165 {
166 refhash_link_t *lp = obj_to_link(hp, op);
167
168 ASSERT(lp->rhl_refcnt > 0);
169
170 if (--lp->rhl_refcnt == 0 && (lp->rhl_flags & RHL_F_DEAD))
171 refhash_remove(hp, op);
172 }
173
174 void *
refhash_first(refhash_t * hp)175 refhash_first(refhash_t *hp)
176 {
177 refhash_link_t *lp;
178
179 lp = list_head(&hp->rh_objs);
180 if (lp == NULL)
181 return (NULL);
182
183 ++lp->rhl_refcnt;
184
185 return (link_to_obj(hp, lp));
186 }
187
188 void *
refhash_next(refhash_t * hp,void * op)189 refhash_next(refhash_t *hp, void *op)
190 {
191 refhash_link_t *lp;
192
193 lp = obj_to_link(hp, op);
194 while ((lp = list_next(&hp->rh_objs, lp)) != NULL) {
195 if (!(lp->rhl_flags & RHL_F_DEAD))
196 break;
197 }
198
199 refhash_rele(hp, op);
200 if (lp == NULL)
201 return (NULL);
202
203 ++lp->rhl_refcnt;
204
205 return (link_to_obj(hp, lp));
206 }
207
208 boolean_t
refhash_obj_valid(refhash_t * hp,const void * op)209 refhash_obj_valid(refhash_t *hp, const void *op)
210 {
211 /* LINTED - E_ARG_INCOMPATIBLE_WITH_ARG_L */
212 const refhash_link_t *lp = obj_to_link(hp, op);
213
214 return ((lp->rhl_flags & RHL_F_DEAD) != 0);
215 }
216