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

Cache object: 7c6d96061de7a8fd8b6fbdd012864207


[ 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.