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__)) 55 static void *my_malloc(int s) { return malloc(s); } 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 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 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 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 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 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 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 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 * 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 423 do_del(void *obj, void *arg) 424 { 425 (void)obj; 426 (void)arg; 427 return DNHT_SCAN_DEL; 428 } 429 430 void 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 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 * 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 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 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