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/libkern/linux_idr.c

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

    1 /*
    2  * Copyright (c) 2005-2012 The DragonFly Project.
    3  * Copyright (c) 2013 François Tigeot
    4  * Copyright (c) 2013 Matthew Dillon
    5  * All rights reserved.
    6  *
    7  * This code is derived from software contributed to The DragonFly Project
    8  * by Jeffrey Hsu.
    9  *
   10  * Redistribution and use in source and binary forms, with or without
   11  * modification, are permitted provided that the following conditions
   12  * are met:
   13  *
   14  * 1. Redistributions of source code must retain the above copyright
   15  *    notice, this list of conditions and the following disclaimer.
   16  * 2. Redistributions in binary form must reproduce the above copyright
   17  *    notice, this list of conditions and the following disclaimer in
   18  *    the documentation and/or other materials provided with the
   19  *    distribution.
   20  * 3. Neither the name of The DragonFly Project nor the names of its
   21  *    contributors may be used to endorse or promote products derived
   22  *    from this software without specific, prior written permission.
   23  *
   24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   25  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   26  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   27  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
   28  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   29  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
   30  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   31  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
   32  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   33  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   34  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   35  * SUCH DAMAGE.
   36  */
   37 
   38 #ifdef USERLAND_TEST
   39 /*
   40  * Testing:
   41  *
   42  * cc -I. -DUSERLAND_TEST libkern/linux_idr.c -o /tmp/idr -g
   43  */
   44 
   45 #define _KERNEL
   46 #define _KERNEL_STRUCTURES
   47 #define KLD_MODULE
   48 #include <stdio.h>
   49 #include <stdlib.h>
   50 #include <unistd.h>
   51 #include <string.h>
   52 #include <limits.h>
   53 #include <assert.h>
   54 #include <sys/idr.h>
   55 #include <sys/errno.h>
   56 
   57 #undef MALLOC_DEFINE
   58 #define MALLOC_DEFINE(a, b, c)
   59 #define lwkt_gettoken(x)
   60 #define lwkt_reltoken(x)
   61 #define kmalloc(bytes, zone, flags)     calloc(bytes, 1)
   62 #define lwkt_token_init(a, b)
   63 #define lwkt_token_uninit(a)
   64 #define kfree(ptr, flags)               free(ptr)
   65 #define KKASSERT(a)
   66 #define panic(str, ...)                 assert(0)
   67 #define min(a, b)       (((a) < (b)) ? (a) : (b))
   68 #define max(a, b)       (((a) > (b)) ? (a) : (b))
   69 
   70 int
   71 main(int ac, char **av)
   72 {
   73         char buf[256];
   74         struct idr idr;
   75         intptr_t generation = 0x10000000;
   76         int error;
   77         int id;
   78 
   79         idr_init(&idr);
   80 
   81         printf("cmd> ");
   82         fflush(stdout);
   83         while (fgets(buf, sizeof(buf), stdin) != NULL) {
   84                 if (sscanf(buf, "a %d", &id) == 1) {
   85                         for (;;) {
   86                                 if (idr_pre_get(&idr, 0) == 0) {
   87                                         fprintf(stderr, "pre_get failed\n");
   88                                         exit(1);
   89                                 }
   90                                 error = idr_get_new_above(&idr,
   91                                                           (void *)generation,
   92                                                           id, &id);
   93                                 if (error == -EAGAIN)
   94                                         continue;
   95                                 if (error) {
   96                                         fprintf(stderr, "get_new err %d\n",
   97                                                 error);
   98                                         exit(1);
   99                                 }
  100                                 printf("allocated %d value %08x\n",
  101                                         id, (int)generation);
  102                                 ++generation;
  103                                 break;
  104                         }
  105                 } else if (sscanf(buf, "f %d", &id) == 1) {
  106                         idr_remove(&idr, id);
  107                 } else if (sscanf(buf, "g %d", &id) == 1) {
  108                         void *res = idr_find(&idr, id);
  109                         printf("find %d res %p\n", id, res);
  110                 }
  111                 printf("cmd> ");
  112                 fflush(stdout);
  113         }
  114         return 0;
  115 }
  116 
  117 #else
  118 
  119 #include <sys/idr.h>
  120 #include <sys/kernel.h>
  121 #include <sys/libkern.h>
  122 #include <sys/malloc.h>
  123 #include <sys/param.h>
  124 #include <sys/systm.h>
  125 #include <sys/spinlock2.h>
  126 #include <sys/limits.h>
  127 
  128 #endif
  129 
  130 /* Must be 2^n - 1 */
  131 #define IDR_DEFAULT_SIZE    255
  132 
  133 MALLOC_DEFINE(M_IDR, "idr", "Integer ID management");
  134 
  135 static void     idr_grow(struct idr *idp, int want);
  136 static void     idr_reserve(struct idr *idp, int id, int incr);
  137 static int      idr_find_free(struct idr *idp, int want, int lim);
  138 
  139 /*
  140  * Number of nodes in right subtree, including the root.
  141  */
  142 static __inline int
  143 right_subtree_size(int n)
  144 {
  145         return (n ^ (n | (n + 1)));
  146 }
  147 
  148 /*
  149  * Bigger ancestor.
  150  */
  151 static __inline int
  152 right_ancestor(int n)
  153 {
  154         return (n | (n + 1));
  155 }
  156 
  157 /*
  158  * Smaller ancestor.
  159  */
  160 static __inline int
  161 left_ancestor(int n)
  162 {
  163         return ((n & (n + 1)) - 1);
  164 }
  165 
  166 static __inline void
  167 idrfixup(struct idr *idp, int id)
  168 {
  169         if (id < idp->idr_freeindex) {
  170                 idp->idr_freeindex = id;
  171         }
  172         while (idp->idr_lastindex >= 0 &&
  173                 idp->idr_nodes[idp->idr_lastindex].data == NULL
  174         ) {
  175                 --idp->idr_lastindex;
  176         }
  177 }
  178 
  179 static __inline struct idr_node *
  180 idr_get_node(struct idr *idp, int id)
  181 {
  182         struct idr_node *idrnp;
  183         if (id < 0 || id >= idp->idr_count)
  184                 return (NULL);
  185         idrnp = &idp->idr_nodes[id];
  186         if (idrnp->allocated == 0)
  187                 return (NULL);
  188         return (idrnp);
  189 }
  190 
  191 static void
  192 idr_reserve(struct idr *idp, int id, int incr)
  193 {
  194         while (id >= 0) {
  195                 idp->idr_nodes[id].allocated += incr;
  196                 KKASSERT(idp->idr_nodes[id].allocated >= 0);
  197                 id = left_ancestor(id);
  198         }
  199 }
  200 
  201 static int
  202 idr_find_free(struct idr *idp, int want, int lim)
  203 {
  204         int id, rsum, rsize, node;
  205 
  206         /*
  207          * Search for a free descriptor starting at the higher
  208          * of want or fd_freefile.  If that fails, consider
  209          * expanding the ofile array.
  210          *
  211          * NOTE! the 'allocated' field is a cumulative recursive allocation
  212          * count.  If we happen to see a value of 0 then we can shortcut
  213          * our search.  Otherwise we run through through the tree going
  214          * down branches we know have free descriptor(s) until we hit a
  215          * leaf node.  The leaf node will be free but will not necessarily
  216          * have an allocated field of 0.
  217          */
  218 
  219         /* move up the tree looking for a subtree with a free node */
  220         for (id = max(want, idp->idr_freeindex); id < min(idp->idr_count, lim);
  221                         id = right_ancestor(id)) {
  222                 if (idp->idr_nodes[id].allocated == 0)
  223                         return (id);
  224 
  225                 rsize = right_subtree_size(id);
  226                 if (idp->idr_nodes[id].allocated == rsize)
  227                         continue;       /* right subtree full */
  228 
  229                 /*
  230                  * Free fd is in the right subtree of the tree rooted at fd.
  231                  * Call that subtree R.  Look for the smallest (leftmost)
  232                  * subtree of R with an unallocated fd: continue moving
  233                  * down the left branch until encountering a full left
  234                  * subtree, then move to the right.
  235                  */
  236                 for (rsum = 0, rsize /= 2; rsize > 0; rsize /= 2) {
  237                         node = id + rsize;
  238                         rsum += idp->idr_nodes[node].allocated;
  239                         if (idp->idr_nodes[id].allocated == rsum + rsize) {
  240                                 id = node;      /* move to the right */
  241                                 if (idp->idr_nodes[node].allocated == 0)
  242                                         return (id);
  243                                 rsum = 0;
  244                         }
  245                 }
  246                 return (id);
  247         }
  248         return (-1);
  249 }
  250 
  251 /*
  252  * Blocking pre-get support, allows callers to use idr_pre_get() in
  253  * combination with idr_get_new_above() such that idr_get_new_above()
  254  * can be called safely with a spinlock held.
  255  *
  256  * Returns 0 on failure, 1 on success.
  257  *
  258  * Caller must hold a blockable lock.
  259  */
  260 int
  261 idr_pre_get(struct idr *idp, __unused unsigned gfp_mask)
  262 {
  263         int want = idp->idr_maxwant;
  264         int lim = INT_MAX;
  265         int result = 1;                         /* success */
  266         int id;
  267 
  268         KKASSERT(mycpu->gd_spinlocks == 0);
  269         lwkt_gettoken(&idp->idr_token);
  270         for (;;) {
  271                 /*
  272                  * Grow if necessary (or if forced by the loop)
  273                  */
  274                 if (want >= idp->idr_count)
  275                         idr_grow(idp, want);
  276 
  277                 /*
  278                  * Check if a spot is available, break and return 0 if true,
  279                  * unless the available spot is beyond our limit.  It is
  280                  * possible to exceed the limit due to the way array growth
  281                  * works.
  282                  *
  283                  * XXX we assume that the caller uses a consistent <sid> such
  284                  *     that the idr_maxwant field is correct, otherwise we
  285                  *     may believe that a slot is available but the caller then
  286                  *     fails in idr_get_new_above() and loops.
  287                  */
  288                 id = idr_find_free(idp, idp->idr_maxwant, lim);
  289                 if (id != -1) {
  290                         if (id >= lim)
  291                                 result = 0;     /* failure */
  292                         break;
  293                 }
  294 
  295                 /*
  296                  * Return ENOSPC if our limit has been reached, otherwise
  297                  * loop and force growth.
  298                  */
  299                 if (idp->idr_count >= lim) {
  300                         result = 0;             /* failure */
  301                         break;
  302                 }
  303                 want = idp->idr_count;
  304         }
  305         lwkt_reltoken(&idp->idr_token);
  306         return result;
  307 }
  308 
  309 /*
  310  * Allocate an integer.  If -EAGAIN is returned the caller should loop
  311  * and call idr_pre_get() with no locks held, and then retry the call
  312  * to idr_get_new_above().
  313  *
  314  * Can be safely called with spinlocks held.
  315  */
  316 int
  317 idr_get_new_above(struct idr *idp, void *ptr, int sid, int *id)
  318 {
  319         int resid;
  320 
  321         KKASSERT(ptr != NULL);
  322 
  323         /*
  324          * NOTE! Because the idp is initialized with a non-zero count,
  325          *       sid might be < idp->idr_count but idr_maxwant might not
  326          *       yet be initialized.  So check both cases.
  327          */
  328         lwkt_gettoken(&idp->idr_token);
  329         if (sid >= idp->idr_count || idp->idr_maxwant < sid) {
  330                 idp->idr_maxwant = max(idp->idr_maxwant, sid);
  331                 lwkt_reltoken(&idp->idr_token);
  332                 return -EAGAIN;
  333         }
  334 
  335         resid = idr_find_free(idp, sid, INT_MAX);
  336         if (resid == -1) {
  337                 lwkt_reltoken(&idp->idr_token);
  338                 return -EAGAIN;
  339         }
  340 
  341         if (resid >= idp->idr_count)
  342                 panic("idr_get_new_above(): illegal resid %d", resid);
  343         if (resid > idp->idr_lastindex)
  344                 idp->idr_lastindex = resid;
  345         if (sid <= idp->idr_freeindex)
  346                 idp->idr_freeindex = resid;
  347         *id = resid;
  348         idr_reserve(idp, resid, 1);
  349         idp->idr_nodes[resid].data = ptr;
  350 
  351         lwkt_reltoken(&idp->idr_token);
  352         return (0);
  353 }
  354 
  355 int
  356 idr_get_new(struct idr *idp, void *ptr, int *id)
  357 {
  358         return idr_get_new_above(idp, ptr, 0, id);
  359 }
  360 
  361 /*
  362  * Grow the file table so it can hold through descriptor (want).
  363  *
  364  * Caller must hold a blockable lock.
  365  */
  366 static void
  367 idr_grow(struct idr *idp, int want)
  368 {
  369         struct idr_node *oldnodes, *newnodes;
  370         int nf;
  371 
  372         /* We want 2^n - 1 descriptors */
  373         nf = idp->idr_count;
  374         do {
  375                 nf = 2 * nf + 1;
  376         } while (nf <= want);
  377 
  378 #ifdef USERLAND_TEST
  379         printf("idr_grow: %d -> %d\n", idp->idr_count, nf);
  380 #endif
  381 
  382         /* Allocate a new zero'ed node array */
  383         newnodes = kmalloc(nf * sizeof(struct idr_node),
  384                            M_IDR, M_ZERO | M_WAITOK);
  385 
  386         /* We might race another grow */
  387         if (nf <= idp->idr_count) {
  388                 kfree(newnodes, M_IDR);
  389                 return;
  390         }
  391 
  392         /*
  393          * Copy existing nodes to the beginning of the new array
  394          */
  395         oldnodes = idp->idr_nodes;
  396         if (oldnodes) {
  397                 bcopy(oldnodes, newnodes,
  398                       idp->idr_count * sizeof(struct idr_node));
  399         }
  400         idp->idr_nodes = newnodes;
  401         idp->idr_count = nf;
  402 
  403         if (oldnodes) {
  404                 kfree(oldnodes, M_IDR);
  405         }
  406         idp->idr_nexpands++;
  407 }
  408 
  409 void
  410 idr_remove(struct idr *idp, int id)
  411 {
  412         void *ptr;
  413 
  414         lwkt_gettoken(&idp->idr_token);
  415         if (id < 0 || id >= idp->idr_count) {
  416                 lwkt_reltoken(&idp->idr_token);
  417                 return;
  418         }
  419         if ((ptr = idp->idr_nodes[id].data) == NULL) {
  420                 lwkt_reltoken(&idp->idr_token);
  421                 return;
  422         }
  423         idp->idr_nodes[id].data = NULL;
  424         idr_reserve(idp, id, -1);
  425         idrfixup(idp, id);
  426         lwkt_reltoken(&idp->idr_token);
  427 }
  428 
  429 /*
  430  * Remove all int allocations, leave array intact.
  431  *
  432  * Caller must hold a blockable lock (or be in a context where holding
  433  * the spinlock is not relevant).
  434  */
  435 void
  436 idr_remove_all(struct idr *idp)
  437 {
  438         lwkt_gettoken(&idp->idr_token);
  439         bzero(idp->idr_nodes, idp->idr_count * sizeof(struct idr_node));
  440         idp->idr_lastindex = -1;
  441         idp->idr_freeindex = 0;
  442         idp->idr_nexpands = 0;
  443         idp->idr_maxwant = 0;
  444         lwkt_reltoken(&idp->idr_token);
  445 }
  446 
  447 void
  448 idr_destroy(struct idr *idp)
  449 {
  450         lwkt_token_uninit(&idp->idr_token);
  451         if (idp->idr_nodes) {
  452                 kfree(idp->idr_nodes, M_IDR);
  453                 idp->idr_nodes = NULL;
  454         }
  455         bzero(idp, sizeof(*idp));
  456 }
  457 
  458 void *
  459 idr_find(struct idr *idp, int id)
  460 {
  461         void *ret;
  462 
  463         if (id < 0 || id >= idp->idr_count) {
  464                 ret = NULL;
  465         } else if (idp->idr_nodes[id].allocated == 0) {
  466                 ret = NULL;
  467         } else {
  468                 ret = idp->idr_nodes[id].data;
  469         }
  470         return ret;
  471 }
  472 
  473 int
  474 idr_for_each(struct idr *idp, int (*fn)(int id, void *p, void *data),
  475              void *data)
  476 {
  477         int i, error = 0;
  478         struct idr_node *nodes;
  479 
  480         nodes = idp->idr_nodes;
  481         for (i = 0; i < idp->idr_count; i++) {
  482                 if (nodes[i].data != NULL && nodes[i].allocated > 0) {
  483                         error = fn(i, nodes[i].data, data);
  484                         if (error != 0)
  485                                 break;
  486                 }
  487         }
  488         return error;
  489 }
  490 
  491 void *
  492 idr_replace(struct idr *idp, void *ptr, int id)
  493 {
  494         struct idr_node *idrnp;
  495         void *ret;
  496 
  497         lwkt_gettoken(&idp->idr_token);
  498         idrnp = idr_get_node(idp, id);
  499         if (idrnp == NULL || ptr == NULL) {
  500                 ret = NULL;
  501         } else {
  502                 ret = idrnp->data;
  503                 idrnp->data = ptr;
  504         }
  505         lwkt_reltoken(&idp->idr_token);
  506         return (ret);
  507 }
  508 
  509 void
  510 idr_init(struct idr *idp)
  511 {
  512         bzero(idp, sizeof(struct idr));
  513         idp->idr_nodes = kmalloc(IDR_DEFAULT_SIZE * sizeof(struct idr_node),
  514                                                 M_IDR, M_WAITOK | M_ZERO);
  515         idp->idr_count = IDR_DEFAULT_SIZE;
  516         idp->idr_lastindex = -1;
  517         idp->idr_maxwant = 0;
  518         lwkt_token_init(&idp->idr_token, "idr token");
  519 }

Cache object: ac70c330226b571c55c7060cc5500664


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