xref: /freebsd/contrib/unbound/util/storage/lruhash.c (revision be771a7b7f4580a30d99e41a5bb1b93a385a119d)
1b7579f77SDag-Erling Smørgrav /*
2b7579f77SDag-Erling Smørgrav  * util/storage/lruhash.c - hashtable, hash function, LRU keeping.
3b7579f77SDag-Erling Smørgrav  *
4b7579f77SDag-Erling Smørgrav  * Copyright (c) 2007, NLnet Labs. All rights reserved.
5b7579f77SDag-Erling Smørgrav  *
6b7579f77SDag-Erling Smørgrav  * This software is open source.
7b7579f77SDag-Erling Smørgrav  *
8b7579f77SDag-Erling Smørgrav  * Redistribution and use in source and binary forms, with or without
9b7579f77SDag-Erling Smørgrav  * modification, are permitted provided that the following conditions
10b7579f77SDag-Erling Smørgrav  * are met:
11b7579f77SDag-Erling Smørgrav  *
12b7579f77SDag-Erling Smørgrav  * Redistributions of source code must retain the above copyright notice,
13b7579f77SDag-Erling Smørgrav  * this list of conditions and the following disclaimer.
14b7579f77SDag-Erling Smørgrav  *
15b7579f77SDag-Erling Smørgrav  * Redistributions in binary form must reproduce the above copyright notice,
16b7579f77SDag-Erling Smørgrav  * this list of conditions and the following disclaimer in the documentation
17b7579f77SDag-Erling Smørgrav  * and/or other materials provided with the distribution.
18b7579f77SDag-Erling Smørgrav  *
19b7579f77SDag-Erling Smørgrav  * Neither the name of the NLNET LABS nor the names of its contributors may
20b7579f77SDag-Erling Smørgrav  * be used to endorse or promote products derived from this software without
21b7579f77SDag-Erling Smørgrav  * specific prior written permission.
22b7579f77SDag-Erling Smørgrav  *
23b7579f77SDag-Erling Smørgrav  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2417d15b25SDag-Erling Smørgrav  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2517d15b25SDag-Erling Smørgrav  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2617d15b25SDag-Erling Smørgrav  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2717d15b25SDag-Erling Smørgrav  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2817d15b25SDag-Erling Smørgrav  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
2917d15b25SDag-Erling Smørgrav  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
3017d15b25SDag-Erling Smørgrav  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
3117d15b25SDag-Erling Smørgrav  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
3217d15b25SDag-Erling Smørgrav  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
3317d15b25SDag-Erling Smørgrav  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34b7579f77SDag-Erling Smørgrav  */
35b7579f77SDag-Erling Smørgrav 
36b7579f77SDag-Erling Smørgrav /**
37b7579f77SDag-Erling Smørgrav  * \file
38b7579f77SDag-Erling Smørgrav  *
39b7579f77SDag-Erling Smørgrav  * This file contains a hashtable with LRU keeping of entries.
40b7579f77SDag-Erling Smørgrav  *
41b7579f77SDag-Erling Smørgrav  */
42b7579f77SDag-Erling Smørgrav 
43b7579f77SDag-Erling Smørgrav #include "config.h"
44b7579f77SDag-Erling Smørgrav #include "util/storage/lruhash.h"
45b7579f77SDag-Erling Smørgrav #include "util/fptr_wlist.h"
46b7579f77SDag-Erling Smørgrav 
47b7579f77SDag-Erling Smørgrav void
48b7579f77SDag-Erling Smørgrav bin_init(struct lruhash_bin* array, size_t size)
49b7579f77SDag-Erling Smørgrav {
50b7579f77SDag-Erling Smørgrav 	size_t i;
51b7579f77SDag-Erling Smørgrav #ifdef THREADS_DISABLED
52b7579f77SDag-Erling Smørgrav 	(void)array;
53b7579f77SDag-Erling Smørgrav #endif
54b7579f77SDag-Erling Smørgrav 	for(i=0; i<size; i++) {
55b7579f77SDag-Erling Smørgrav 		lock_quick_init(&array[i].lock);
56b7579f77SDag-Erling Smørgrav 		lock_protect(&array[i].lock, &array[i],
57b7579f77SDag-Erling Smørgrav 			sizeof(struct lruhash_bin));
58b7579f77SDag-Erling Smørgrav 	}
59b7579f77SDag-Erling Smørgrav }
60b7579f77SDag-Erling Smørgrav 
61b7579f77SDag-Erling Smørgrav struct lruhash*
623005e0a3SDag-Erling Smørgrav lruhash_create(size_t start_size, size_t maxmem,
633005e0a3SDag-Erling Smørgrav 	lruhash_sizefunc_type sizefunc, lruhash_compfunc_type compfunc,
643005e0a3SDag-Erling Smørgrav 	lruhash_delkeyfunc_type delkeyfunc,
653005e0a3SDag-Erling Smørgrav 	lruhash_deldatafunc_type deldatafunc, void* arg)
66b7579f77SDag-Erling Smørgrav {
67b7579f77SDag-Erling Smørgrav 	struct lruhash* table = (struct lruhash*)calloc(1,
68b7579f77SDag-Erling Smørgrav 		sizeof(struct lruhash));
69b7579f77SDag-Erling Smørgrav 	if(!table)
70b7579f77SDag-Erling Smørgrav 		return NULL;
71b7579f77SDag-Erling Smørgrav 	lock_quick_init(&table->lock);
72b7579f77SDag-Erling Smørgrav 	table->sizefunc = sizefunc;
73b7579f77SDag-Erling Smørgrav 	table->compfunc = compfunc;
74b7579f77SDag-Erling Smørgrav 	table->delkeyfunc = delkeyfunc;
75b7579f77SDag-Erling Smørgrav 	table->deldatafunc = deldatafunc;
76b7579f77SDag-Erling Smørgrav 	table->cb_arg = arg;
77b7579f77SDag-Erling Smørgrav 	table->size = start_size;
78b7579f77SDag-Erling Smørgrav 	table->size_mask = (int)(start_size-1);
79b7579f77SDag-Erling Smørgrav 	table->lru_start = NULL;
80b7579f77SDag-Erling Smørgrav 	table->lru_end = NULL;
81b7579f77SDag-Erling Smørgrav 	table->num = 0;
82b7579f77SDag-Erling Smørgrav 	table->space_used = 0;
83b7579f77SDag-Erling Smørgrav 	table->space_max = maxmem;
848f76bb7dSCy Schubert 	table->max_collisions = 0;
85b7579f77SDag-Erling Smørgrav 	table->array = calloc(table->size, sizeof(struct lruhash_bin));
86b7579f77SDag-Erling Smørgrav 	if(!table->array) {
87b7579f77SDag-Erling Smørgrav 		lock_quick_destroy(&table->lock);
88b7579f77SDag-Erling Smørgrav 		free(table);
89b7579f77SDag-Erling Smørgrav 		return NULL;
90b7579f77SDag-Erling Smørgrav 	}
91b7579f77SDag-Erling Smørgrav 	bin_init(table->array, table->size);
92b7579f77SDag-Erling Smørgrav 	lock_protect(&table->lock, table, sizeof(*table));
93b7579f77SDag-Erling Smørgrav 	lock_protect(&table->lock, table->array,
94b7579f77SDag-Erling Smørgrav 		table->size*sizeof(struct lruhash_bin));
95b7579f77SDag-Erling Smørgrav 	return table;
96b7579f77SDag-Erling Smørgrav }
97b7579f77SDag-Erling Smørgrav 
98b7579f77SDag-Erling Smørgrav void
99b7579f77SDag-Erling Smørgrav bin_delete(struct lruhash* table, struct lruhash_bin* bin)
100b7579f77SDag-Erling Smørgrav {
101b7579f77SDag-Erling Smørgrav 	struct lruhash_entry* p, *np;
102b7579f77SDag-Erling Smørgrav 	void *d;
103b7579f77SDag-Erling Smørgrav 	if(!bin)
104b7579f77SDag-Erling Smørgrav 		return;
105b7579f77SDag-Erling Smørgrav 	lock_quick_destroy(&bin->lock);
106b7579f77SDag-Erling Smørgrav 	p = bin->overflow_list;
107b7579f77SDag-Erling Smørgrav 	bin->overflow_list = NULL;
108b7579f77SDag-Erling Smørgrav 	while(p) {
109b7579f77SDag-Erling Smørgrav 		np = p->overflow_next;
110b7579f77SDag-Erling Smørgrav 		d = p->data;
111b7579f77SDag-Erling Smørgrav 		(*table->delkeyfunc)(p->key, table->cb_arg);
112b7579f77SDag-Erling Smørgrav 		(*table->deldatafunc)(d, table->cb_arg);
113b7579f77SDag-Erling Smørgrav 		p = np;
114b7579f77SDag-Erling Smørgrav 	}
115b7579f77SDag-Erling Smørgrav }
116b7579f77SDag-Erling Smørgrav 
117b7579f77SDag-Erling Smørgrav void
118b7579f77SDag-Erling Smørgrav bin_split(struct lruhash* table, struct lruhash_bin* newa,
119b7579f77SDag-Erling Smørgrav 	int newmask)
120b7579f77SDag-Erling Smørgrav {
121b7579f77SDag-Erling Smørgrav 	size_t i;
122b7579f77SDag-Erling Smørgrav 	struct lruhash_entry *p, *np;
123b7579f77SDag-Erling Smørgrav 	struct lruhash_bin* newbin;
124b7579f77SDag-Erling Smørgrav 	/* move entries to new table. Notice that since hash x is mapped to
125b7579f77SDag-Erling Smørgrav 	 * bin x & mask, and new mask uses one more bit, so all entries in
126b7579f77SDag-Erling Smørgrav 	 * one bin will go into the old bin or bin | newbit */
127b7579f77SDag-Erling Smørgrav #ifndef THREADS_DISABLED
128b7579f77SDag-Erling Smørgrav 	int newbit = newmask - table->size_mask;
129b7579f77SDag-Erling Smørgrav #endif
130b7579f77SDag-Erling Smørgrav 	/* so, really, this task could also be threaded, per bin. */
131b7579f77SDag-Erling Smørgrav 	/* LRU list is not changed */
132b7579f77SDag-Erling Smørgrav 	for(i=0; i<table->size; i++)
133b7579f77SDag-Erling Smørgrav 	{
134b7579f77SDag-Erling Smørgrav 		lock_quick_lock(&table->array[i].lock);
135b7579f77SDag-Erling Smørgrav 		p = table->array[i].overflow_list;
136b7579f77SDag-Erling Smørgrav 		/* lock both destination bins */
137b7579f77SDag-Erling Smørgrav 		lock_quick_lock(&newa[i].lock);
138b7579f77SDag-Erling Smørgrav 		lock_quick_lock(&newa[newbit|i].lock);
139b7579f77SDag-Erling Smørgrav 		while(p) {
140b7579f77SDag-Erling Smørgrav 			np = p->overflow_next;
141b7579f77SDag-Erling Smørgrav 			/* link into correct new bin */
142b7579f77SDag-Erling Smørgrav 			newbin = &newa[p->hash & newmask];
143b7579f77SDag-Erling Smørgrav 			p->overflow_next = newbin->overflow_list;
144b7579f77SDag-Erling Smørgrav 			newbin->overflow_list = p;
145b7579f77SDag-Erling Smørgrav 			p=np;
146b7579f77SDag-Erling Smørgrav 		}
147b7579f77SDag-Erling Smørgrav 		lock_quick_unlock(&newa[i].lock);
148b7579f77SDag-Erling Smørgrav 		lock_quick_unlock(&newa[newbit|i].lock);
149b7579f77SDag-Erling Smørgrav 		lock_quick_unlock(&table->array[i].lock);
150b7579f77SDag-Erling Smørgrav 	}
151b7579f77SDag-Erling Smørgrav }
152b7579f77SDag-Erling Smørgrav 
153b7579f77SDag-Erling Smørgrav void
154b7579f77SDag-Erling Smørgrav lruhash_delete(struct lruhash* table)
155b7579f77SDag-Erling Smørgrav {
156b7579f77SDag-Erling Smørgrav 	size_t i;
157b7579f77SDag-Erling Smørgrav 	if(!table)
158b7579f77SDag-Erling Smørgrav 		return;
159b7579f77SDag-Erling Smørgrav 	/* delete lock on hashtable to force check its OK */
160b7579f77SDag-Erling Smørgrav 	lock_quick_destroy(&table->lock);
161b7579f77SDag-Erling Smørgrav 	for(i=0; i<table->size; i++)
162b7579f77SDag-Erling Smørgrav 		bin_delete(table, &table->array[i]);
163b7579f77SDag-Erling Smørgrav 	free(table->array);
164b7579f77SDag-Erling Smørgrav 	free(table);
165b7579f77SDag-Erling Smørgrav }
166b7579f77SDag-Erling Smørgrav 
167b7579f77SDag-Erling Smørgrav void
168b7579f77SDag-Erling Smørgrav bin_overflow_remove(struct lruhash_bin* bin, struct lruhash_entry* entry)
169b7579f77SDag-Erling Smørgrav {
170b7579f77SDag-Erling Smørgrav 	struct lruhash_entry* p = bin->overflow_list;
171b7579f77SDag-Erling Smørgrav 	struct lruhash_entry** prevp = &bin->overflow_list;
172b7579f77SDag-Erling Smørgrav 	while(p) {
173b7579f77SDag-Erling Smørgrav 		if(p == entry) {
174b7579f77SDag-Erling Smørgrav 			*prevp = p->overflow_next;
175b7579f77SDag-Erling Smørgrav 			return;
176b7579f77SDag-Erling Smørgrav 		}
177b7579f77SDag-Erling Smørgrav 		prevp = &p->overflow_next;
178b7579f77SDag-Erling Smørgrav 		p = p->overflow_next;
179b7579f77SDag-Erling Smørgrav 	}
180b7579f77SDag-Erling Smørgrav }
181b7579f77SDag-Erling Smørgrav 
182b7579f77SDag-Erling Smørgrav void
183b7579f77SDag-Erling Smørgrav reclaim_space(struct lruhash* table, struct lruhash_entry** list)
184b7579f77SDag-Erling Smørgrav {
185b7579f77SDag-Erling Smørgrav 	struct lruhash_entry* d;
186b7579f77SDag-Erling Smørgrav 	struct lruhash_bin* bin;
187b7579f77SDag-Erling Smørgrav 	log_assert(table);
188b7579f77SDag-Erling Smørgrav 	/* does not delete MRU entry, so table will not be empty. */
189b7579f77SDag-Erling Smørgrav 	while(table->num > 1 && table->space_used > table->space_max) {
190b7579f77SDag-Erling Smørgrav 		/* notice that since we hold the hashtable lock, nobody
191b7579f77SDag-Erling Smørgrav 		   can change the lru chain. So it cannot be deleted underneath
192b7579f77SDag-Erling Smørgrav 		   us. We still need the hashbin and entry write lock to make
193b7579f77SDag-Erling Smørgrav 		   sure we flush all users away from the entry.
194b7579f77SDag-Erling Smørgrav 		   which is unlikely, since it is LRU, if someone got a rdlock
195b7579f77SDag-Erling Smørgrav 		   it would be moved to front, but to be sure. */
196b7579f77SDag-Erling Smørgrav 		d = table->lru_end;
197b7579f77SDag-Erling Smørgrav 		/* specialised, delete from end of double linked list,
198b7579f77SDag-Erling Smørgrav 		   and we know num>1, so there is a previous lru entry. */
199b7579f77SDag-Erling Smørgrav 		log_assert(d && d->lru_prev);
200b7579f77SDag-Erling Smørgrav 		table->lru_end = d->lru_prev;
201b7579f77SDag-Erling Smørgrav 		d->lru_prev->lru_next = NULL;
202b7579f77SDag-Erling Smørgrav 		/* schedule entry for deletion */
203b7579f77SDag-Erling Smørgrav 		bin = &table->array[d->hash & table->size_mask];
204b7579f77SDag-Erling Smørgrav 		table->num --;
205b7579f77SDag-Erling Smørgrav 		lock_quick_lock(&bin->lock);
206b7579f77SDag-Erling Smørgrav 		bin_overflow_remove(bin, d);
207b7579f77SDag-Erling Smørgrav 		d->overflow_next = *list;
208b7579f77SDag-Erling Smørgrav 		*list = d;
209b7579f77SDag-Erling Smørgrav 		lock_rw_wrlock(&d->lock);
210b7579f77SDag-Erling Smørgrav 		table->space_used -= table->sizefunc(d->key, d->data);
211b7579f77SDag-Erling Smørgrav 		if(table->markdelfunc)
212b7579f77SDag-Erling Smørgrav 			(*table->markdelfunc)(d->key);
213b7579f77SDag-Erling Smørgrav 		lock_rw_unlock(&d->lock);
214b7579f77SDag-Erling Smørgrav 		lock_quick_unlock(&bin->lock);
215b7579f77SDag-Erling Smørgrav 	}
216b7579f77SDag-Erling Smørgrav }
217b7579f77SDag-Erling Smørgrav 
218b7579f77SDag-Erling Smørgrav struct lruhash_entry*
219b7579f77SDag-Erling Smørgrav bin_find_entry(struct lruhash* table,
2208f76bb7dSCy Schubert 	struct lruhash_bin* bin, hashvalue_type hash, void* key, size_t* collisions)
221b7579f77SDag-Erling Smørgrav {
2228f76bb7dSCy Schubert 	size_t c = 0;
223b7579f77SDag-Erling Smørgrav 	struct lruhash_entry* p = bin->overflow_list;
224b7579f77SDag-Erling Smørgrav 	while(p) {
225b7579f77SDag-Erling Smørgrav 		if(p->hash == hash && table->compfunc(p->key, key) == 0)
2268f76bb7dSCy Schubert 			break;
2278f76bb7dSCy Schubert 		c++;
228b7579f77SDag-Erling Smørgrav 		p = p->overflow_next;
229b7579f77SDag-Erling Smørgrav 	}
2308f76bb7dSCy Schubert 	if (collisions != NULL)
2318f76bb7dSCy Schubert 		*collisions = c;
2328f76bb7dSCy Schubert 	return p;
233b7579f77SDag-Erling Smørgrav }
234b7579f77SDag-Erling Smørgrav 
235b7579f77SDag-Erling Smørgrav void
236b7579f77SDag-Erling Smørgrav table_grow(struct lruhash* table)
237b7579f77SDag-Erling Smørgrav {
238b7579f77SDag-Erling Smørgrav 	struct lruhash_bin* newa;
239b7579f77SDag-Erling Smørgrav 	int newmask;
240b7579f77SDag-Erling Smørgrav 	size_t i;
241b7579f77SDag-Erling Smørgrav 	if(table->size_mask == (int)(((size_t)-1)>>1)) {
242b7579f77SDag-Erling Smørgrav 		log_err("hash array malloc: size_t too small");
243b7579f77SDag-Erling Smørgrav 		return;
244b7579f77SDag-Erling Smørgrav 	}
245b7579f77SDag-Erling Smørgrav 	/* try to allocate new array, if not fail */
246b7579f77SDag-Erling Smørgrav 	newa = calloc(table->size*2, sizeof(struct lruhash_bin));
247b7579f77SDag-Erling Smørgrav 	if(!newa) {
248b7579f77SDag-Erling Smørgrav 		log_err("hash grow: malloc failed");
249b7579f77SDag-Erling Smørgrav 		/* continue with smaller array. Though its slower. */
250b7579f77SDag-Erling Smørgrav 		return;
251b7579f77SDag-Erling Smørgrav 	}
252b7579f77SDag-Erling Smørgrav 	bin_init(newa, table->size*2);
253b7579f77SDag-Erling Smørgrav 	newmask = (table->size_mask << 1) | 1;
254b7579f77SDag-Erling Smørgrav 	bin_split(table, newa, newmask);
255b7579f77SDag-Erling Smørgrav 	/* delete the old bins */
256b7579f77SDag-Erling Smørgrav 	lock_unprotect(&table->lock, table->array);
257b7579f77SDag-Erling Smørgrav 	for(i=0; i<table->size; i++) {
258b7579f77SDag-Erling Smørgrav 		lock_quick_destroy(&table->array[i].lock);
259b7579f77SDag-Erling Smørgrav 	}
260b7579f77SDag-Erling Smørgrav 	free(table->array);
261b7579f77SDag-Erling Smørgrav 
262b7579f77SDag-Erling Smørgrav 	table->size *= 2;
263b7579f77SDag-Erling Smørgrav 	table->size_mask = newmask;
264b7579f77SDag-Erling Smørgrav 	table->array = newa;
265b7579f77SDag-Erling Smørgrav 	lock_protect(&table->lock, table->array,
266b7579f77SDag-Erling Smørgrav 		table->size*sizeof(struct lruhash_bin));
267b7579f77SDag-Erling Smørgrav 	return;
268b7579f77SDag-Erling Smørgrav }
269b7579f77SDag-Erling Smørgrav 
270b7579f77SDag-Erling Smørgrav void
271b7579f77SDag-Erling Smørgrav lru_front(struct lruhash* table, struct lruhash_entry* entry)
272b7579f77SDag-Erling Smørgrav {
273b7579f77SDag-Erling Smørgrav 	entry->lru_prev = NULL;
274b7579f77SDag-Erling Smørgrav 	entry->lru_next = table->lru_start;
275b7579f77SDag-Erling Smørgrav 	if(!table->lru_start)
276b7579f77SDag-Erling Smørgrav 		table->lru_end = entry;
277b7579f77SDag-Erling Smørgrav 	else	table->lru_start->lru_prev = entry;
278b7579f77SDag-Erling Smørgrav 	table->lru_start = entry;
279b7579f77SDag-Erling Smørgrav }
280b7579f77SDag-Erling Smørgrav 
281b7579f77SDag-Erling Smørgrav void
282b7579f77SDag-Erling Smørgrav lru_remove(struct lruhash* table, struct lruhash_entry* entry)
283b7579f77SDag-Erling Smørgrav {
284b7579f77SDag-Erling Smørgrav 	if(entry->lru_prev)
285b7579f77SDag-Erling Smørgrav 		entry->lru_prev->lru_next = entry->lru_next;
286b7579f77SDag-Erling Smørgrav 	else	table->lru_start = entry->lru_next;
287b7579f77SDag-Erling Smørgrav 	if(entry->lru_next)
288b7579f77SDag-Erling Smørgrav 		entry->lru_next->lru_prev = entry->lru_prev;
289b7579f77SDag-Erling Smørgrav 	else	table->lru_end = entry->lru_prev;
290b7579f77SDag-Erling Smørgrav }
291b7579f77SDag-Erling Smørgrav 
292b7579f77SDag-Erling Smørgrav void
293b7579f77SDag-Erling Smørgrav lru_touch(struct lruhash* table, struct lruhash_entry* entry)
294b7579f77SDag-Erling Smørgrav {
295b7579f77SDag-Erling Smørgrav 	log_assert(table && entry);
296b7579f77SDag-Erling Smørgrav 	if(entry == table->lru_start)
297b7579f77SDag-Erling Smørgrav 		return; /* nothing to do */
298b7579f77SDag-Erling Smørgrav 	/* remove from current lru position */
299b7579f77SDag-Erling Smørgrav 	lru_remove(table, entry);
300b7579f77SDag-Erling Smørgrav 	/* add at front */
301b7579f77SDag-Erling Smørgrav 	lru_front(table, entry);
302b7579f77SDag-Erling Smørgrav }
303b7579f77SDag-Erling Smørgrav 
304b7579f77SDag-Erling Smørgrav void
3053005e0a3SDag-Erling Smørgrav lruhash_insert(struct lruhash* table, hashvalue_type hash,
306b7579f77SDag-Erling Smørgrav         struct lruhash_entry* entry, void* data, void* cb_arg)
307b7579f77SDag-Erling Smørgrav {
308b7579f77SDag-Erling Smørgrav 	struct lruhash_bin* bin;
309b7579f77SDag-Erling Smørgrav 	struct lruhash_entry* found, *reclaimlist=NULL;
310b7579f77SDag-Erling Smørgrav 	size_t need_size;
3118f76bb7dSCy Schubert 	size_t collisions;
312b7579f77SDag-Erling Smørgrav 	fptr_ok(fptr_whitelist_hash_sizefunc(table->sizefunc));
313b7579f77SDag-Erling Smørgrav 	fptr_ok(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc));
314b7579f77SDag-Erling Smørgrav 	fptr_ok(fptr_whitelist_hash_deldatafunc(table->deldatafunc));
315b7579f77SDag-Erling Smørgrav 	fptr_ok(fptr_whitelist_hash_compfunc(table->compfunc));
316b7579f77SDag-Erling Smørgrav 	fptr_ok(fptr_whitelist_hash_markdelfunc(table->markdelfunc));
317b7579f77SDag-Erling Smørgrav 	need_size = table->sizefunc(entry->key, data);
318b7579f77SDag-Erling Smørgrav 	if(cb_arg == NULL) cb_arg = table->cb_arg;
319b7579f77SDag-Erling Smørgrav 
320b7579f77SDag-Erling Smørgrav 	/* find bin */
321b7579f77SDag-Erling Smørgrav 	lock_quick_lock(&table->lock);
322b7579f77SDag-Erling Smørgrav 	bin = &table->array[hash & table->size_mask];
323b7579f77SDag-Erling Smørgrav 	lock_quick_lock(&bin->lock);
324b7579f77SDag-Erling Smørgrav 
325b7579f77SDag-Erling Smørgrav 	/* see if entry exists already */
3268f76bb7dSCy Schubert 	if(!(found=bin_find_entry(table, bin, hash, entry->key, &collisions))) {
327b7579f77SDag-Erling Smørgrav 		/* if not: add to bin */
328b7579f77SDag-Erling Smørgrav 		entry->overflow_next = bin->overflow_list;
329b7579f77SDag-Erling Smørgrav 		bin->overflow_list = entry;
330b7579f77SDag-Erling Smørgrav 		lru_front(table, entry);
331b7579f77SDag-Erling Smørgrav 		table->num++;
3328f76bb7dSCy Schubert 		if (table->max_collisions < collisions)
3338f76bb7dSCy Schubert 			table->max_collisions = collisions;
334b7579f77SDag-Erling Smørgrav 		table->space_used += need_size;
335b7579f77SDag-Erling Smørgrav 	} else {
336b7579f77SDag-Erling Smørgrav 		/* if so: update data - needs a writelock */
337b7579f77SDag-Erling Smørgrav 		table->space_used += need_size -
338b7579f77SDag-Erling Smørgrav 			(*table->sizefunc)(found->key, found->data);
339b7579f77SDag-Erling Smørgrav 		(*table->delkeyfunc)(entry->key, cb_arg);
340b7579f77SDag-Erling Smørgrav 		lru_touch(table, found);
341b7579f77SDag-Erling Smørgrav 		lock_rw_wrlock(&found->lock);
342b7579f77SDag-Erling Smørgrav 		(*table->deldatafunc)(found->data, cb_arg);
343b7579f77SDag-Erling Smørgrav 		found->data = data;
344b7579f77SDag-Erling Smørgrav 		lock_rw_unlock(&found->lock);
345b7579f77SDag-Erling Smørgrav 	}
346b7579f77SDag-Erling Smørgrav 	lock_quick_unlock(&bin->lock);
347b7579f77SDag-Erling Smørgrav 	if(table->space_used > table->space_max)
348b7579f77SDag-Erling Smørgrav 		reclaim_space(table, &reclaimlist);
349b7579f77SDag-Erling Smørgrav 	if(table->num >= table->size)
350b7579f77SDag-Erling Smørgrav 		table_grow(table);
351b7579f77SDag-Erling Smørgrav 	lock_quick_unlock(&table->lock);
352b7579f77SDag-Erling Smørgrav 
353b7579f77SDag-Erling Smørgrav 	/* finish reclaim if any (outside of critical region) */
354b7579f77SDag-Erling Smørgrav 	while(reclaimlist) {
355b7579f77SDag-Erling Smørgrav 		struct lruhash_entry* n = reclaimlist->overflow_next;
356b7579f77SDag-Erling Smørgrav 		void* d = reclaimlist->data;
357b7579f77SDag-Erling Smørgrav 		(*table->delkeyfunc)(reclaimlist->key, cb_arg);
358b7579f77SDag-Erling Smørgrav 		(*table->deldatafunc)(d, cb_arg);
359b7579f77SDag-Erling Smørgrav 		reclaimlist = n;
360b7579f77SDag-Erling Smørgrav 	}
361b7579f77SDag-Erling Smørgrav }
362b7579f77SDag-Erling Smørgrav 
363b7579f77SDag-Erling Smørgrav struct lruhash_entry*
3643005e0a3SDag-Erling Smørgrav lruhash_lookup(struct lruhash* table, hashvalue_type hash, void* key, int wr)
365b7579f77SDag-Erling Smørgrav {
366b7579f77SDag-Erling Smørgrav 	struct lruhash_entry* entry;
367b7579f77SDag-Erling Smørgrav 	struct lruhash_bin* bin;
368b7579f77SDag-Erling Smørgrav 	fptr_ok(fptr_whitelist_hash_compfunc(table->compfunc));
369b7579f77SDag-Erling Smørgrav 
370b7579f77SDag-Erling Smørgrav 	lock_quick_lock(&table->lock);
371b7579f77SDag-Erling Smørgrav 	bin = &table->array[hash & table->size_mask];
372b7579f77SDag-Erling Smørgrav 	lock_quick_lock(&bin->lock);
3738f76bb7dSCy Schubert 	if((entry=bin_find_entry(table, bin, hash, key, NULL)))
374b7579f77SDag-Erling Smørgrav 		lru_touch(table, entry);
375b7579f77SDag-Erling Smørgrav 	lock_quick_unlock(&table->lock);
376b7579f77SDag-Erling Smørgrav 
377b7579f77SDag-Erling Smørgrav 	if(entry) {
378b7579f77SDag-Erling Smørgrav 		if(wr)	{ lock_rw_wrlock(&entry->lock); }
379b7579f77SDag-Erling Smørgrav 		else	{ lock_rw_rdlock(&entry->lock); }
380b7579f77SDag-Erling Smørgrav 	}
381b7579f77SDag-Erling Smørgrav 	lock_quick_unlock(&bin->lock);
382b7579f77SDag-Erling Smørgrav 	return entry;
383b7579f77SDag-Erling Smørgrav }
384b7579f77SDag-Erling Smørgrav 
385b7579f77SDag-Erling Smørgrav void
3863005e0a3SDag-Erling Smørgrav lruhash_remove(struct lruhash* table, hashvalue_type hash, void* key)
387b7579f77SDag-Erling Smørgrav {
388b7579f77SDag-Erling Smørgrav 	struct lruhash_entry* entry;
389b7579f77SDag-Erling Smørgrav 	struct lruhash_bin* bin;
390b7579f77SDag-Erling Smørgrav 	void *d;
391b7579f77SDag-Erling Smørgrav 	fptr_ok(fptr_whitelist_hash_sizefunc(table->sizefunc));
392b7579f77SDag-Erling Smørgrav 	fptr_ok(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc));
393b7579f77SDag-Erling Smørgrav 	fptr_ok(fptr_whitelist_hash_deldatafunc(table->deldatafunc));
394b7579f77SDag-Erling Smørgrav 	fptr_ok(fptr_whitelist_hash_compfunc(table->compfunc));
395b7579f77SDag-Erling Smørgrav 	fptr_ok(fptr_whitelist_hash_markdelfunc(table->markdelfunc));
396b7579f77SDag-Erling Smørgrav 
397b7579f77SDag-Erling Smørgrav 	lock_quick_lock(&table->lock);
398b7579f77SDag-Erling Smørgrav 	bin = &table->array[hash & table->size_mask];
399b7579f77SDag-Erling Smørgrav 	lock_quick_lock(&bin->lock);
4008f76bb7dSCy Schubert 	if((entry=bin_find_entry(table, bin, hash, key, NULL))) {
401b7579f77SDag-Erling Smørgrav 		bin_overflow_remove(bin, entry);
402b7579f77SDag-Erling Smørgrav 		lru_remove(table, entry);
403b7579f77SDag-Erling Smørgrav 	} else {
404b7579f77SDag-Erling Smørgrav 		lock_quick_unlock(&table->lock);
405b7579f77SDag-Erling Smørgrav 		lock_quick_unlock(&bin->lock);
406b7579f77SDag-Erling Smørgrav 		return;
407b7579f77SDag-Erling Smørgrav 	}
408b7579f77SDag-Erling Smørgrav 	table->num--;
409b7579f77SDag-Erling Smørgrav 	table->space_used -= (*table->sizefunc)(entry->key, entry->data);
410b7579f77SDag-Erling Smørgrav 	lock_rw_wrlock(&entry->lock);
411b7579f77SDag-Erling Smørgrav 	if(table->markdelfunc)
412b7579f77SDag-Erling Smørgrav 		(*table->markdelfunc)(entry->key);
413b7579f77SDag-Erling Smørgrav 	lock_rw_unlock(&entry->lock);
414b7579f77SDag-Erling Smørgrav 	lock_quick_unlock(&bin->lock);
415f44e67d1SCy Schubert 	lock_quick_unlock(&table->lock);
416b7579f77SDag-Erling Smørgrav 	/* finish removal */
417b7579f77SDag-Erling Smørgrav 	d = entry->data;
418b7579f77SDag-Erling Smørgrav 	(*table->delkeyfunc)(entry->key, table->cb_arg);
419b7579f77SDag-Erling Smørgrav 	(*table->deldatafunc)(d, table->cb_arg);
420b7579f77SDag-Erling Smørgrav }
421b7579f77SDag-Erling Smørgrav 
422b7579f77SDag-Erling Smørgrav /** clear bin, respecting locks, does not do space, LRU */
423b7579f77SDag-Erling Smørgrav static void
424b7579f77SDag-Erling Smørgrav bin_clear(struct lruhash* table, struct lruhash_bin* bin)
425b7579f77SDag-Erling Smørgrav {
426b7579f77SDag-Erling Smørgrav 	struct lruhash_entry* p, *np;
427b7579f77SDag-Erling Smørgrav 	void *d;
428b7579f77SDag-Erling Smørgrav 	lock_quick_lock(&bin->lock);
429b7579f77SDag-Erling Smørgrav 	p = bin->overflow_list;
430b7579f77SDag-Erling Smørgrav 	while(p) {
431b7579f77SDag-Erling Smørgrav 		lock_rw_wrlock(&p->lock);
432b7579f77SDag-Erling Smørgrav 		np = p->overflow_next;
433b7579f77SDag-Erling Smørgrav 		d = p->data;
434b7579f77SDag-Erling Smørgrav 		if(table->markdelfunc)
435b7579f77SDag-Erling Smørgrav 			(*table->markdelfunc)(p->key);
436b7579f77SDag-Erling Smørgrav 		lock_rw_unlock(&p->lock);
437b7579f77SDag-Erling Smørgrav 		(*table->delkeyfunc)(p->key, table->cb_arg);
438b7579f77SDag-Erling Smørgrav 		(*table->deldatafunc)(d, table->cb_arg);
439b7579f77SDag-Erling Smørgrav 		p = np;
440b7579f77SDag-Erling Smørgrav 	}
441b7579f77SDag-Erling Smørgrav 	bin->overflow_list = NULL;
442b7579f77SDag-Erling Smørgrav 	lock_quick_unlock(&bin->lock);
443b7579f77SDag-Erling Smørgrav }
444b7579f77SDag-Erling Smørgrav 
445b7579f77SDag-Erling Smørgrav void
446b7579f77SDag-Erling Smørgrav lruhash_clear(struct lruhash* table)
447b7579f77SDag-Erling Smørgrav {
448b7579f77SDag-Erling Smørgrav 	size_t i;
449b7579f77SDag-Erling Smørgrav 	if(!table)
450b7579f77SDag-Erling Smørgrav 		return;
451b7579f77SDag-Erling Smørgrav 	fptr_ok(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc));
452b7579f77SDag-Erling Smørgrav 	fptr_ok(fptr_whitelist_hash_deldatafunc(table->deldatafunc));
453b7579f77SDag-Erling Smørgrav 	fptr_ok(fptr_whitelist_hash_markdelfunc(table->markdelfunc));
454b7579f77SDag-Erling Smørgrav 
455b7579f77SDag-Erling Smørgrav 	lock_quick_lock(&table->lock);
456b7579f77SDag-Erling Smørgrav 	for(i=0; i<table->size; i++) {
457b7579f77SDag-Erling Smørgrav 		bin_clear(table, &table->array[i]);
458b7579f77SDag-Erling Smørgrav 	}
459b7579f77SDag-Erling Smørgrav 	table->lru_start = NULL;
460b7579f77SDag-Erling Smørgrav 	table->lru_end = NULL;
461b7579f77SDag-Erling Smørgrav 	table->num = 0;
462b7579f77SDag-Erling Smørgrav 	table->space_used = 0;
463b7579f77SDag-Erling Smørgrav 	lock_quick_unlock(&table->lock);
464b7579f77SDag-Erling Smørgrav }
465b7579f77SDag-Erling Smørgrav 
466b7579f77SDag-Erling Smørgrav void
467b7579f77SDag-Erling Smørgrav lruhash_status(struct lruhash* table, const char* id, int extended)
468b7579f77SDag-Erling Smørgrav {
469b7579f77SDag-Erling Smørgrav 	lock_quick_lock(&table->lock);
470b7579f77SDag-Erling Smørgrav 	log_info("%s: %u entries, memory %u / %u",
471b7579f77SDag-Erling Smørgrav 		id, (unsigned)table->num, (unsigned)table->space_used,
472b7579f77SDag-Erling Smørgrav 		(unsigned)table->space_max);
473b7579f77SDag-Erling Smørgrav 	log_info("  itemsize %u, array %u, mask %d",
474b7579f77SDag-Erling Smørgrav 		(unsigned)(table->num? table->space_used/table->num : 0),
475b7579f77SDag-Erling Smørgrav 		(unsigned)table->size, table->size_mask);
476b7579f77SDag-Erling Smørgrav 	if(extended) {
477b7579f77SDag-Erling Smørgrav 		size_t i;
478b7579f77SDag-Erling Smørgrav 		int min=(int)table->size*2, max=-2;
479b7579f77SDag-Erling Smørgrav 		for(i=0; i<table->size; i++) {
480b7579f77SDag-Erling Smørgrav 			int here = 0;
481b7579f77SDag-Erling Smørgrav 			struct lruhash_entry *en;
482b7579f77SDag-Erling Smørgrav 			lock_quick_lock(&table->array[i].lock);
483b7579f77SDag-Erling Smørgrav 			en = table->array[i].overflow_list;
484b7579f77SDag-Erling Smørgrav 			while(en) {
485b7579f77SDag-Erling Smørgrav 				here ++;
486b7579f77SDag-Erling Smørgrav 				en = en->overflow_next;
487b7579f77SDag-Erling Smørgrav 			}
488b7579f77SDag-Erling Smørgrav 			lock_quick_unlock(&table->array[i].lock);
489b7579f77SDag-Erling Smørgrav 			if(extended >= 2)
490b7579f77SDag-Erling Smørgrav 				log_info("bin[%d] %d", (int)i, here);
491b7579f77SDag-Erling Smørgrav 			if(here > max) max = here;
492b7579f77SDag-Erling Smørgrav 			if(here < min) min = here;
493b7579f77SDag-Erling Smørgrav 		}
494b7579f77SDag-Erling Smørgrav 		log_info("  bin min %d, avg %.2lf, max %d", min,
495b7579f77SDag-Erling Smørgrav 			(double)table->num/(double)table->size, max);
496b7579f77SDag-Erling Smørgrav 	}
497b7579f77SDag-Erling Smørgrav 	lock_quick_unlock(&table->lock);
498b7579f77SDag-Erling Smørgrav }
499b7579f77SDag-Erling Smørgrav 
500b7579f77SDag-Erling Smørgrav size_t
501b7579f77SDag-Erling Smørgrav lruhash_get_mem(struct lruhash* table)
502b7579f77SDag-Erling Smørgrav {
503b7579f77SDag-Erling Smørgrav 	size_t s;
504b7579f77SDag-Erling Smørgrav 	lock_quick_lock(&table->lock);
505b7579f77SDag-Erling Smørgrav 	s = sizeof(struct lruhash) + table->space_used;
506b7579f77SDag-Erling Smørgrav #ifdef USE_THREAD_DEBUG
507b7579f77SDag-Erling Smørgrav 	if(table->size != 0) {
508b7579f77SDag-Erling Smørgrav 		size_t i;
509b7579f77SDag-Erling Smørgrav 		for(i=0; i<table->size; i++)
510b7579f77SDag-Erling Smørgrav 			s += sizeof(struct lruhash_bin) +
511b7579f77SDag-Erling Smørgrav 				lock_get_mem(&table->array[i].lock);
512b7579f77SDag-Erling Smørgrav 	}
513b7579f77SDag-Erling Smørgrav #else /* no THREAD_DEBUG */
514b7579f77SDag-Erling Smørgrav 	if(table->size != 0)
515b7579f77SDag-Erling Smørgrav 		s += (table->size)*(sizeof(struct lruhash_bin) +
516b7579f77SDag-Erling Smørgrav 			lock_get_mem(&table->array[0].lock));
517b7579f77SDag-Erling Smørgrav #endif
518b7579f77SDag-Erling Smørgrav 	lock_quick_unlock(&table->lock);
519b7579f77SDag-Erling Smørgrav 	s += lock_get_mem(&table->lock);
520b7579f77SDag-Erling Smørgrav 	return s;
521b7579f77SDag-Erling Smørgrav }
522b7579f77SDag-Erling Smørgrav 
523b7579f77SDag-Erling Smørgrav void
5243005e0a3SDag-Erling Smørgrav lruhash_setmarkdel(struct lruhash* table, lruhash_markdelfunc_type md)
525b7579f77SDag-Erling Smørgrav {
526b7579f77SDag-Erling Smørgrav 	lock_quick_lock(&table->lock);
527b7579f77SDag-Erling Smørgrav 	table->markdelfunc = md;
528b7579f77SDag-Erling Smørgrav 	lock_quick_unlock(&table->lock);
529b7579f77SDag-Erling Smørgrav }
530b7579f77SDag-Erling Smørgrav 
531b7579f77SDag-Erling Smørgrav void
532335c7cdaSCy Schubert lruhash_update_space_used(struct lruhash* table, void* cb_arg, int diff_size)
533335c7cdaSCy Schubert {
534335c7cdaSCy Schubert 	struct lruhash_entry *reclaimlist = NULL;
535335c7cdaSCy Schubert 
536335c7cdaSCy Schubert 	fptr_ok(fptr_whitelist_hash_sizefunc(table->sizefunc));
537335c7cdaSCy Schubert 	fptr_ok(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc));
538335c7cdaSCy Schubert 	fptr_ok(fptr_whitelist_hash_deldatafunc(table->deldatafunc));
539335c7cdaSCy Schubert 	fptr_ok(fptr_whitelist_hash_markdelfunc(table->markdelfunc));
540335c7cdaSCy Schubert 
541335c7cdaSCy Schubert 	if(cb_arg == NULL) cb_arg = table->cb_arg;
542335c7cdaSCy Schubert 
543335c7cdaSCy Schubert 	/* update space used */
544335c7cdaSCy Schubert 	lock_quick_lock(&table->lock);
545335c7cdaSCy Schubert 
546335c7cdaSCy Schubert 	if((int)table->space_used + diff_size < 0)
547335c7cdaSCy Schubert 		table->space_used = 0;
548335c7cdaSCy Schubert 	else table->space_used = (size_t)((int)table->space_used + diff_size);
549335c7cdaSCy Schubert 
550335c7cdaSCy Schubert 	if(table->space_used > table->space_max)
551335c7cdaSCy Schubert 		reclaim_space(table, &reclaimlist);
552335c7cdaSCy Schubert 
553335c7cdaSCy Schubert 	lock_quick_unlock(&table->lock);
554335c7cdaSCy Schubert 
555335c7cdaSCy Schubert 	/* finish reclaim if any (outside of critical region) */
556335c7cdaSCy Schubert 	while(reclaimlist) {
557335c7cdaSCy Schubert 		struct lruhash_entry* n = reclaimlist->overflow_next;
558335c7cdaSCy Schubert 		void* d = reclaimlist->data;
559335c7cdaSCy Schubert 		(*table->delkeyfunc)(reclaimlist->key, cb_arg);
560335c7cdaSCy Schubert 		(*table->deldatafunc)(d, cb_arg);
561335c7cdaSCy Schubert 		reclaimlist = n;
562335c7cdaSCy Schubert 	}
563335c7cdaSCy Schubert }
564335c7cdaSCy Schubert 
565*be771a7bSCy Schubert void lruhash_update_space_max(struct lruhash* table, void* cb_arg, size_t max)
566*be771a7bSCy Schubert {
567*be771a7bSCy Schubert 	struct lruhash_entry *reclaimlist = NULL;
568*be771a7bSCy Schubert 
569*be771a7bSCy Schubert 	fptr_ok(fptr_whitelist_hash_sizefunc(table->sizefunc));
570*be771a7bSCy Schubert 	fptr_ok(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc));
571*be771a7bSCy Schubert 	fptr_ok(fptr_whitelist_hash_deldatafunc(table->deldatafunc));
572*be771a7bSCy Schubert 	fptr_ok(fptr_whitelist_hash_markdelfunc(table->markdelfunc));
573*be771a7bSCy Schubert 
574*be771a7bSCy Schubert 	if(cb_arg == NULL) cb_arg = table->cb_arg;
575*be771a7bSCy Schubert 
576*be771a7bSCy Schubert 	/* update space max */
577*be771a7bSCy Schubert 	lock_quick_lock(&table->lock);
578*be771a7bSCy Schubert 	table->space_max = max;
579*be771a7bSCy Schubert 
580*be771a7bSCy Schubert 	if(table->space_used > table->space_max)
581*be771a7bSCy Schubert 		reclaim_space(table, &reclaimlist);
582*be771a7bSCy Schubert 
583*be771a7bSCy Schubert 	lock_quick_unlock(&table->lock);
584*be771a7bSCy Schubert 
585*be771a7bSCy Schubert 	/* finish reclaim if any (outside of critical region) */
586*be771a7bSCy Schubert 	while(reclaimlist) {
587*be771a7bSCy Schubert 		struct lruhash_entry* n = reclaimlist->overflow_next;
588*be771a7bSCy Schubert 		void* d = reclaimlist->data;
589*be771a7bSCy Schubert 		(*table->delkeyfunc)(reclaimlist->key, cb_arg);
590*be771a7bSCy Schubert 		(*table->deldatafunc)(d, cb_arg);
591*be771a7bSCy Schubert 		reclaimlist = n;
592*be771a7bSCy Schubert 	}
593*be771a7bSCy Schubert }
594*be771a7bSCy Schubert 
595335c7cdaSCy Schubert void
596b7579f77SDag-Erling Smørgrav lruhash_traverse(struct lruhash* h, int wr,
597b7579f77SDag-Erling Smørgrav 	void (*func)(struct lruhash_entry*, void*), void* arg)
598b7579f77SDag-Erling Smørgrav {
599b7579f77SDag-Erling Smørgrav 	size_t i;
600b7579f77SDag-Erling Smørgrav 	struct lruhash_entry* e;
601b7579f77SDag-Erling Smørgrav 
602b7579f77SDag-Erling Smørgrav 	lock_quick_lock(&h->lock);
603b7579f77SDag-Erling Smørgrav 	for(i=0; i<h->size; i++) {
604b7579f77SDag-Erling Smørgrav 		lock_quick_lock(&h->array[i].lock);
605b7579f77SDag-Erling Smørgrav 		for(e = h->array[i].overflow_list; e; e = e->overflow_next) {
606b7579f77SDag-Erling Smørgrav 			if(wr) {
607b7579f77SDag-Erling Smørgrav 				lock_rw_wrlock(&e->lock);
608b7579f77SDag-Erling Smørgrav 			} else {
609b7579f77SDag-Erling Smørgrav 				lock_rw_rdlock(&e->lock);
610b7579f77SDag-Erling Smørgrav 			}
611b7579f77SDag-Erling Smørgrav 			(*func)(e, arg);
612b7579f77SDag-Erling Smørgrav 			lock_rw_unlock(&e->lock);
613b7579f77SDag-Erling Smørgrav 		}
614b7579f77SDag-Erling Smørgrav 		lock_quick_unlock(&h->array[i].lock);
615b7579f77SDag-Erling Smørgrav 	}
616b7579f77SDag-Erling Smørgrav 	lock_quick_unlock(&h->lock);
617b7579f77SDag-Erling Smørgrav }
61865b390aaSDag-Erling Smørgrav 
61965b390aaSDag-Erling Smørgrav /*
62065b390aaSDag-Erling Smørgrav  * Demote: the opposite of touch, move an entry to the bottom
62165b390aaSDag-Erling Smørgrav  * of the LRU pile.
62265b390aaSDag-Erling Smørgrav  */
62365b390aaSDag-Erling Smørgrav 
62465b390aaSDag-Erling Smørgrav void
62565b390aaSDag-Erling Smørgrav lru_demote(struct lruhash* table, struct lruhash_entry* entry)
62665b390aaSDag-Erling Smørgrav {
62765b390aaSDag-Erling Smørgrav 	log_assert(table && entry);
62865b390aaSDag-Erling Smørgrav 	if (entry == table->lru_end)
62965b390aaSDag-Erling Smørgrav 		return; /* nothing to do */
63065b390aaSDag-Erling Smørgrav 	/* remove from current lru position */
63165b390aaSDag-Erling Smørgrav 	lru_remove(table, entry);
63265b390aaSDag-Erling Smørgrav 	/* add at end */
63365b390aaSDag-Erling Smørgrav 	entry->lru_next = NULL;
63465b390aaSDag-Erling Smørgrav 	entry->lru_prev = table->lru_end;
63565b390aaSDag-Erling Smørgrav 
63665b390aaSDag-Erling Smørgrav 	if (table->lru_end == NULL)
63765b390aaSDag-Erling Smørgrav 	{
63865b390aaSDag-Erling Smørgrav 		table->lru_start = entry;
63965b390aaSDag-Erling Smørgrav 	}
64065b390aaSDag-Erling Smørgrav 	else
64165b390aaSDag-Erling Smørgrav 	{
64265b390aaSDag-Erling Smørgrav 		table->lru_end->lru_next = entry;
64365b390aaSDag-Erling Smørgrav 	}
64465b390aaSDag-Erling Smørgrav 	table->lru_end = entry;
64565b390aaSDag-Erling Smørgrav }
64665b390aaSDag-Erling Smørgrav 
64765b390aaSDag-Erling Smørgrav struct lruhash_entry*
64865b390aaSDag-Erling Smørgrav lruhash_insert_or_retrieve(struct lruhash* table, hashvalue_type hash,
64965b390aaSDag-Erling Smørgrav 	struct lruhash_entry* entry, void* data, void* cb_arg)
65065b390aaSDag-Erling Smørgrav {
65165b390aaSDag-Erling Smørgrav 	struct lruhash_bin* bin;
65265b390aaSDag-Erling Smørgrav 	struct lruhash_entry* found, *reclaimlist = NULL;
65365b390aaSDag-Erling Smørgrav 	size_t need_size;
6548f76bb7dSCy Schubert 	size_t collisions;
65565b390aaSDag-Erling Smørgrav 	fptr_ok(fptr_whitelist_hash_sizefunc(table->sizefunc));
65665b390aaSDag-Erling Smørgrav 	fptr_ok(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc));
65765b390aaSDag-Erling Smørgrav 	fptr_ok(fptr_whitelist_hash_deldatafunc(table->deldatafunc));
65865b390aaSDag-Erling Smørgrav 	fptr_ok(fptr_whitelist_hash_compfunc(table->compfunc));
65965b390aaSDag-Erling Smørgrav 	fptr_ok(fptr_whitelist_hash_markdelfunc(table->markdelfunc));
66065b390aaSDag-Erling Smørgrav 	need_size = table->sizefunc(entry->key, data);
66165b390aaSDag-Erling Smørgrav 	if (cb_arg == NULL) cb_arg = table->cb_arg;
66265b390aaSDag-Erling Smørgrav 
66365b390aaSDag-Erling Smørgrav 	/* find bin */
66465b390aaSDag-Erling Smørgrav 	lock_quick_lock(&table->lock);
66565b390aaSDag-Erling Smørgrav 	bin = &table->array[hash & table->size_mask];
66665b390aaSDag-Erling Smørgrav 	lock_quick_lock(&bin->lock);
66765b390aaSDag-Erling Smørgrav 
66865b390aaSDag-Erling Smørgrav 	/* see if entry exists already */
6698f76bb7dSCy Schubert 	if ((found = bin_find_entry(table, bin, hash, entry->key, &collisions)) != NULL) {
67065b390aaSDag-Erling Smørgrav 		/* if so: keep the existing data - acquire a writelock */
67165b390aaSDag-Erling Smørgrav 		lock_rw_wrlock(&found->lock);
67265b390aaSDag-Erling Smørgrav 	}
67365b390aaSDag-Erling Smørgrav 	else
67465b390aaSDag-Erling Smørgrav 	{
67565b390aaSDag-Erling Smørgrav 		/* if not: add to bin */
67665b390aaSDag-Erling Smørgrav 		entry->overflow_next = bin->overflow_list;
67765b390aaSDag-Erling Smørgrav 		bin->overflow_list = entry;
67865b390aaSDag-Erling Smørgrav 		lru_front(table, entry);
67965b390aaSDag-Erling Smørgrav 		table->num++;
6808f76bb7dSCy Schubert 		if (table->max_collisions < collisions)
6818f76bb7dSCy Schubert 			table->max_collisions = collisions;
68265b390aaSDag-Erling Smørgrav 		table->space_used += need_size;
68365b390aaSDag-Erling Smørgrav 		/* return the entry that was presented, and lock it */
68465b390aaSDag-Erling Smørgrav 		found = entry;
68565b390aaSDag-Erling Smørgrav 		lock_rw_wrlock(&found->lock);
68665b390aaSDag-Erling Smørgrav 	}
68765b390aaSDag-Erling Smørgrav 	lock_quick_unlock(&bin->lock);
68865b390aaSDag-Erling Smørgrav 	if (table->space_used > table->space_max)
68965b390aaSDag-Erling Smørgrav 		reclaim_space(table, &reclaimlist);
69065b390aaSDag-Erling Smørgrav 	if (table->num >= table->size)
69165b390aaSDag-Erling Smørgrav 		table_grow(table);
69265b390aaSDag-Erling Smørgrav 	lock_quick_unlock(&table->lock);
69365b390aaSDag-Erling Smørgrav 
69465b390aaSDag-Erling Smørgrav 	/* finish reclaim if any (outside of critical region) */
69565b390aaSDag-Erling Smørgrav 	while (reclaimlist) {
69665b390aaSDag-Erling Smørgrav 		struct lruhash_entry* n = reclaimlist->overflow_next;
69765b390aaSDag-Erling Smørgrav 		void* d = reclaimlist->data;
69865b390aaSDag-Erling Smørgrav 		(*table->delkeyfunc)(reclaimlist->key, cb_arg);
69965b390aaSDag-Erling Smørgrav 		(*table->deldatafunc)(d, cb_arg);
70065b390aaSDag-Erling Smørgrav 		reclaimlist = n;
70165b390aaSDag-Erling Smørgrav 	}
70265b390aaSDag-Erling Smørgrav 
70365b390aaSDag-Erling Smørgrav 	/* return the entry that was selected */
70465b390aaSDag-Erling Smørgrav 	return found;
70565b390aaSDag-Erling Smørgrav }
70665b390aaSDag-Erling Smørgrav 
707