/src/unbound/util/storage/slabhash.c
Line | Count | Source |
1 | | /* |
2 | | * util/storage/slabhash.c - hashtable consisting of several smaller tables. |
3 | | * |
4 | | * Copyright (c) 2007, NLnet Labs. All rights reserved. |
5 | | * |
6 | | * This software is open source. |
7 | | * |
8 | | * Redistribution and use in source and binary forms, with or without |
9 | | * modification, are permitted provided that the following conditions |
10 | | * are met: |
11 | | * |
12 | | * Redistributions of source code must retain the above copyright notice, |
13 | | * this list of conditions and the following disclaimer. |
14 | | * |
15 | | * Redistributions in binary form must reproduce the above copyright notice, |
16 | | * this list of conditions and the following disclaimer in the documentation |
17 | | * and/or other materials provided with the distribution. |
18 | | * |
19 | | * Neither the name of the NLNET LABS nor the names of its contributors may |
20 | | * be used to endorse or promote products derived from this software without |
21 | | * specific prior written permission. |
22 | | * |
23 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
24 | | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
25 | | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
26 | | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
27 | | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
28 | | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED |
29 | | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
30 | | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
31 | | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
32 | | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
33 | | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
34 | | */ |
35 | | |
36 | | /** |
37 | | * \file |
38 | | * |
39 | | * Implementation of hash table that consists of smaller hash tables. |
40 | | * This results in a partitioned lruhash table. |
41 | | * It cannot grow, but that gives it the ability to have multiple |
42 | | * locks. Also this means there are multiple LRU lists. |
43 | | */ |
44 | | |
45 | | #include "config.h" |
46 | | #include "util/storage/slabhash.h" |
47 | | |
48 | | struct slabhash* slabhash_create(size_t numtables, size_t start_size, |
49 | | size_t maxmem, lruhash_sizefunc_type sizefunc, |
50 | | lruhash_compfunc_type compfunc, lruhash_delkeyfunc_type delkeyfunc, |
51 | | lruhash_deldatafunc_type deldatafunc, void* arg) |
52 | 4.00k | { |
53 | 4.00k | size_t i; |
54 | 4.00k | struct slabhash* sl = (struct slabhash*)calloc(1, |
55 | 4.00k | sizeof(struct slabhash)); |
56 | 4.00k | if(!sl) return NULL; |
57 | 4.00k | sl->size = numtables; |
58 | 4.00k | log_assert(sl->size > 0); |
59 | 4.00k | sl->array = (struct lruhash**)calloc(sl->size, sizeof(struct lruhash*)); |
60 | 4.00k | if(!sl->array) { |
61 | 0 | free(sl); |
62 | 0 | return NULL; |
63 | 0 | } |
64 | 4.00k | sl->mask = (uint32_t)(sl->size - 1); |
65 | 4.00k | if(sl->mask == 0) { |
66 | 0 | sl->shift = 0; |
67 | 4.00k | } else { |
68 | 4.00k | log_assert( (sl->size & sl->mask) == 0 |
69 | 4.00k | /* size must be power of 2 */ ); |
70 | 4.00k | sl->shift = 0; |
71 | 124k | while(!(sl->mask & 0x80000000)) { |
72 | 120k | sl->mask <<= 1; |
73 | 120k | sl->shift ++; |
74 | 120k | } |
75 | 4.00k | } |
76 | 20.0k | for(i=0; i<sl->size; i++) { |
77 | 16.0k | sl->array[i] = lruhash_create(start_size, maxmem / sl->size, |
78 | 16.0k | sizefunc, compfunc, delkeyfunc, deldatafunc, arg); |
79 | 16.0k | if(!sl->array[i]) { |
80 | 0 | slabhash_delete(sl); |
81 | 0 | return NULL; |
82 | 0 | } |
83 | 16.0k | } |
84 | 4.00k | return sl; |
85 | 4.00k | } |
86 | | |
87 | | void slabhash_delete(struct slabhash* sl) |
88 | 4.00k | { |
89 | 4.00k | if(!sl) |
90 | 0 | return; |
91 | 4.00k | if(sl->array) { |
92 | 4.00k | size_t i; |
93 | 20.0k | for(i=0; i<sl->size; i++) |
94 | 16.0k | lruhash_delete(sl->array[i]); |
95 | 4.00k | free(sl->array); |
96 | 4.00k | } |
97 | 4.00k | free(sl); |
98 | 4.00k | } |
99 | | |
100 | | void slabhash_clear(struct slabhash* sl) |
101 | 0 | { |
102 | 0 | size_t i; |
103 | 0 | if(!sl) |
104 | 0 | return; |
105 | 0 | for(i=0; i<sl->size; i++) |
106 | 0 | lruhash_clear(sl->array[i]); |
107 | 0 | } |
108 | | |
109 | | /** helper routine to calculate the slabhash index */ |
110 | | static unsigned int |
111 | | slab_idx(struct slabhash* sl, hashvalue_type hash) |
112 | 18.2k | { |
113 | 18.2k | return ((hash & sl->mask) >> sl->shift); |
114 | 18.2k | } |
115 | | |
116 | | void slabhash_insert(struct slabhash* sl, hashvalue_type hash, |
117 | | struct lruhash_entry* entry, void* data, void* arg) |
118 | 9.12k | { |
119 | 9.12k | lruhash_insert(sl->array[slab_idx(sl, hash)], hash, entry, data, arg); |
120 | 9.12k | } |
121 | | |
122 | | struct lruhash_entry* slabhash_lookup(struct slabhash* sl, |
123 | | hashvalue_type hash, void* key, int wr) |
124 | 9.12k | { |
125 | 9.12k | return lruhash_lookup(sl->array[slab_idx(sl, hash)], hash, key, wr); |
126 | 9.12k | } |
127 | | |
128 | | void slabhash_remove(struct slabhash* sl, hashvalue_type hash, void* key) |
129 | 0 | { |
130 | 0 | lruhash_remove(sl->array[slab_idx(sl, hash)], hash, key); |
131 | 0 | } |
132 | | |
133 | | void slabhash_status(struct slabhash* sl, const char* id, int extended) |
134 | 0 | { |
135 | 0 | size_t i; |
136 | 0 | char num[17]; |
137 | 0 | log_info("Slabhash %s: %u tables mask=%x shift=%d", |
138 | 0 | id, (unsigned)sl->size, (unsigned)sl->mask, sl->shift); |
139 | 0 | for(i=0; i<sl->size; i++) { |
140 | 0 | snprintf(num, sizeof(num), "table %u", (unsigned)i); |
141 | 0 | lruhash_status(sl->array[i], num, extended); |
142 | 0 | } |
143 | 0 | } |
144 | | |
145 | | size_t slabhash_get_size(struct slabhash* sl) |
146 | 0 | { |
147 | 0 | size_t i, total = 0; |
148 | 0 | for(i=0; i<sl->size; i++) { |
149 | 0 | lock_quick_lock(&sl->array[i]->lock); |
150 | 0 | total += sl->array[i]->space_max; |
151 | 0 | lock_quick_unlock(&sl->array[i]->lock); |
152 | 0 | } |
153 | 0 | return total; |
154 | 0 | } |
155 | | |
156 | | int slabhash_is_size(struct slabhash* sl, size_t size, size_t slabs) |
157 | 0 | { |
158 | | /* divide by slabs and then multiply by the number of slabs, |
159 | | * because if the size is not an even multiple of slabs, the |
160 | | * uneven amount needs to be removed for comparison */ |
161 | 0 | if(!sl) return 0; |
162 | 0 | if(sl->size != slabs) return 0; |
163 | 0 | if(slabs == 0) return 0; |
164 | 0 | if( (size/slabs)*slabs == slabhash_get_size(sl)) |
165 | 0 | return 1; |
166 | 0 | return 0; |
167 | 0 | } |
168 | | |
169 | | void slabhash_update_space_used(struct slabhash* sl, hashvalue_type hash, |
170 | | void* cb_arg, int diff_size) |
171 | 0 | { |
172 | 0 | lruhash_update_space_used(sl->array[slab_idx(sl, hash)], cb_arg, |
173 | 0 | diff_size); |
174 | 0 | } |
175 | | |
176 | | size_t slabhash_get_mem(struct slabhash* sl) |
177 | 0 | { |
178 | 0 | size_t i, total = sizeof(*sl); |
179 | 0 | total += sizeof(struct lruhash*)*sl->size; |
180 | 0 | for(i=0; i<sl->size; i++) { |
181 | 0 | total += lruhash_get_mem(sl->array[i]); |
182 | 0 | } |
183 | 0 | return total; |
184 | 0 | } |
185 | | |
186 | | struct lruhash* slabhash_gettable(struct slabhash* sl, hashvalue_type hash) |
187 | 0 | { |
188 | 0 | return sl->array[slab_idx(sl, hash)]; |
189 | 0 | } |
190 | | |
191 | | /* test code, here to avoid linking problems with fptr_wlist */ |
192 | | /** delete key */ |
193 | 0 | static void delkey(struct slabhash_testkey* k) { |
194 | 0 | lock_rw_destroy(&k->entry.lock); free(k);} |
195 | | /** delete data */ |
196 | 0 | static void deldata(struct slabhash_testdata* d) {free(d);} |
197 | | |
198 | | size_t test_slabhash_sizefunc(void* ATTR_UNUSED(key), void* ATTR_UNUSED(data)) |
199 | 0 | { |
200 | 0 | return sizeof(struct slabhash_testkey) + |
201 | 0 | sizeof(struct slabhash_testdata); |
202 | 0 | } |
203 | | |
204 | | int test_slabhash_compfunc(void* key1, void* key2) |
205 | 0 | { |
206 | 0 | struct slabhash_testkey* k1 = (struct slabhash_testkey*)key1; |
207 | 0 | struct slabhash_testkey* k2 = (struct slabhash_testkey*)key2; |
208 | 0 | if(k1->id == k2->id) |
209 | 0 | return 0; |
210 | 0 | if(k1->id > k2->id) |
211 | 0 | return 1; |
212 | 0 | return -1; |
213 | 0 | } |
214 | | |
215 | | void test_slabhash_delkey(void* key, void* ATTR_UNUSED(arg)) |
216 | 0 | { |
217 | 0 | delkey((struct slabhash_testkey*)key); |
218 | 0 | } |
219 | | |
220 | | void test_slabhash_deldata(void* data, void* ATTR_UNUSED(arg)) |
221 | 0 | { |
222 | 0 | deldata((struct slabhash_testdata*)data); |
223 | 0 | } |
224 | | |
225 | | void slabhash_setmarkdel(struct slabhash* sl, lruhash_markdelfunc_type md) |
226 | 4.00k | { |
227 | 4.00k | size_t i; |
228 | 20.0k | for(i=0; i<sl->size; i++) { |
229 | 16.0k | lruhash_setmarkdel(sl->array[i], md); |
230 | 16.0k | } |
231 | 4.00k | } |
232 | | |
233 | | void slabhash_traverse(struct slabhash* sh, int wr, |
234 | | void (*func)(struct lruhash_entry*, void*), void* arg) |
235 | 0 | { |
236 | 0 | size_t i; |
237 | 0 | for(i=0; i<sh->size; i++) |
238 | 0 | lruhash_traverse(sh->array[i], wr, func, arg); |
239 | 0 | } |
240 | | |
241 | | size_t count_slabhash_entries(struct slabhash* sh) |
242 | 0 | { |
243 | 0 | size_t slab, cnt = 0; |
244 | |
|
245 | 0 | for(slab=0; slab<sh->size; slab++) { |
246 | 0 | lock_quick_lock(&sh->array[slab]->lock); |
247 | 0 | cnt += sh->array[slab]->num; |
248 | 0 | lock_quick_unlock(&sh->array[slab]->lock); |
249 | 0 | } |
250 | 0 | return cnt; |
251 | 0 | } |
252 | | |
253 | | void get_slabhash_stats(struct slabhash* sh, long long* num, long long* collisions) |
254 | 0 | { |
255 | 0 | size_t slab, cnt = 0, max_collisions = 0; |
256 | |
|
257 | 0 | for(slab=0; slab<sh->size; slab++) { |
258 | 0 | lock_quick_lock(&sh->array[slab]->lock); |
259 | 0 | cnt += sh->array[slab]->num; |
260 | 0 | if (max_collisions < sh->array[slab]->max_collisions) { |
261 | 0 | max_collisions = sh->array[slab]->max_collisions; |
262 | 0 | } |
263 | 0 | lock_quick_unlock(&sh->array[slab]->lock); |
264 | 0 | } |
265 | 0 | if (num != NULL) |
266 | 0 | *num = cnt; |
267 | 0 | if (collisions != NULL) |
268 | 0 | *collisions = max_collisions; |
269 | 0 | } |
270 | | |
271 | | void slabhash_adjust_size(struct slabhash* sl, size_t max) |
272 | 0 | { |
273 | 0 | size_t space_max = max / sl->size; |
274 | 0 | size_t i; |
275 | 0 | for(i=0; i<sl->size; i++) { |
276 | | lruhash_update_space_max(sl->array[i], NULL, space_max); |
277 | 0 | } |
278 | 0 | } |