1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 1998-2002,2010 Luigi Rizzo, Universita` di Pisa
5 * All rights reserved
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 /*
30 * Binary heap and hash tables, used in dummynet
31 */
32
33 #include <sys/cdefs.h>
34 #include <sys/param.h>
35 #ifdef _KERNEL
36 #include <sys/systm.h>
37 #include <sys/malloc.h>
38 #include <sys/kernel.h>
39 #include <netpfil/ipfw/dn_heap.h>
40 #ifndef log
41 #define log(x, arg...)
42 #endif
43
44 #else /* !_KERNEL */
45
46 #include <stdio.h>
47 #include <dn_test.h>
48 #include <strings.h>
49 #include <stdlib.h>
50
51 #include "dn_heap.h"
52 #define log(x, arg...) fprintf(stderr, ## arg)
53 #define panic(x...) fprintf(stderr, ## x), exit(1)
54 #define MALLOC_DEFINE(a, b, c) volatile int __dummy__ ## a __attribute__((__unused__))
my_malloc(int s)55 static void *my_malloc(int s) { return malloc(s); }
my_free(void * p)56 static void my_free(void *p) { free(p); }
57 #define malloc(s, t, w) my_malloc(s)
58 #define free(p, t) my_free(p)
59 #endif /* !_KERNEL */
60
61 static MALLOC_DEFINE(M_DN_HEAP, "dummynet", "dummynet heap");
62
63 /*
64 * Heap management functions.
65 *
66 * In the heap, first node is element 0. Children of i are 2i+1 and 2i+2.
67 * Some macros help finding parent/children so we can optimize them.
68 *
69 * heap_init() is called to expand the heap when needed.
70 * Increment size in blocks of 16 entries.
71 * Returns 1 on error, 0 on success
72 */
73 #define HEAP_FATHER(x) ( ( (x) - 1 ) / 2 )
74 #define HEAP_LEFT(x) ( (x)+(x) + 1 )
75 #define HEAP_SWAP(a, b, buffer) { buffer = a ; a = b ; b = buffer ; }
76 #define HEAP_INCREMENT 15
77
78 static int
heap_resize(struct dn_heap * h,unsigned int new_size)79 heap_resize(struct dn_heap *h, unsigned int new_size)
80 {
81 struct dn_heap_entry *p;
82
83 if ((unsigned int)h->size >= new_size ) /* have enough room */
84 return 0;
85 #if 1 /* round to the next power of 2 */
86 new_size |= new_size >> 1;
87 new_size |= new_size >> 2;
88 new_size |= new_size >> 4;
89 new_size |= new_size >> 8;
90 new_size |= new_size >> 16;
91 #else
92 new_size = (new_size + HEAP_INCREMENT ) & ~HEAP_INCREMENT;
93 #endif
94 p = mallocarray(new_size, sizeof(*p), M_DN_HEAP, M_NOWAIT);
95 if (p == NULL) {
96 printf("--- %s, resize %d failed\n", __func__, new_size );
97 return 1; /* error */
98 }
99 if (h->size > 0) {
100 bcopy(h->p, p, h->size * sizeof(*p) );
101 free(h->p, M_DN_HEAP);
102 }
103 h->p = p;
104 h->size = new_size;
105 return 0;
106 }
107
108 int
heap_init(struct dn_heap * h,int size,int ofs)109 heap_init(struct dn_heap *h, int size, int ofs)
110 {
111 if (heap_resize(h, size))
112 return 1;
113 h->elements = 0;
114 h->ofs = ofs;
115 return 0;
116 }
117
118 /*
119 * Insert element in heap. Normally, p != NULL, we insert p in
120 * a new position and bubble up. If p == NULL, then the element is
121 * already in place, and key is the position where to start the
122 * bubble-up.
123 * Returns 1 on failure (cannot allocate new heap entry)
124 *
125 * If ofs > 0 the position (index, int) of the element in the heap is
126 * also stored in the element itself at the given offset in bytes.
127 */
128 #define SET_OFFSET(h, i) do { \
129 if (h->ofs > 0) \
130 *((int32_t *)((char *)(h->p[i].object) + h->ofs)) = i; \
131 } while (0)
132 /*
133 * RESET_OFFSET is used for sanity checks. It sets ofs
134 * to an invalid value.
135 */
136 #define RESET_OFFSET(h, i) do { \
137 if (h->ofs > 0) \
138 *((int32_t *)((char *)(h->p[i].object) + h->ofs)) = -16; \
139 } while (0)
140
141 int
heap_insert(struct dn_heap * h,uint64_t key1,void * p)142 heap_insert(struct dn_heap *h, uint64_t key1, void *p)
143 {
144 int son = h->elements;
145
146 //log("%s key %llu p %p\n", __FUNCTION__, key1, p);
147 if (p == NULL) { /* data already there, set starting point */
148 son = key1;
149 } else { /* insert new element at the end, possibly resize */
150 son = h->elements;
151 if (son == h->size) /* need resize... */
152 // XXX expand by 16 or so
153 if (heap_resize(h, h->elements+16) )
154 return 1; /* failure... */
155 h->p[son].object = p;
156 h->p[son].key = key1;
157 h->elements++;
158 }
159 /* make sure that son >= father along the path */
160 while (son > 0) {
161 int father = HEAP_FATHER(son);
162 struct dn_heap_entry tmp;
163
164 if (DN_KEY_LT( h->p[father].key, h->p[son].key ) )
165 break; /* found right position */
166 /* son smaller than father, swap and repeat */
167 HEAP_SWAP(h->p[son], h->p[father], tmp);
168 SET_OFFSET(h, son);
169 son = father;
170 }
171 SET_OFFSET(h, son);
172 return 0;
173 }
174
175 /*
176 * remove top element from heap, or obj if obj != NULL
177 */
178 bool
heap_extract(struct dn_heap * h,void * obj)179 heap_extract(struct dn_heap *h, void *obj)
180 {
181 int child, father, max = h->elements - 1;
182
183 if (max < 0) {
184 return false;
185 }
186 if (obj == NULL)
187 father = 0; /* default: move up smallest child */
188 else { /* extract specific element, index is at offset */
189 if (h->ofs <= 0)
190 panic("%s: extract from middle not set on %p\n",
191 __FUNCTION__, h);
192 father = *((int *)((char *)obj + h->ofs));
193 if (father < 0 || father >= h->elements)
194 return false;
195 }
196 /* We should make sure that the object we're trying to remove is
197 * actually in this heap. */
198 if (obj != NULL && h->p[father].object != obj)
199 return false;
200
201 /*
202 * below, father is the index of the empty element, which
203 * we replace at each step with the smallest child until we
204 * reach the bottom level.
205 */
206 // XXX why removing RESET_OFFSET increases runtime by 10% ?
207 RESET_OFFSET(h, father);
208 while ( (child = HEAP_LEFT(father)) <= max ) {
209 if (child != max &&
210 DN_KEY_LT(h->p[child+1].key, h->p[child].key) )
211 child++; /* take right child, otherwise left */
212 h->p[father] = h->p[child];
213 SET_OFFSET(h, father);
214 father = child;
215 }
216 h->elements--;
217 if (father != max) {
218 /*
219 * Fill hole with last entry and bubble up,
220 * reusing the insert code
221 */
222 h->p[father] = h->p[max];
223 heap_insert(h, father, NULL);
224 }
225
226 return true;
227 }
228
229 #if 0
230 /*
231 * change object position and update references
232 * XXX this one is never used!
233 */
234 static void
235 heap_move(struct dn_heap *h, uint64_t new_key, void *object)
236 {
237 int temp, i, max = h->elements-1;
238 struct dn_heap_entry *p, buf;
239
240 if (h->ofs <= 0)
241 panic("cannot move items on this heap");
242 p = h->p; /* shortcut */
243
244 i = *((int *)((char *)object + h->ofs));
245 if (DN_KEY_LT(new_key, p[i].key) ) { /* must move up */
246 p[i].key = new_key;
247 for (; i>0 &&
248 DN_KEY_LT(new_key, p[(temp = HEAP_FATHER(i))].key);
249 i = temp ) { /* bubble up */
250 HEAP_SWAP(p[i], p[temp], buf);
251 SET_OFFSET(h, i);
252 }
253 } else { /* must move down */
254 p[i].key = new_key;
255 while ( (temp = HEAP_LEFT(i)) <= max ) {
256 /* found left child */
257 if (temp != max &&
258 DN_KEY_LT(p[temp+1].key, p[temp].key))
259 temp++; /* select child with min key */
260 if (DN_KEY_LT(>p[temp].key, new_key)) {
261 /* go down */
262 HEAP_SWAP(p[i], p[temp], buf);
263 SET_OFFSET(h, i);
264 } else
265 break;
266 i = temp;
267 }
268 }
269 SET_OFFSET(h, i);
270 }
271 #endif /* heap_move, unused */
272
273 /*
274 * heapify() will reorganize data inside an array to maintain the
275 * heap property. It is needed when we delete a bunch of entries.
276 */
277 static void
heapify(struct dn_heap * h)278 heapify(struct dn_heap *h)
279 {
280 int i;
281
282 for (i = 0; i < h->elements; i++ )
283 heap_insert(h, i , NULL);
284 }
285
286 int
heap_scan(struct dn_heap * h,int (* fn)(void *,uintptr_t),uintptr_t arg)287 heap_scan(struct dn_heap *h, int (*fn)(void *, uintptr_t),
288 uintptr_t arg)
289 {
290 int i, ret, found;
291
292 for (i = found = 0 ; i < h->elements ;) {
293 ret = fn(h->p[i].object, arg);
294 if (ret & HEAP_SCAN_DEL) {
295 h->elements-- ;
296 h->p[i] = h->p[h->elements] ;
297 found++ ;
298 } else
299 i++ ;
300 if (ret & HEAP_SCAN_END)
301 break;
302 }
303 if (found)
304 heapify(h);
305 return found;
306 }
307
308 /*
309 * cleanup the heap and free data structure
310 */
311 void
heap_free(struct dn_heap * h)312 heap_free(struct dn_heap *h)
313 {
314 if (h->size >0 )
315 free(h->p, M_DN_HEAP);
316 bzero(h, sizeof(*h) );
317 }
318
319 /*
320 * hash table support.
321 */
322
323 struct dn_ht {
324 int buckets; /* how many buckets, really buckets - 1*/
325 int entries; /* how many entries */
326 int ofs; /* offset of link field */
327 uint32_t (*hash)(uintptr_t, int, void *arg);
328 int (*match)(void *_el, uintptr_t key, int, void *);
329 void *(*newh)(uintptr_t, int, void *);
330 void **ht; /* bucket heads */
331 };
332 /*
333 * Initialize, allocating bucket pointers inline.
334 * Recycle previous record if possible.
335 * If the 'newh' function is not supplied, we assume that the
336 * key passed to ht_find is the same object to be stored in.
337 */
338 struct dn_ht *
dn_ht_init(struct dn_ht * ht,int buckets,int ofs,uint32_t (* h)(uintptr_t,int,void *),int (* match)(void *,uintptr_t,int,void *),void * (* newh)(uintptr_t,int,void *))339 dn_ht_init(struct dn_ht *ht, int buckets, int ofs,
340 uint32_t (*h)(uintptr_t, int, void *),
341 int (*match)(void *, uintptr_t, int, void *),
342 void *(*newh)(uintptr_t, int, void *))
343 {
344 int l;
345
346 /*
347 * Notes about rounding bucket size to a power of two.
348 * Given the original bucket size, we compute the nearest lower and
349 * higher power of two, minus 1 (respectively b_min and b_max) because
350 * this value will be used to do an AND with the index returned
351 * by hash function.
352 * To choice between these two values, the original bucket size is
353 * compared with b_min. If the original size is greater than 4/3 b_min,
354 * we round the bucket size to b_max, else to b_min.
355 * This ratio try to round to the nearest power of two, advantaging
356 * the greater size if the different between two power is relatively
357 * big.
358 * Rounding the bucket size to a power of two avoid the use of
359 * module when calculating the correct bucket.
360 * The ht->buckets variable store the bucket size - 1 to simply
361 * do an AND between the index returned by hash function and ht->bucket
362 * instead of a module.
363 */
364 int b_min; /* min buckets */
365 int b_max; /* max buckets */
366 int b_ori; /* original buckets */
367
368 if (h == NULL || match == NULL) {
369 printf("--- missing hash or match function");
370 return NULL;
371 }
372 if (buckets < 1 || buckets > 65536)
373 return NULL;
374
375 b_ori = buckets;
376 /* calculate next power of 2, - 1*/
377 buckets |= buckets >> 1;
378 buckets |= buckets >> 2;
379 buckets |= buckets >> 4;
380 buckets |= buckets >> 8;
381 buckets |= buckets >> 16;
382
383 b_max = buckets; /* Next power */
384 b_min = buckets >> 1; /* Previous power */
385
386 /* Calculate the 'nearest' bucket size */
387 if (b_min * 4000 / 3000 < b_ori)
388 buckets = b_max;
389 else
390 buckets = b_min;
391
392 if (ht) { /* see if we can reuse */
393 if (buckets <= ht->buckets) {
394 ht->buckets = buckets;
395 } else {
396 /* free pointers if not allocated inline */
397 if (ht->ht != (void *)(ht + 1))
398 free(ht->ht, M_DN_HEAP);
399 free(ht, M_DN_HEAP);
400 ht = NULL;
401 }
402 }
403 if (ht == NULL) {
404 /* Allocate buckets + 1 entries because buckets is use to
405 * do the AND with the index returned by hash function
406 */
407 l = sizeof(*ht) + (buckets + 1) * sizeof(void **);
408 ht = malloc(l, M_DN_HEAP, M_NOWAIT | M_ZERO);
409 }
410 if (ht) {
411 ht->ht = (void **)(ht + 1);
412 ht->buckets = buckets;
413 ht->ofs = ofs;
414 ht->hash = h;
415 ht->match = match;
416 ht->newh = newh;
417 }
418 return ht;
419 }
420
421 /* dummy callback for dn_ht_free to unlink all */
422 static int
do_del(void * obj,void * arg)423 do_del(void *obj, void *arg)
424 {
425 (void)obj;
426 (void)arg;
427 return DNHT_SCAN_DEL;
428 }
429
430 void
dn_ht_free(struct dn_ht * ht,int flags)431 dn_ht_free(struct dn_ht *ht, int flags)
432 {
433 if (ht == NULL)
434 return;
435 if (flags & DNHT_REMOVE) {
436 (void)dn_ht_scan(ht, do_del, NULL);
437 } else {
438 if (ht->ht && ht->ht != (void *)(ht + 1))
439 free(ht->ht, M_DN_HEAP);
440 free(ht, M_DN_HEAP);
441 }
442 }
443
444 int
dn_ht_entries(struct dn_ht * ht)445 dn_ht_entries(struct dn_ht *ht)
446 {
447 return ht ? ht->entries : 0;
448 }
449
450 /* lookup and optionally create or delete element */
451 void *
dn_ht_find(struct dn_ht * ht,uintptr_t key,int flags,void * arg)452 dn_ht_find(struct dn_ht *ht, uintptr_t key, int flags, void *arg)
453 {
454 int i;
455 void **pp, *p;
456
457 if (ht == NULL) /* easy on an empty hash */
458 return NULL;
459 i = (ht->buckets == 1) ? 0 :
460 (ht->hash(key, flags, arg) & ht->buckets);
461
462 for (pp = &ht->ht[i]; (p = *pp); pp = (void **)((char *)p + ht->ofs)) {
463 if (flags & DNHT_MATCH_PTR) {
464 if (key == (uintptr_t)p)
465 break;
466 } else if (ht->match(p, key, flags, arg)) /* found match */
467 break;
468 }
469 if (p) {
470 if (flags & DNHT_REMOVE) {
471 /* link in the next element */
472 *pp = *(void **)((char *)p + ht->ofs);
473 *(void **)((char *)p + ht->ofs) = NULL;
474 ht->entries--;
475 }
476 } else if (flags & DNHT_INSERT) {
477 // printf("%s before calling new, bucket %d ofs %d\n",
478 // __FUNCTION__, i, ht->ofs);
479 p = ht->newh ? ht->newh(key, flags, arg) : (void *)key;
480 // printf("%s newh returns %p\n", __FUNCTION__, p);
481 if (p) {
482 ht->entries++;
483 *(void **)((char *)p + ht->ofs) = ht->ht[i];
484 ht->ht[i] = p;
485 }
486 }
487 return p;
488 }
489
490 /*
491 * do a scan with the option to delete the object. Extract next before
492 * running the callback because the element may be destroyed there.
493 */
494 int
dn_ht_scan(struct dn_ht * ht,int (* fn)(void *,void *),void * arg)495 dn_ht_scan(struct dn_ht *ht, int (*fn)(void *, void *), void *arg)
496 {
497 int i, ret, found = 0;
498 void **curp, *cur, *next;
499
500 if (ht == NULL || fn == NULL)
501 return 0;
502 for (i = 0; i <= ht->buckets; i++) {
503 curp = &ht->ht[i];
504 while ( (cur = *curp) != NULL) {
505 next = *(void **)((char *)cur + ht->ofs);
506 ret = fn(cur, arg);
507 if (ret & DNHT_SCAN_DEL) {
508 found++;
509 ht->entries--;
510 *curp = next;
511 } else {
512 curp = (void **)((char *)cur + ht->ofs);
513 }
514 if (ret & DNHT_SCAN_END)
515 return found;
516 }
517 }
518 return found;
519 }
520
521 /*
522 * Similar to dn_ht_scan(), except that the scan is performed only
523 * in the bucket 'bucket'. The function returns a correct bucket number if
524 * the original is invalid.
525 * If the callback returns DNHT_SCAN_END, the function move the ht->ht[i]
526 * pointer to the last entry processed. Moreover, the bucket number passed
527 * by caller is decremented, because usually the caller increment it.
528 */
529 int
dn_ht_scan_bucket(struct dn_ht * ht,int * bucket,int (* fn)(void *,void *),void * arg)530 dn_ht_scan_bucket(struct dn_ht *ht, int *bucket, int (*fn)(void *, void *),
531 void *arg)
532 {
533 int i, ret, found = 0;
534 void **curp, *cur, *next;
535
536 if (ht == NULL || fn == NULL)
537 return 0;
538 if (*bucket > ht->buckets)
539 *bucket = 0;
540 i = *bucket;
541
542 curp = &ht->ht[i];
543 while ( (cur = *curp) != NULL) {
544 next = *(void **)((char *)cur + ht->ofs);
545 ret = fn(cur, arg);
546 if (ret & DNHT_SCAN_DEL) {
547 found++;
548 ht->entries--;
549 *curp = next;
550 } else {
551 curp = (void **)((char *)cur + ht->ofs);
552 }
553 if (ret & DNHT_SCAN_END)
554 return found;
555 }
556 return found;
557 }
558