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$ 31 */ 32 33 #include <sys/cdefs.h> 34 #include <sys/param.h> 35 #ifdef _KERNEL 36 __FBSDID("$FreeBSD$"); 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) volatile int __dummy__ ## a __attribute__((__unused__)) 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 } 553