xref: /openbsd-src/usr.sbin/unbound/util/storage/slabhash.c (revision 2bdc0ed15d5bcb82703283d48e5e85ed47cc34d3)
1933707f3Ssthen /*
2933707f3Ssthen  * util/storage/slabhash.c - hashtable consisting of several smaller tables.
3933707f3Ssthen  *
4933707f3Ssthen  * Copyright (c) 2007, NLnet Labs. All rights reserved.
5933707f3Ssthen  *
6933707f3Ssthen  * This software is open source.
7933707f3Ssthen  *
8933707f3Ssthen  * Redistribution and use in source and binary forms, with or without
9933707f3Ssthen  * modification, are permitted provided that the following conditions
10933707f3Ssthen  * are met:
11933707f3Ssthen  *
12933707f3Ssthen  * Redistributions of source code must retain the above copyright notice,
13933707f3Ssthen  * this list of conditions and the following disclaimer.
14933707f3Ssthen  *
15933707f3Ssthen  * Redistributions in binary form must reproduce the above copyright notice,
16933707f3Ssthen  * this list of conditions and the following disclaimer in the documentation
17933707f3Ssthen  * and/or other materials provided with the distribution.
18933707f3Ssthen  *
19933707f3Ssthen  * Neither the name of the NLNET LABS nor the names of its contributors may
20933707f3Ssthen  * be used to endorse or promote products derived from this software without
21933707f3Ssthen  * specific prior written permission.
22933707f3Ssthen  *
23933707f3Ssthen  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
245d76a658Ssthen  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
255d76a658Ssthen  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
265d76a658Ssthen  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
275d76a658Ssthen  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
285d76a658Ssthen  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
295d76a658Ssthen  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
305d76a658Ssthen  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
315d76a658Ssthen  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
325d76a658Ssthen  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
335d76a658Ssthen  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34933707f3Ssthen  */
35933707f3Ssthen 
36933707f3Ssthen /**
37933707f3Ssthen  * \file
38933707f3Ssthen  *
39933707f3Ssthen  * Implementation of hash table that consists of smaller hash tables.
40933707f3Ssthen  * This results in a partitioned lruhash table.
41933707f3Ssthen  * It cannot grow, but that gives it the ability to have multiple
42933707f3Ssthen  * locks. Also this means there are multiple LRU lists.
43933707f3Ssthen  */
44933707f3Ssthen 
45933707f3Ssthen #include "config.h"
46933707f3Ssthen #include "util/storage/slabhash.h"
47933707f3Ssthen 
slabhash_create(size_t numtables,size_t start_size,size_t maxmem,lruhash_sizefunc_type sizefunc,lruhash_compfunc_type compfunc,lruhash_delkeyfunc_type delkeyfunc,lruhash_deldatafunc_type deldatafunc,void * arg)48933707f3Ssthen struct slabhash* slabhash_create(size_t numtables, size_t start_size,
4977079be7Ssthen 	size_t maxmem, lruhash_sizefunc_type sizefunc,
5077079be7Ssthen 	lruhash_compfunc_type compfunc, lruhash_delkeyfunc_type delkeyfunc,
5177079be7Ssthen 	lruhash_deldatafunc_type deldatafunc, void* arg)
52933707f3Ssthen {
53933707f3Ssthen 	size_t i;
54933707f3Ssthen 	struct slabhash* sl = (struct slabhash*)calloc(1,
55933707f3Ssthen 		sizeof(struct slabhash));
56933707f3Ssthen 	if(!sl) return NULL;
57933707f3Ssthen 	sl->size = numtables;
58933707f3Ssthen 	log_assert(sl->size > 0);
59933707f3Ssthen 	sl->array = (struct lruhash**)calloc(sl->size, sizeof(struct lruhash*));
60933707f3Ssthen 	if(!sl->array) {
61933707f3Ssthen 		free(sl);
62933707f3Ssthen 		return NULL;
63933707f3Ssthen 	}
64933707f3Ssthen 	sl->mask = (uint32_t)(sl->size - 1);
65933707f3Ssthen 	if(sl->mask == 0) {
66933707f3Ssthen 		sl->shift = 0;
67933707f3Ssthen 	} else {
68933707f3Ssthen 		log_assert( (sl->size & sl->mask) == 0
69933707f3Ssthen 			/* size must be power of 2 */ );
70933707f3Ssthen 		sl->shift = 0;
71933707f3Ssthen 		while(!(sl->mask & 0x80000000)) {
72933707f3Ssthen 			sl->mask <<= 1;
73933707f3Ssthen 			sl->shift ++;
74933707f3Ssthen 		}
75933707f3Ssthen 	}
76933707f3Ssthen 	for(i=0; i<sl->size; i++) {
77933707f3Ssthen 		sl->array[i] = lruhash_create(start_size, maxmem / sl->size,
78933707f3Ssthen 			sizefunc, compfunc, delkeyfunc, deldatafunc, arg);
79933707f3Ssthen 		if(!sl->array[i]) {
80933707f3Ssthen 			slabhash_delete(sl);
81933707f3Ssthen 			return NULL;
82933707f3Ssthen 		}
83933707f3Ssthen 	}
84933707f3Ssthen 	return sl;
85933707f3Ssthen }
86933707f3Ssthen 
slabhash_delete(struct slabhash * sl)87933707f3Ssthen void slabhash_delete(struct slabhash* sl)
88933707f3Ssthen {
89933707f3Ssthen 	if(!sl)
90933707f3Ssthen 		return;
91933707f3Ssthen 	if(sl->array) {
92933707f3Ssthen 		size_t i;
93933707f3Ssthen 		for(i=0; i<sl->size; i++)
94933707f3Ssthen 			lruhash_delete(sl->array[i]);
95933707f3Ssthen 		free(sl->array);
96933707f3Ssthen 	}
97933707f3Ssthen 	free(sl);
98933707f3Ssthen }
99933707f3Ssthen 
slabhash_clear(struct slabhash * sl)100933707f3Ssthen void slabhash_clear(struct slabhash* sl)
101933707f3Ssthen {
102933707f3Ssthen 	size_t i;
103933707f3Ssthen 	if(!sl)
104933707f3Ssthen 		return;
105933707f3Ssthen 	for(i=0; i<sl->size; i++)
106933707f3Ssthen 		lruhash_clear(sl->array[i]);
107933707f3Ssthen }
108933707f3Ssthen 
109933707f3Ssthen /** helper routine to calculate the slabhash index */
110933707f3Ssthen static unsigned int
slab_idx(struct slabhash * sl,hashvalue_type hash)11177079be7Ssthen slab_idx(struct slabhash* sl, hashvalue_type hash)
112933707f3Ssthen {
113933707f3Ssthen 	return ((hash & sl->mask) >> sl->shift);
114933707f3Ssthen }
115933707f3Ssthen 
slabhash_insert(struct slabhash * sl,hashvalue_type hash,struct lruhash_entry * entry,void * data,void * arg)11677079be7Ssthen void slabhash_insert(struct slabhash* sl, hashvalue_type hash,
117933707f3Ssthen 	struct lruhash_entry* entry, void* data, void* arg)
118933707f3Ssthen {
119933707f3Ssthen 	lruhash_insert(sl->array[slab_idx(sl, hash)], hash, entry, data, arg);
120933707f3Ssthen }
121933707f3Ssthen 
slabhash_lookup(struct slabhash * sl,hashvalue_type hash,void * key,int wr)122933707f3Ssthen struct lruhash_entry* slabhash_lookup(struct slabhash* sl,
12377079be7Ssthen 	hashvalue_type hash, void* key, int wr)
124933707f3Ssthen {
125933707f3Ssthen 	return lruhash_lookup(sl->array[slab_idx(sl, hash)], hash, key, wr);
126933707f3Ssthen }
127933707f3Ssthen 
slabhash_remove(struct slabhash * sl,hashvalue_type hash,void * key)12877079be7Ssthen void slabhash_remove(struct slabhash* sl, hashvalue_type hash, void* key)
129933707f3Ssthen {
130933707f3Ssthen 	lruhash_remove(sl->array[slab_idx(sl, hash)], hash, key);
131933707f3Ssthen }
132933707f3Ssthen 
slabhash_status(struct slabhash * sl,const char * id,int extended)133933707f3Ssthen void slabhash_status(struct slabhash* sl, const char* id, int extended)
134933707f3Ssthen {
135933707f3Ssthen 	size_t i;
136933707f3Ssthen 	char num[17];
137933707f3Ssthen 	log_info("Slabhash %s: %u tables mask=%x shift=%d",
138933707f3Ssthen 		id, (unsigned)sl->size, (unsigned)sl->mask, sl->shift);
139933707f3Ssthen 	for(i=0; i<sl->size; i++) {
140933707f3Ssthen 		snprintf(num, sizeof(num), "table %u", (unsigned)i);
141933707f3Ssthen 		lruhash_status(sl->array[i], num, extended);
142933707f3Ssthen 	}
143933707f3Ssthen }
144933707f3Ssthen 
slabhash_get_size(struct slabhash * sl)145933707f3Ssthen size_t slabhash_get_size(struct slabhash* sl)
146933707f3Ssthen {
147933707f3Ssthen 	size_t i, total = 0;
148933707f3Ssthen 	for(i=0; i<sl->size; i++) {
149933707f3Ssthen 		lock_quick_lock(&sl->array[i]->lock);
150933707f3Ssthen 		total += sl->array[i]->space_max;
151933707f3Ssthen 		lock_quick_unlock(&sl->array[i]->lock);
152933707f3Ssthen 	}
153933707f3Ssthen 	return total;
154933707f3Ssthen }
155933707f3Ssthen 
slabhash_is_size(struct slabhash * sl,size_t size,size_t slabs)1562308e98cSsthen int slabhash_is_size(struct slabhash* sl, size_t size, size_t slabs)
1572308e98cSsthen {
1582308e98cSsthen 	/* divide by slabs and then multiply by the number of slabs,
1592308e98cSsthen 	 * because if the size is not an even multiple of slabs, the
1602308e98cSsthen 	 * uneven amount needs to be removed for comparison */
1612308e98cSsthen 	if(!sl) return 0;
1622308e98cSsthen 	if(sl->size != slabs) return 0;
1632308e98cSsthen 	if(slabs == 0) return 0;
1642308e98cSsthen 	if( (size/slabs)*slabs == slabhash_get_size(sl))
1652308e98cSsthen 		return 1;
1662308e98cSsthen 	return 0;
1672308e98cSsthen }
1682308e98cSsthen 
slabhash_update_space_used(struct slabhash * sl,hashvalue_type hash,void * cb_arg,int diff_size)169*2bdc0ed1Ssthen void slabhash_update_space_used(struct slabhash* sl, hashvalue_type hash,
170*2bdc0ed1Ssthen 	void* cb_arg, int diff_size)
171*2bdc0ed1Ssthen {
172*2bdc0ed1Ssthen 	lruhash_update_space_used(sl->array[slab_idx(sl, hash)], cb_arg,
173*2bdc0ed1Ssthen 		diff_size);
174*2bdc0ed1Ssthen }
175*2bdc0ed1Ssthen 
slabhash_get_mem(struct slabhash * sl)176933707f3Ssthen size_t slabhash_get_mem(struct slabhash* sl)
177933707f3Ssthen {
178933707f3Ssthen 	size_t i, total = sizeof(*sl);
179933707f3Ssthen 	total += sizeof(struct lruhash*)*sl->size;
180933707f3Ssthen 	for(i=0; i<sl->size; i++) {
181933707f3Ssthen 		total += lruhash_get_mem(sl->array[i]);
182933707f3Ssthen 	}
183933707f3Ssthen 	return total;
184933707f3Ssthen }
185933707f3Ssthen 
slabhash_gettable(struct slabhash * sl,hashvalue_type hash)18677079be7Ssthen struct lruhash* slabhash_gettable(struct slabhash* sl, hashvalue_type hash)
187933707f3Ssthen {
188933707f3Ssthen 	return sl->array[slab_idx(sl, hash)];
189933707f3Ssthen }
190933707f3Ssthen 
191933707f3Ssthen /* test code, here to avoid linking problems with fptr_wlist */
192933707f3Ssthen /** delete key */
delkey(struct slabhash_testkey * k)193933707f3Ssthen static void delkey(struct slabhash_testkey* k) {
194933707f3Ssthen 	lock_rw_destroy(&k->entry.lock); free(k);}
195933707f3Ssthen /** delete data */
deldata(struct slabhash_testdata * d)196933707f3Ssthen static void deldata(struct slabhash_testdata* d) {free(d);}
197933707f3Ssthen 
test_slabhash_sizefunc(void * ATTR_UNUSED (key),void * ATTR_UNUSED (data))198933707f3Ssthen size_t test_slabhash_sizefunc(void* ATTR_UNUSED(key), void* ATTR_UNUSED(data))
199933707f3Ssthen {
200933707f3Ssthen 	return sizeof(struct slabhash_testkey) +
201933707f3Ssthen 		sizeof(struct slabhash_testdata);
202933707f3Ssthen }
203933707f3Ssthen 
test_slabhash_compfunc(void * key1,void * key2)204933707f3Ssthen int test_slabhash_compfunc(void* key1, void* key2)
205933707f3Ssthen {
206933707f3Ssthen 	struct slabhash_testkey* k1 = (struct slabhash_testkey*)key1;
207933707f3Ssthen 	struct slabhash_testkey* k2 = (struct slabhash_testkey*)key2;
208933707f3Ssthen 	if(k1->id == k2->id)
209933707f3Ssthen 		return 0;
210933707f3Ssthen 	if(k1->id > k2->id)
211933707f3Ssthen 		return 1;
212933707f3Ssthen 	return -1;
213933707f3Ssthen }
214933707f3Ssthen 
test_slabhash_delkey(void * key,void * ATTR_UNUSED (arg))215933707f3Ssthen void test_slabhash_delkey(void* key, void* ATTR_UNUSED(arg))
216933707f3Ssthen {
217933707f3Ssthen 	delkey((struct slabhash_testkey*)key);
218933707f3Ssthen }
219933707f3Ssthen 
test_slabhash_deldata(void * data,void * ATTR_UNUSED (arg))220933707f3Ssthen void test_slabhash_deldata(void* data, void* ATTR_UNUSED(arg))
221933707f3Ssthen {
222933707f3Ssthen 	deldata((struct slabhash_testdata*)data);
223933707f3Ssthen }
224933707f3Ssthen 
slabhash_setmarkdel(struct slabhash * sl,lruhash_markdelfunc_type md)22577079be7Ssthen void slabhash_setmarkdel(struct slabhash* sl, lruhash_markdelfunc_type md)
226933707f3Ssthen {
227933707f3Ssthen 	size_t i;
228933707f3Ssthen 	for(i=0; i<sl->size; i++) {
229933707f3Ssthen 		lruhash_setmarkdel(sl->array[i], md);
230933707f3Ssthen 	}
231933707f3Ssthen }
232933707f3Ssthen 
slabhash_traverse(struct slabhash * sh,int wr,void (* func)(struct lruhash_entry *,void *),void * arg)233933707f3Ssthen void slabhash_traverse(struct slabhash* sh, int wr,
234933707f3Ssthen 	void (*func)(struct lruhash_entry*, void*), void* arg)
235933707f3Ssthen {
236933707f3Ssthen 	size_t i;
237933707f3Ssthen 	for(i=0; i<sh->size; i++)
238933707f3Ssthen 		lruhash_traverse(sh->array[i], wr, func, arg);
23998f3ca02Sbrad }
24098f3ca02Sbrad 
count_slabhash_entries(struct slabhash * sh)24198f3ca02Sbrad size_t count_slabhash_entries(struct slabhash* sh)
24298f3ca02Sbrad {
24398f3ca02Sbrad 	size_t slab, cnt = 0;
24498f3ca02Sbrad 
24598f3ca02Sbrad 	for(slab=0; slab<sh->size; slab++) {
24698f3ca02Sbrad 		lock_quick_lock(&sh->array[slab]->lock);
24798f3ca02Sbrad 		cnt += sh->array[slab]->num;
24898f3ca02Sbrad 		lock_quick_unlock(&sh->array[slab]->lock);
24998f3ca02Sbrad 	}
25098f3ca02Sbrad 	return cnt;
251933707f3Ssthen }
2528b7325afSsthen 
get_slabhash_stats(struct slabhash * sh,long long * num,long long * collisions)2538b7325afSsthen void get_slabhash_stats(struct slabhash* sh, long long* num, long long* collisions)
2548b7325afSsthen {
2558b7325afSsthen 	size_t slab, cnt = 0, max_collisions = 0;
2568b7325afSsthen 
2578b7325afSsthen 	for(slab=0; slab<sh->size; slab++) {
2588b7325afSsthen 		lock_quick_lock(&sh->array[slab]->lock);
2598b7325afSsthen 		cnt += sh->array[slab]->num;
2608b7325afSsthen 		if (max_collisions < sh->array[slab]->max_collisions) {
2618b7325afSsthen 			max_collisions = sh->array[slab]->max_collisions;
2628b7325afSsthen 		}
2638b7325afSsthen 		lock_quick_unlock(&sh->array[slab]->lock);
2648b7325afSsthen 	}
2658b7325afSsthen 	if (num != NULL)
2668b7325afSsthen 		*num = cnt;
2678b7325afSsthen 	if (collisions != NULL)
2688b7325afSsthen 		*collisions = max_collisions;
2698b7325afSsthen }
270