1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2004 Poul-Henning Kamp 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 * Unit number allocation functions. 30 * 31 * These functions implement a mixed run-length/bitmap management of unit 32 * number spaces in a very memory efficient manner. 33 * 34 * Allocation policy is always lowest free number first. 35 * 36 * A return value of -1 signals that no more unit numbers are available. 37 * 38 * There is no cost associated with the range of unitnumbers, so unless 39 * the resource really is finite, specify INT_MAX to new_unrhdr() and 40 * forget about checking the return value. 41 * 42 * If a mutex is not provided when the unit number space is created, a 43 * default global mutex is used. The advantage to passing a mutex in, is 44 * that the alloc_unrl() function can be called with the mutex already 45 * held (it will not be released by alloc_unrl()). 46 * 47 * The allocation function alloc_unr{l}() never sleeps (but it may block on 48 * the mutex of course). 49 * 50 * Freeing a unit number may require allocating memory, and can therefore 51 * sleep so the free_unr() function does not come in a pre-locked variant. 52 * 53 * A userland test program is included. 54 * 55 * Memory usage is a very complex function of the exact allocation 56 * pattern, but always very compact: 57 * * For the very typical case where a single unbroken run of unit 58 * numbers are allocated 44 bytes are used on i386. 59 * * For a unit number space of 1000 units and the random pattern 60 * in the usermode test program included, the worst case usage 61 * was 252 bytes on i386 for 500 allocated and 500 free units. 62 * * For a unit number space of 10000 units and the random pattern 63 * in the usermode test program included, the worst case usage 64 * was 798 bytes on i386 for 5000 allocated and 5000 free units. 65 * * The worst case is where every other unit number is allocated and 66 * the rest are free. In that case 44 + N/4 bytes are used where 67 * N is the number of the highest unit allocated. 68 */ 69 70 #include <sys/param.h> 71 #include <sys/types.h> 72 #include <sys/_unrhdr.h> 73 74 #ifdef _KERNEL 75 76 #include <sys/bitstring.h> 77 #include <sys/malloc.h> 78 #include <sys/kernel.h> 79 #include <sys/systm.h> 80 #include <sys/limits.h> 81 #include <sys/lock.h> 82 #include <sys/mutex.h> 83 84 /* 85 * In theory it would be smarter to allocate the individual blocks 86 * with the zone allocator, but at this time the expectation is that 87 * there will typically not even be enough allocations to fill a single 88 * page, so we stick with malloc for now. 89 */ 90 static MALLOC_DEFINE(M_UNIT, "Unitno", "Unit number allocation"); 91 92 #define Malloc(foo) malloc(foo, M_UNIT, M_WAITOK | M_ZERO) 93 #define Free(foo) free(foo, M_UNIT) 94 95 static struct mtx unitmtx; 96 97 MTX_SYSINIT(unit, &unitmtx, "unit# allocation", MTX_DEF); 98 99 #else /* ...USERLAND */ 100 101 #include <bitstring.h> 102 #include <err.h> 103 #include <errno.h> 104 #include <getopt.h> 105 #include <stdbool.h> 106 #include <stdio.h> 107 #include <stdlib.h> 108 #include <string.h> 109 110 #define KASSERT(cond, arg) \ 111 do { \ 112 if (!(cond)) { \ 113 printf arg; \ 114 abort(); \ 115 } \ 116 } while (0) 117 118 static int no_alloc; 119 #define Malloc(foo) _Malloc(foo, __LINE__) 120 static void * 121 _Malloc(size_t foo, int line) 122 { 123 124 KASSERT(no_alloc == 0, ("malloc in wrong place() line %d", line)); 125 return (calloc(foo, 1)); 126 } 127 #define Free(foo) free(foo) 128 129 struct unrhdr; 130 131 #define UNR_NO_MTX ((void *)(uintptr_t)-1) 132 133 struct mtx { 134 int state; 135 } unitmtx; 136 137 static void 138 mtx_lock(struct mtx *mp) 139 { 140 KASSERT(mp->state == 0, ("mutex already locked")); 141 mp->state = 1; 142 } 143 144 static void 145 mtx_unlock(struct mtx *mp) 146 { 147 KASSERT(mp->state == 1, ("mutex not locked")); 148 mp->state = 0; 149 } 150 151 #define MA_OWNED 9 152 153 static void 154 mtx_assert(struct mtx *mp, int flag) 155 { 156 if (flag == MA_OWNED) { 157 KASSERT(mp->state == 1, ("mtx_assert(MA_OWNED) not true")); 158 } 159 } 160 161 #define CTASSERT(foo) 162 #define WITNESS_WARN(flags, lock, fmt, ...) (void)0 163 164 #endif /* USERLAND */ 165 166 /* 167 * This is our basic building block. 168 * 169 * It can be used in three different ways depending on the value of the ptr 170 * element: 171 * If ptr is NULL, it represents a run of free items. 172 * If ptr points to the unrhdr it represents a run of allocated items. 173 * Otherwise it points to a bitstring of allocated items. 174 * 175 * For runs the len field is the length of the run. 176 * For bitmaps the len field represents the number of allocated items. 177 * 178 * The bitmap is the same size as struct unr to optimize memory management. 179 * 180 * Two special ranges are not covered by unrs: 181 * - at the start of the allocator space, all elements in [low, low + first) 182 * are allocated; 183 * - at the end of the allocator space, all elements in [high - last, high] 184 * are free. 185 */ 186 struct unr { 187 TAILQ_ENTRY(unr) list; 188 u_int len; 189 void *ptr; 190 }; 191 192 struct unrb { 193 bitstr_t map[sizeof(struct unr) / sizeof(bitstr_t)]; 194 }; 195 196 CTASSERT((sizeof(struct unr) % sizeof(bitstr_t)) == 0); 197 198 /* Number of bits we can store in the bitmap */ 199 #define NBITS (NBBY * sizeof(((struct unrb *)NULL)->map)) 200 201 static inline bool 202 is_bitmap(struct unrhdr *uh, struct unr *up) 203 { 204 return (up->ptr != uh && up->ptr != NULL); 205 } 206 207 /* Is the unrb empty in at least the first len bits? */ 208 static inline bool 209 ub_empty(struct unrb *ub, int len) { 210 int first_set; 211 212 bit_ffs(ub->map, len, &first_set); 213 return (first_set == -1); 214 } 215 216 /* Is the unrb full? That is, is the number of set elements equal to len? */ 217 static inline bool 218 ub_full(struct unrb *ub, int len) 219 { 220 int first_clear; 221 222 bit_ffc(ub->map, len, &first_clear); 223 return (first_clear == -1); 224 } 225 226 /* 227 * start: ipos = -1, upos = NULL; 228 * end: ipos = -1, upos = uh 229 */ 230 struct unrhdr_iter { 231 struct unrhdr *uh; 232 int ipos; 233 int upos_first_item; 234 void *upos; 235 }; 236 237 void * 238 create_iter_unr(struct unrhdr *uh) 239 { 240 struct unrhdr_iter *iter; 241 242 iter = Malloc(sizeof(*iter)); 243 iter->ipos = -1; 244 iter->uh = uh; 245 iter->upos = NULL; 246 iter->upos_first_item = -1; 247 return (iter); 248 } 249 250 static void 251 next_iter_unrl(struct unrhdr *uh, struct unrhdr_iter *iter) 252 { 253 struct unr *up; 254 struct unrb *ub; 255 u_int y; 256 int c; 257 258 if (iter->ipos == -1) { 259 if (iter->upos == uh) 260 return; 261 y = uh->low - 1; 262 if (uh->first == 0) { 263 up = TAILQ_FIRST(&uh->head); 264 if (up == NULL) { 265 iter->upos = uh; 266 return; 267 } 268 iter->upos = up; 269 if (up->ptr == NULL) 270 iter->upos = NULL; 271 else 272 iter->upos_first_item = uh->low; 273 } 274 } else { 275 y = iter->ipos; 276 } 277 278 up = iter->upos; 279 280 /* Special case for the compacted [low, first) run. */ 281 if (up == NULL) { 282 if (y + 1 < uh->low + uh->first) { 283 iter->ipos = y + 1; 284 return; 285 } 286 up = iter->upos = TAILQ_FIRST(&uh->head); 287 iter->upos_first_item = uh->low + uh->first; 288 } 289 290 for (;;) { 291 if (y + 1 < iter->upos_first_item + up->len) { 292 if (up->ptr == uh) { 293 iter->ipos = y + 1; 294 return; 295 } else if (is_bitmap(uh, up)) { 296 ub = up->ptr; 297 bit_ffs_at(&ub->map[0], 298 y + 1 - iter->upos_first_item, 299 up->len, &c); 300 if (c != -1) { 301 iter->ipos = iter->upos_first_item + c; 302 return; 303 } 304 } 305 } 306 iter->upos_first_item += up->len; 307 y = iter->upos_first_item - 1; 308 up = iter->upos = TAILQ_NEXT((struct unr *)iter->upos, list); 309 if (iter->upos == NULL) { 310 iter->ipos = -1; 311 iter->upos = uh; 312 return; 313 } 314 } 315 } 316 317 /* 318 * returns -1 on end, otherwise the next element 319 */ 320 int 321 next_iter_unr(void *handle) 322 { 323 struct unrhdr *uh; 324 struct unrhdr_iter *iter; 325 326 iter = handle; 327 uh = iter->uh; 328 if (uh->mtx != NULL) 329 mtx_lock(uh->mtx); 330 next_iter_unrl(uh, iter); 331 if (uh->mtx != NULL) 332 mtx_unlock(uh->mtx); 333 return (iter->ipos); 334 } 335 336 void 337 free_iter_unr(void *handle) 338 { 339 Free(handle); 340 } 341 342 #if defined(DIAGNOSTIC) || !defined(_KERNEL) 343 /* 344 * Consistency check function. 345 * 346 * Checks the internal consistency as well as we can. 347 * 348 * Called at all boundaries of this API. 349 */ 350 static void 351 check_unrhdr(struct unrhdr *uh, int line) 352 { 353 struct unr *up; 354 struct unrb *ub; 355 int w; 356 u_int y, z; 357 358 y = uh->first; 359 z = 0; 360 TAILQ_FOREACH(up, &uh->head, list) { 361 z++; 362 if (is_bitmap(uh, up)) { 363 ub = up->ptr; 364 KASSERT (up->len <= NBITS, 365 ("UNR inconsistency: len %u max %zd (line %d)\n", 366 up->len, NBITS, line)); 367 z++; 368 w = 0; 369 bit_count(ub->map, 0, up->len, &w); 370 y += w; 371 } else if (up->ptr != NULL) 372 y += up->len; 373 } 374 KASSERT (y == uh->busy, 375 ("UNR inconsistency: items %u found %u (line %d)\n", 376 uh->busy, y, line)); 377 KASSERT (z == uh->alloc, 378 ("UNR inconsistency: chunks %u found %u (line %d)\n", 379 uh->alloc, z, line)); 380 } 381 382 #else 383 384 static __inline void 385 check_unrhdr(struct unrhdr *uh __unused, int line __unused) 386 { 387 388 } 389 390 #endif 391 392 /* 393 * Userland memory management. Just use calloc and keep track of how 394 * many elements we have allocated for check_unrhdr(). 395 */ 396 397 static __inline void * 398 new_unr(struct unrhdr *uh, void **p1, void **p2) 399 { 400 void *p; 401 402 uh->alloc++; 403 KASSERT(*p1 != NULL || *p2 != NULL, ("Out of cached memory")); 404 if (*p1 != NULL) { 405 p = *p1; 406 *p1 = NULL; 407 return (p); 408 } else { 409 p = *p2; 410 *p2 = NULL; 411 return (p); 412 } 413 } 414 415 static __inline void 416 delete_unr(struct unrhdr *uh, void *ptr) 417 { 418 struct unr *up; 419 420 uh->alloc--; 421 up = ptr; 422 TAILQ_INSERT_TAIL(&uh->ppfree, up, list); 423 } 424 425 void 426 clean_unrhdrl(struct unrhdr *uh) 427 { 428 struct unr *up; 429 430 if (uh->mtx != NULL) 431 mtx_assert(uh->mtx, MA_OWNED); 432 while ((up = TAILQ_FIRST(&uh->ppfree)) != NULL) { 433 TAILQ_REMOVE(&uh->ppfree, up, list); 434 if (uh->mtx != NULL) 435 mtx_unlock(uh->mtx); 436 Free(up); 437 if (uh->mtx != NULL) 438 mtx_lock(uh->mtx); 439 } 440 441 } 442 443 void 444 clean_unrhdr(struct unrhdr *uh) 445 { 446 447 if (uh->mtx != NULL) 448 mtx_lock(uh->mtx); 449 clean_unrhdrl(uh); 450 if (uh->mtx != NULL) 451 mtx_unlock(uh->mtx); 452 } 453 454 void 455 init_unrhdr(struct unrhdr *uh, int low, int high, struct mtx *mutex) 456 { 457 458 KASSERT(low >= 0 && low <= high, 459 ("UNR: use error: new_unrhdr(%d, %d)", low, high)); 460 if (mutex == UNR_NO_MTX) 461 uh->mtx = NULL; 462 else if (mutex != NULL) 463 uh->mtx = mutex; 464 else 465 uh->mtx = &unitmtx; 466 TAILQ_INIT(&uh->head); 467 TAILQ_INIT(&uh->ppfree); 468 uh->low = low; 469 uh->high = high; 470 uh->first = 0; 471 uh->last = 1 + (high - low); 472 uh->busy = 0; 473 uh->alloc = 0; 474 check_unrhdr(uh, __LINE__); 475 } 476 477 /* 478 * Allocate a new unrheader set. 479 * 480 * Highest and lowest valid values given as parameters. 481 */ 482 483 struct unrhdr * 484 new_unrhdr(int low, int high, struct mtx *mutex) 485 { 486 struct unrhdr *uh; 487 488 uh = Malloc(sizeof *uh); 489 init_unrhdr(uh, low, high, mutex); 490 return (uh); 491 } 492 493 void 494 delete_unrhdr(struct unrhdr *uh) 495 { 496 497 check_unrhdr(uh, __LINE__); 498 KASSERT(uh->busy == 0, ("unrhdr has %u allocations", uh->busy)); 499 KASSERT(uh->alloc == 0, ("UNR memory leak in delete_unrhdr")); 500 KASSERT(TAILQ_FIRST(&uh->ppfree) == NULL, 501 ("unrhdr has postponed item for free")); 502 Free(uh); 503 } 504 505 void 506 clear_unrhdr(struct unrhdr *uh) 507 { 508 struct unr *up, *uq; 509 510 KASSERT(TAILQ_EMPTY(&uh->ppfree), 511 ("unrhdr has postponed item for free")); 512 TAILQ_FOREACH_SAFE(up, &uh->head, list, uq) { 513 if (up->ptr != uh) { 514 Free(up->ptr); 515 } 516 Free(up); 517 } 518 uh->busy = 0; 519 uh->alloc = 0; 520 init_unrhdr(uh, uh->low, uh->high, uh->mtx); 521 522 check_unrhdr(uh, __LINE__); 523 } 524 525 /* 526 * Look for sequence of items which can be combined into a bitmap, if 527 * multiple are present, take the one which saves most memory. 528 * 529 * Return (1) if a sequence was found to indicate that another call 530 * might be able to do more. Return (0) if we found no suitable sequence. 531 * 532 * NB: called from alloc_unr(), no new memory allocation allowed. 533 */ 534 static int 535 optimize_unr(struct unrhdr *uh) 536 { 537 struct unr *up, *uf, *us; 538 struct unrb *ub, *ubf; 539 u_int a, l, ba; 540 541 /* 542 * Look for the run of items (if any) which when collapsed into 543 * a bitmap would save most memory. 544 */ 545 us = NULL; 546 ba = 0; 547 TAILQ_FOREACH(uf, &uh->head, list) { 548 if (uf->len >= NBITS) 549 continue; 550 a = 1; 551 if (is_bitmap(uh, uf)) 552 a++; 553 l = uf->len; 554 up = uf; 555 while (1) { 556 up = TAILQ_NEXT(up, list); 557 if (up == NULL) 558 break; 559 if ((up->len + l) > NBITS) 560 break; 561 a++; 562 if (is_bitmap(uh, up)) 563 a++; 564 l += up->len; 565 } 566 if (a > ba) { 567 ba = a; 568 us = uf; 569 } 570 } 571 if (ba < 3) 572 return (0); 573 574 /* 575 * If the first element is not a bitmap, make it one. 576 * Trying to do so without allocating more memory complicates things 577 * a bit 578 */ 579 if (!is_bitmap(uh, us)) { 580 uf = TAILQ_NEXT(us, list); 581 TAILQ_REMOVE(&uh->head, us, list); 582 a = us->len; 583 l = us->ptr == uh ? 1 : 0; 584 ub = (void *)us; 585 bit_nclear(ub->map, 0, NBITS - 1); 586 if (l) 587 bit_nset(ub->map, 0, a); 588 if (!is_bitmap(uh, uf)) { 589 if (uf->ptr == NULL) 590 bit_nclear(ub->map, a, a + uf->len - 1); 591 else 592 bit_nset(ub->map, a, a + uf->len - 1); 593 uf->ptr = ub; 594 uf->len += a; 595 us = uf; 596 } else { 597 ubf = uf->ptr; 598 for (l = 0; l < uf->len; l++, a++) { 599 if (bit_test(ubf->map, l)) 600 bit_set(ub->map, a); 601 else 602 bit_clear(ub->map, a); 603 } 604 uf->len = a; 605 delete_unr(uh, uf->ptr); 606 uf->ptr = ub; 607 us = uf; 608 } 609 } 610 ub = us->ptr; 611 while (1) { 612 uf = TAILQ_NEXT(us, list); 613 if (uf == NULL) 614 return (1); 615 if (uf->len + us->len > NBITS) 616 return (1); 617 if (uf->ptr == NULL) { 618 bit_nclear(ub->map, us->len, us->len + uf->len - 1); 619 us->len += uf->len; 620 TAILQ_REMOVE(&uh->head, uf, list); 621 delete_unr(uh, uf); 622 } else if (uf->ptr == uh) { 623 bit_nset(ub->map, us->len, us->len + uf->len - 1); 624 us->len += uf->len; 625 TAILQ_REMOVE(&uh->head, uf, list); 626 delete_unr(uh, uf); 627 } else { 628 ubf = uf->ptr; 629 for (l = 0; l < uf->len; l++, us->len++) { 630 if (bit_test(ubf->map, l)) 631 bit_set(ub->map, us->len); 632 else 633 bit_clear(ub->map, us->len); 634 } 635 TAILQ_REMOVE(&uh->head, uf, list); 636 delete_unr(uh, ubf); 637 delete_unr(uh, uf); 638 } 639 } 640 } 641 642 /* 643 * See if a given unr should be collapsed with a neighbor. 644 * 645 * NB: called from alloc_unr(), no new memory allocation allowed. 646 */ 647 static void 648 collapse_unr(struct unrhdr *uh, struct unr *up) 649 { 650 struct unr *upp; 651 struct unrb *ub; 652 653 /* If bitmap is all set or clear, change it to runlength */ 654 if (is_bitmap(uh, up)) { 655 ub = up->ptr; 656 if (ub_full(ub, up->len)) { 657 delete_unr(uh, up->ptr); 658 up->ptr = uh; 659 } else if (ub_empty(ub, up->len)) { 660 delete_unr(uh, up->ptr); 661 up->ptr = NULL; 662 } 663 } 664 665 /* If nothing left in runlength, delete it */ 666 if (up->len == 0) { 667 upp = TAILQ_PREV(up, unrhd, list); 668 if (upp == NULL) 669 upp = TAILQ_NEXT(up, list); 670 TAILQ_REMOVE(&uh->head, up, list); 671 delete_unr(uh, up); 672 up = upp; 673 } 674 675 /* If we have "hot-spot" still, merge with neighbor if possible */ 676 if (up != NULL) { 677 upp = TAILQ_PREV(up, unrhd, list); 678 if (upp != NULL && up->ptr == upp->ptr) { 679 up->len += upp->len; 680 TAILQ_REMOVE(&uh->head, upp, list); 681 delete_unr(uh, upp); 682 } 683 upp = TAILQ_NEXT(up, list); 684 if (upp != NULL && up->ptr == upp->ptr) { 685 up->len += upp->len; 686 TAILQ_REMOVE(&uh->head, upp, list); 687 delete_unr(uh, upp); 688 } 689 } 690 691 /* Merge into ->first if possible */ 692 upp = TAILQ_FIRST(&uh->head); 693 if (upp != NULL && upp->ptr == uh) { 694 uh->first += upp->len; 695 TAILQ_REMOVE(&uh->head, upp, list); 696 delete_unr(uh, upp); 697 if (up == upp) 698 up = NULL; 699 } 700 701 /* Merge into ->last if possible */ 702 upp = TAILQ_LAST(&uh->head, unrhd); 703 if (upp != NULL && upp->ptr == NULL) { 704 uh->last += upp->len; 705 TAILQ_REMOVE(&uh->head, upp, list); 706 delete_unr(uh, upp); 707 if (up == upp) 708 up = NULL; 709 } 710 711 /* Try to make bitmaps */ 712 while (optimize_unr(uh)) 713 continue; 714 } 715 716 /* 717 * Allocate a free unr. 718 */ 719 int 720 alloc_unrl(struct unrhdr *uh) 721 { 722 struct unr *up; 723 struct unrb *ub; 724 u_int x; 725 int y; 726 727 if (uh->mtx != NULL) 728 mtx_assert(uh->mtx, MA_OWNED); 729 check_unrhdr(uh, __LINE__); 730 x = uh->low + uh->first; 731 732 up = TAILQ_FIRST(&uh->head); 733 734 /* 735 * If we have an ideal split, just adjust the first+last 736 */ 737 if (up == NULL && uh->last > 0) { 738 uh->first++; 739 uh->last--; 740 uh->busy++; 741 return (x); 742 } 743 744 /* 745 * We can always allocate from the first list element, so if we have 746 * nothing on the list, we must have run out of unit numbers. 747 */ 748 if (up == NULL) 749 return (-1); 750 751 KASSERT(up->ptr != uh, ("UNR first element is allocated")); 752 753 if (up->ptr == NULL) { /* free run */ 754 uh->first++; 755 up->len--; 756 } else { /* bitmap */ 757 ub = up->ptr; 758 bit_ffc(ub->map, up->len, &y); 759 KASSERT(y != -1, ("UNR corruption: No clear bit in bitmap.")); 760 bit_set(ub->map, y); 761 x += y; 762 } 763 uh->busy++; 764 collapse_unr(uh, up); 765 return (x); 766 } 767 768 int 769 alloc_unr(struct unrhdr *uh) 770 { 771 int i; 772 773 if (uh->mtx != NULL) 774 mtx_lock(uh->mtx); 775 i = alloc_unrl(uh); 776 clean_unrhdrl(uh); 777 if (uh->mtx != NULL) 778 mtx_unlock(uh->mtx); 779 return (i); 780 } 781 782 static int 783 alloc_unr_specificl(struct unrhdr *uh, u_int item, void **p1, void **p2) 784 { 785 struct unr *up, *upn; 786 struct unrb *ub; 787 u_int i, last, tl; 788 789 if (uh->mtx != NULL) 790 mtx_assert(uh->mtx, MA_OWNED); 791 792 if (item < uh->low + uh->first || item > uh->high) 793 return (-1); 794 795 up = TAILQ_FIRST(&uh->head); 796 /* Ideal split. */ 797 if (up == NULL && item - uh->low == uh->first) { 798 uh->first++; 799 uh->last--; 800 uh->busy++; 801 check_unrhdr(uh, __LINE__); 802 return (item); 803 } 804 805 i = item - uh->low - uh->first; 806 807 if (up == NULL) { 808 up = new_unr(uh, p1, p2); 809 up->ptr = NULL; 810 up->len = i; 811 TAILQ_INSERT_TAIL(&uh->head, up, list); 812 up = new_unr(uh, p1, p2); 813 up->ptr = uh; 814 up->len = 1; 815 TAILQ_INSERT_TAIL(&uh->head, up, list); 816 uh->last = uh->high - uh->low - i; 817 uh->busy++; 818 check_unrhdr(uh, __LINE__); 819 return (item); 820 } else { 821 /* Find the item which contains the unit we want to allocate. */ 822 TAILQ_FOREACH(up, &uh->head, list) { 823 if (up->len > i) 824 break; 825 i -= up->len; 826 } 827 } 828 829 if (up == NULL) { 830 if (i > 0) { 831 up = new_unr(uh, p1, p2); 832 up->ptr = NULL; 833 up->len = i; 834 TAILQ_INSERT_TAIL(&uh->head, up, list); 835 } 836 up = new_unr(uh, p1, p2); 837 up->ptr = uh; 838 up->len = 1; 839 TAILQ_INSERT_TAIL(&uh->head, up, list); 840 goto done; 841 } 842 843 if (is_bitmap(uh, up)) { 844 ub = up->ptr; 845 if (bit_test(ub->map, i) == 0) { 846 bit_set(ub->map, i); 847 goto done; 848 } else 849 return (-1); 850 } else if (up->ptr == uh) 851 return (-1); 852 853 KASSERT(up->ptr == NULL, 854 ("alloc_unr_specificl: up->ptr != NULL (up=%p)", up)); 855 856 /* Split off the tail end, if any. */ 857 tl = up->len - (1 + i); 858 if (tl > 0) { 859 upn = new_unr(uh, p1, p2); 860 upn->ptr = NULL; 861 upn->len = tl; 862 TAILQ_INSERT_AFTER(&uh->head, up, upn, list); 863 } 864 865 /* Split off head end, if any */ 866 if (i > 0) { 867 upn = new_unr(uh, p1, p2); 868 upn->len = i; 869 upn->ptr = NULL; 870 TAILQ_INSERT_BEFORE(up, upn, list); 871 } 872 up->len = 1; 873 up->ptr = uh; 874 875 done: 876 last = uh->high - uh->low - (item - uh->low); 877 if (uh->last > last) 878 uh->last = last; 879 uh->busy++; 880 collapse_unr(uh, up); 881 check_unrhdr(uh, __LINE__); 882 return (item); 883 } 884 885 int 886 alloc_unr_specific(struct unrhdr *uh, u_int item) 887 { 888 void *p1, *p2; 889 int i; 890 891 WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "alloc_unr_specific"); 892 893 p1 = Malloc(sizeof(struct unr)); 894 p2 = Malloc(sizeof(struct unr)); 895 896 if (uh->mtx != NULL) 897 mtx_lock(uh->mtx); 898 i = alloc_unr_specificl(uh, item, &p1, &p2); 899 if (uh->mtx != NULL) 900 mtx_unlock(uh->mtx); 901 902 if (p1 != NULL) 903 Free(p1); 904 if (p2 != NULL) 905 Free(p2); 906 907 return (i); 908 } 909 910 /* 911 * Free a unr. 912 * 913 * If we can save unrs by using a bitmap, do so. 914 */ 915 static void 916 free_unrl(struct unrhdr *uh, u_int item, void **p1, void **p2) 917 { 918 struct unr *up, *upp, *upn; 919 struct unrb *ub; 920 u_int pl; 921 922 KASSERT(item >= uh->low && item <= uh->high, 923 ("UNR: free_unr(%u) out of range [%u...%u]", 924 item, uh->low, uh->high)); 925 check_unrhdr(uh, __LINE__); 926 item -= uh->low; 927 upp = TAILQ_FIRST(&uh->head); 928 /* 929 * Freeing in the ideal split case 930 */ 931 if (item + 1 == uh->first && upp == NULL) { 932 uh->last++; 933 uh->first--; 934 uh->busy--; 935 check_unrhdr(uh, __LINE__); 936 return; 937 } 938 /* 939 * Freeing in the ->first section. Create a run starting at the 940 * freed item. The code below will subdivide it. 941 */ 942 if (item < uh->first) { 943 up = new_unr(uh, p1, p2); 944 up->ptr = uh; 945 up->len = uh->first - item; 946 TAILQ_INSERT_HEAD(&uh->head, up, list); 947 uh->first -= up->len; 948 } 949 950 item -= uh->first; 951 952 /* Find the item which contains the unit we want to free */ 953 TAILQ_FOREACH(up, &uh->head, list) { 954 if (up->len > item) 955 break; 956 item -= up->len; 957 } 958 959 /* Handle bitmap items */ 960 if (is_bitmap(uh, up)) { 961 ub = up->ptr; 962 963 KASSERT(bit_test(ub->map, item) != 0, 964 ("UNR: Freeing free item %d (bitmap)\n", item)); 965 bit_clear(ub->map, item); 966 uh->busy--; 967 collapse_unr(uh, up); 968 return; 969 } 970 971 KASSERT(up->ptr == uh, ("UNR Freeing free item %d (run))\n", item)); 972 973 /* Just this one left, reap it */ 974 if (up->len == 1) { 975 up->ptr = NULL; 976 uh->busy--; 977 collapse_unr(uh, up); 978 return; 979 } 980 981 /* Check if we can shift the item into the previous 'free' run */ 982 upp = TAILQ_PREV(up, unrhd, list); 983 if (item == 0 && upp != NULL && upp->ptr == NULL) { 984 upp->len++; 985 up->len--; 986 uh->busy--; 987 collapse_unr(uh, up); 988 return; 989 } 990 991 /* Check if we can shift the item to the next 'free' run */ 992 upn = TAILQ_NEXT(up, list); 993 if (item == up->len - 1 && upn != NULL && upn->ptr == NULL) { 994 upn->len++; 995 up->len--; 996 uh->busy--; 997 collapse_unr(uh, up); 998 return; 999 } 1000 1001 /* Split off the tail end, if any. */ 1002 pl = up->len - (1 + item); 1003 if (pl > 0) { 1004 upp = new_unr(uh, p1, p2); 1005 upp->ptr = uh; 1006 upp->len = pl; 1007 TAILQ_INSERT_AFTER(&uh->head, up, upp, list); 1008 } 1009 1010 /* Split off head end, if any */ 1011 if (item > 0) { 1012 upp = new_unr(uh, p1, p2); 1013 upp->len = item; 1014 upp->ptr = uh; 1015 TAILQ_INSERT_BEFORE(up, upp, list); 1016 } 1017 up->len = 1; 1018 up->ptr = NULL; 1019 uh->busy--; 1020 collapse_unr(uh, up); 1021 } 1022 1023 void 1024 free_unr(struct unrhdr *uh, u_int item) 1025 { 1026 void *p1, *p2; 1027 1028 WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "free_unr"); 1029 p1 = Malloc(sizeof(struct unr)); 1030 p2 = Malloc(sizeof(struct unr)); 1031 if (uh->mtx != NULL) 1032 mtx_lock(uh->mtx); 1033 free_unrl(uh, item, &p1, &p2); 1034 clean_unrhdrl(uh); 1035 if (uh->mtx != NULL) 1036 mtx_unlock(uh->mtx); 1037 if (p1 != NULL) 1038 Free(p1); 1039 if (p2 != NULL) 1040 Free(p2); 1041 } 1042 1043 #ifdef _KERNEL 1044 #include "opt_ddb.h" 1045 #ifdef DDB 1046 #include <ddb/ddb.h> 1047 #endif 1048 #endif 1049 1050 #if (defined(_KERNEL) && defined(DDB)) || !defined(_KERNEL) 1051 1052 #if !defined(_KERNEL) 1053 #define db_printf printf 1054 #endif 1055 1056 static void 1057 print_unr(struct unrhdr *uh, struct unr *up) 1058 { 1059 u_int x; 1060 struct unrb *ub; 1061 1062 db_printf(" %p len = %5u ", up, up->len); 1063 if (up->ptr == NULL) 1064 db_printf("free\n"); 1065 else if (up->ptr == uh) 1066 db_printf("alloc\n"); 1067 else { 1068 ub = up->ptr; 1069 db_printf("bitmap ["); 1070 for (x = 0; x < up->len; x++) { 1071 if (bit_test(ub->map, x)) 1072 db_printf("#"); 1073 else 1074 db_printf(" "); 1075 } 1076 db_printf("]\n"); 1077 } 1078 } 1079 1080 static void 1081 print_unrhdr(struct unrhdr *uh) 1082 { 1083 struct unr *up; 1084 u_int x; 1085 1086 db_printf( 1087 "%p low = %u high = %u first = %u last = %u busy %u chunks = %u\n", 1088 uh, uh->low, uh->high, uh->first, uh->last, uh->busy, uh->alloc); 1089 x = uh->low + uh->first; 1090 TAILQ_FOREACH(up, &uh->head, list) { 1091 db_printf(" from = %5u", x); 1092 print_unr(uh, up); 1093 if (up->ptr == NULL || up->ptr == uh) 1094 x += up->len; 1095 else 1096 x += NBITS; 1097 } 1098 } 1099 1100 #endif 1101 1102 #if defined(_KERNEL) && defined(DDB) 1103 DB_SHOW_COMMAND(unrhdr, unrhdr_print_unrhdr) 1104 { 1105 if (!have_addr) { 1106 db_printf("show unrhdr addr\n"); 1107 return; 1108 } 1109 1110 print_unrhdr((struct unrhdr *)addr); 1111 } 1112 1113 static void 1114 print_unrhdr_iter(struct unrhdr_iter *iter) 1115 { 1116 db_printf("iter %p unrhdr %p ipos %d upos %p ufi %d\n", 1117 iter, iter->uh, iter->ipos, iter->upos, iter->upos_first_item); 1118 } 1119 1120 DB_SHOW_COMMAND(unrhdr_iter, unrhdr_print_iter) 1121 { 1122 if (!have_addr) { 1123 db_printf("show unrhdr_iter addr\n"); 1124 return; 1125 } 1126 1127 print_unrhdr_iter((struct unrhdr_iter *)addr); 1128 } 1129 #endif 1130 1131 #ifndef _KERNEL /* USERLAND test driver */ 1132 1133 /* 1134 * Simple stochastic test driver for the above functions. The code resides 1135 * here so that it can access static functions and structures. 1136 */ 1137 1138 static bool verbose; 1139 #define VPRINTF(...) {if (verbose) printf(__VA_ARGS__);} 1140 1141 static void 1142 test_alloc_unr(struct unrhdr *uh, u_int i, char a[]) 1143 { 1144 int j; 1145 1146 if (a[i]) { 1147 VPRINTF("F %u\n", i); 1148 free_unr(uh, i); 1149 a[i] = 0; 1150 } else { 1151 no_alloc = 1; 1152 j = alloc_unr(uh); 1153 if (j != -1) { 1154 a[j] = 1; 1155 VPRINTF("A %d\n", j); 1156 } 1157 no_alloc = 0; 1158 } 1159 } 1160 1161 static void 1162 test_alloc_unr_specific(struct unrhdr *uh, u_int i, char a[]) 1163 { 1164 int j; 1165 1166 j = alloc_unr_specific(uh, i); 1167 if (j == -1) { 1168 VPRINTF("F %u\n", i); 1169 a[i] = 0; 1170 free_unr(uh, i); 1171 } else { 1172 a[i] = 1; 1173 VPRINTF("A %d\n", j); 1174 } 1175 } 1176 1177 #define TBASE 7 1178 #define XSIZE 10 1179 #define ISIZE 1000 1180 1181 static int 1182 test_iter_compar(const void *a, const void *b) 1183 { 1184 return (*(const int *)a - *(const int *)b); 1185 } 1186 1187 static void 1188 test_iter_fill(int *vals, struct unrhdr *uh, int i, int v, int *res) 1189 { 1190 int x; 1191 1192 vals[i] = v; 1193 x = alloc_unr_specific(uh, v); 1194 if (x != v) { 1195 VPRINTF("alloc_unr_specific failed %d %d\n", x, v); 1196 *res = 1; 1197 } 1198 } 1199 1200 static void 1201 test_iter(void) 1202 { 1203 struct unrhdr *uh; 1204 void *ihandle; 1205 int vals[ISIZE]; 1206 int i, j, v, x, res; 1207 1208 res = 0; 1209 uh = new_unrhdr(TBASE, INT_MAX, NULL); 1210 for (i = 0; i < XSIZE; i++) { 1211 vals[i] = i + TBASE; 1212 x = alloc_unr_specific(uh, i + TBASE); 1213 if (x != i + TBASE) { 1214 VPRINTF("alloc_unr_specific failed %d %d\n", x, 1215 i + TBASE); 1216 res = 1; 1217 } 1218 } 1219 for (; i < ISIZE; i++) { 1220 for (;;) { 1221 again: 1222 v = arc4random_uniform(INT_MAX); 1223 if (v < TBASE) 1224 goto again; 1225 for (j = 0; j < i; j++) { 1226 if (v == vals[j] || v + 1 == vals[j]) 1227 goto again; 1228 } 1229 break; 1230 } 1231 test_iter_fill(vals, uh, i, v, &res); 1232 i++, v++; 1233 if (i < ISIZE) 1234 test_iter_fill(vals, uh, i, v, &res); 1235 } 1236 qsort(vals, ISIZE, sizeof(vals[0]), test_iter_compar); 1237 1238 ihandle = create_iter_unr(uh); 1239 i = 0; 1240 while ((v = next_iter_unr(ihandle)) != -1) { 1241 if (vals[i] != v) { 1242 VPRINTF("iter %d: iter %d != val %d\n", i, v, vals[i]); 1243 if (res == 0) { 1244 if (verbose) 1245 print_unrhdr(uh); 1246 res = 1; 1247 } 1248 } else { 1249 VPRINTF("iter %d: val %d\n", i, v); 1250 } 1251 i++; 1252 } 1253 free_iter_unr(ihandle); 1254 clean_unrhdr(uh); 1255 clear_unrhdr(uh); 1256 delete_unrhdr(uh); 1257 exit(res); 1258 } 1259 1260 static void 1261 usage(char **argv) 1262 { 1263 printf("%s [-h] [-i] [-r REPETITIONS] [-v]\n", argv[0]); 1264 } 1265 1266 int 1267 main(int argc, char **argv) 1268 { 1269 struct unrhdr *uh; 1270 char *a; 1271 long count = 10000; /* Number of unrs to test */ 1272 long reps = 1, m; 1273 int ch; 1274 u_int i; 1275 bool testing_iter; 1276 1277 verbose = false; 1278 testing_iter = false; 1279 1280 while ((ch = getopt(argc, argv, "hir:v")) != -1) { 1281 switch (ch) { 1282 case 'i': 1283 testing_iter = true; 1284 break; 1285 case 'r': 1286 errno = 0; 1287 reps = strtol(optarg, NULL, 0); 1288 if (errno == ERANGE || errno == EINVAL) { 1289 usage(argv); 1290 exit(2); 1291 } 1292 1293 break; 1294 case 'v': 1295 verbose = true; 1296 break; 1297 case 'h': 1298 default: 1299 usage(argv); 1300 exit(2); 1301 } 1302 } 1303 1304 setbuf(stdout, NULL); 1305 1306 if (testing_iter) 1307 test_iter(); 1308 1309 uh = new_unrhdr(0, count - 1, NULL); 1310 print_unrhdr(uh); 1311 1312 a = calloc(count, sizeof(char)); 1313 if (a == NULL) 1314 err(1, "calloc failed"); 1315 1316 printf("sizeof(struct unr) %zu\n", sizeof(struct unr)); 1317 printf("sizeof(struct unrb) %zu\n", sizeof(struct unrb)); 1318 printf("sizeof(struct unrhdr) %zu\n", sizeof(struct unrhdr)); 1319 printf("NBITS %lu\n", (unsigned long)NBITS); 1320 for (m = 0; m < count * reps; m++) { 1321 i = arc4random_uniform(count); 1322 #if 0 1323 if (a[i] && (j & 1)) 1324 continue; 1325 #endif 1326 if ((arc4random() & 1) != 0) 1327 test_alloc_unr(uh, i, a); 1328 else 1329 test_alloc_unr_specific(uh, i, a); 1330 1331 if (verbose) 1332 print_unrhdr(uh); 1333 check_unrhdr(uh, __LINE__); 1334 } 1335 for (i = 0; i < (u_int)count; i++) { 1336 if (a[i]) { 1337 if (verbose) { 1338 printf("C %u\n", i); 1339 print_unrhdr(uh); 1340 } 1341 free_unr(uh, i); 1342 } 1343 } 1344 print_unrhdr(uh); 1345 delete_unrhdr(uh); 1346 free(a); 1347 return (0); 1348 } 1349 #endif 1350