The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netpfil/ipfw/dn_heap.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 18f27e5e4ec5f55e4c187f9c8d9a032a


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.