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