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/dev/sound/clone.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) 2007 Ariff Abdullah <ariff@FreeBSD.org>
    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 #include <sys/param.h>
   32 #include <sys/systm.h>
   33 #include <sys/conf.h>
   34 #include <sys/kernel.h>
   35 #include <sys/malloc.h>
   36 #include <sys/proc.h>
   37 
   38 #ifdef HAVE_KERNEL_OPTION_HEADERS
   39 #include "opt_snd.h"
   40 #endif
   41 
   42 #if defined(SND_DIAGNOSTIC) || defined(SND_DEBUG)
   43 #include <dev/sound/pcm/sound.h>
   44 #endif
   45 
   46 #include <dev/sound/clone.h>
   47 
   48 /*
   49  * So here we go again, another clonedevs manager. Unlike default clonedevs,
   50  * this clone manager is designed to withstand various abusive behavior
   51  * (such as 'while : ; do ls /dev/whatever ; done', etc.), reusable object
   52  * after reaching certain expiration threshold, aggressive garbage collector,
   53  * transparent device allocator and concurrency handling across multiple
   54  * thread/proc. Due to limited information given by dev_clone EVENTHANDLER,
   55  * we don't have much clues whether the caller wants a real open() or simply
   56  * making fun of us with things like stat(), mtime() etc. Assuming that:
   57  * 1) Time window between dev_clone EH <-> real open() should be small
   58  * enough and 2) mtime()/stat() etc. always looks like a half way / stalled
   59  * operation, we can decide whether a new cdev must be created, old
   60  * (expired) cdev can be reused or an existing cdev can be shared.
   61  *
   62  * Most of the operations and logics are generic enough and can be applied
   63  * on other places (such as if_tap, snp, etc).  Perhaps this can be
   64  * rearranged to complement clone_*(). However, due to this still being
   65  * specific to the sound driver (and as a proof of concept on how it can be
   66  * done), si_drv2 is used to keep the pointer of the clone list entry to
   67  * avoid expensive lookup.
   68  */
   69 
   70 /* clone entry */
   71 struct snd_clone_entry {
   72         TAILQ_ENTRY(snd_clone_entry) link;
   73         struct snd_clone *parent;
   74         struct cdev *devt;
   75         struct timespec tsp;
   76         uint32_t flags;
   77         pid_t pid;
   78         int unit;
   79 };
   80 
   81 /* clone manager */
   82 struct snd_clone {
   83         TAILQ_HEAD(link_head, snd_clone_entry) head;
   84         struct timespec tsp;
   85         int refcount;
   86         int size;
   87         int typemask;
   88         int maxunit;
   89         int deadline;
   90         uint32_t flags;
   91 };
   92 
   93 #ifdef SND_DIAGNOSTIC
   94 #define SND_CLONE_ASSERT(x, y)          do {                    \
   95         if (!(x))                                               \
   96                 panic y;                                        \
   97 } while (0)
   98 #else
   99 #define SND_CLONE_ASSERT(...)           KASSERT(__VA_ARGS__)
  100 #endif
  101 
  102 /*
  103  * snd_clone_create() : Return opaque allocated clone manager.
  104  */
  105 struct snd_clone *
  106 snd_clone_create(int typemask, int maxunit, int deadline, uint32_t flags)
  107 {
  108         struct snd_clone *c;
  109 
  110         SND_CLONE_ASSERT(!(typemask & ~SND_CLONE_MAXUNIT),
  111             ("invalid typemask: 0x%08x", typemask));
  112         SND_CLONE_ASSERT(maxunit == -1 ||
  113             !(maxunit & ~(~typemask & SND_CLONE_MAXUNIT)),
  114             ("maxunit overflow: typemask=0x%08x maxunit=%d",
  115             typemask, maxunit));
  116         SND_CLONE_ASSERT(!(flags & ~SND_CLONE_MASK),
  117             ("invalid clone flags=0x%08x", flags));
  118 
  119         c = malloc(sizeof(*c), M_DEVBUF, M_WAITOK | M_ZERO);
  120         c->refcount = 0;
  121         c->size = 0;
  122         c->typemask = typemask;
  123         c->maxunit = (maxunit == -1) ? (~typemask & SND_CLONE_MAXUNIT) :
  124             maxunit;
  125         c->deadline = deadline;
  126         c->flags = flags;
  127         getnanouptime(&c->tsp);
  128         TAILQ_INIT(&c->head);
  129 
  130         return (c);
  131 }
  132 
  133 int
  134 snd_clone_busy(struct snd_clone *c)
  135 {
  136         struct snd_clone_entry *ce;
  137 
  138         SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
  139 
  140         if (c->size == 0)
  141                 return (0);
  142 
  143         TAILQ_FOREACH(ce, &c->head, link) {
  144                 if ((ce->flags & SND_CLONE_BUSY) ||
  145                     (ce->devt != NULL && ce->devt->si_threadcount != 0))
  146                         return (EBUSY);
  147         }
  148 
  149         return (0);
  150 }
  151 
  152 /*
  153  * snd_clone_enable()/disable() : Suspend/resume clone allocation through
  154  * snd_clone_alloc(). Everything else will not be affected by this.
  155  */
  156 int
  157 snd_clone_enable(struct snd_clone *c)
  158 {
  159         SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
  160 
  161         if (c->flags & SND_CLONE_ENABLE)
  162                 return (EINVAL);
  163 
  164         c->flags |= SND_CLONE_ENABLE;
  165 
  166         return (0);
  167 }
  168 
  169 int
  170 snd_clone_disable(struct snd_clone *c)
  171 {
  172         SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
  173 
  174         if (!(c->flags & SND_CLONE_ENABLE))
  175                 return (EINVAL);
  176 
  177         c->flags &= ~SND_CLONE_ENABLE;
  178 
  179         return (0);
  180 }
  181 
  182 /*
  183  * Getters / Setters. Not worth explaining :)
  184  */
  185 int
  186 snd_clone_getsize(struct snd_clone *c)
  187 {
  188         SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
  189 
  190         return (c->size);
  191 }
  192 
  193 int
  194 snd_clone_getmaxunit(struct snd_clone *c)
  195 {
  196         SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
  197 
  198         return (c->maxunit);
  199 }
  200 
  201 int
  202 snd_clone_setmaxunit(struct snd_clone *c, int maxunit)
  203 {
  204         SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
  205         SND_CLONE_ASSERT(maxunit == -1 ||
  206             !(maxunit & ~(~c->typemask & SND_CLONE_MAXUNIT)),
  207             ("maxunit overflow: typemask=0x%08x maxunit=%d",
  208             c->typemask, maxunit));
  209 
  210         c->maxunit = (maxunit == -1) ? (~c->typemask & SND_CLONE_MAXUNIT) :
  211             maxunit;
  212 
  213         return (c->maxunit);
  214 }
  215 
  216 int
  217 snd_clone_getdeadline(struct snd_clone *c)
  218 {
  219         SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
  220 
  221         return (c->deadline);
  222 }
  223 
  224 int
  225 snd_clone_setdeadline(struct snd_clone *c, int deadline)
  226 {
  227         SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
  228 
  229         c->deadline = deadline;
  230 
  231         return (c->deadline);
  232 }
  233 
  234 uint32_t
  235 snd_clone_getflags(struct snd_clone *c)
  236 {
  237         SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
  238 
  239         return (c->flags);
  240 }
  241 
  242 uint32_t
  243 snd_clone_setflags(struct snd_clone *c, uint32_t flags)
  244 {
  245         SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
  246         SND_CLONE_ASSERT(!(flags & ~SND_CLONE_MASK),
  247             ("invalid clone flags=0x%08x", flags));
  248 
  249         c->flags = flags;
  250 
  251         return (c->flags);
  252 }
  253 
  254 uint32_t
  255 snd_clone_getdevflags(struct cdev *dev)
  256 {
  257         struct snd_clone_entry *ce;
  258 
  259         SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
  260 
  261         ce = dev->si_drv2;
  262         if (ce == NULL)
  263                 return (0xffffffff);
  264 
  265         SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
  266 
  267         return (ce->flags);
  268 }
  269 
  270 uint32_t
  271 snd_clone_setdevflags(struct cdev *dev, uint32_t flags)
  272 {
  273         struct snd_clone_entry *ce;
  274 
  275         SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
  276         SND_CLONE_ASSERT(!(flags & ~SND_CLONE_DEVMASK),
  277             ("invalid clone dev flags=0x%08x", flags));
  278 
  279         ce = dev->si_drv2;
  280         if (ce == NULL)
  281                 return (0xffffffff);
  282 
  283         SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
  284 
  285         ce->flags = flags;
  286 
  287         return (ce->flags);
  288 }
  289 
  290 /* Elapsed time conversion to ms */
  291 #define SND_CLONE_ELAPSED(x, y)                                         \
  292         ((((x)->tv_sec - (y)->tv_sec) * 1000) +                         \
  293         (((y)->tv_nsec > (x)->tv_nsec) ?                                \
  294         (((1000000000L + (x)->tv_nsec -                                 \
  295         (y)->tv_nsec) / 1000000) - 1000) :                              \
  296         (((x)->tv_nsec - (y)->tv_nsec) / 1000000)))
  297 
  298 #define SND_CLONE_EXPIRED(x, y, z)                                      \
  299         ((x)->deadline < 1 ||                                           \
  300         ((y)->tv_sec - (z)->tv_sec) > ((x)->deadline / 1000) ||         \
  301         SND_CLONE_ELAPSED(y, z) > (x)->deadline)
  302 
  303 /*
  304  * snd_clone_gc() : Garbage collector for stalled, expired objects. Refer to
  305  * clone.h for explanations on GC settings.
  306  */
  307 int
  308 snd_clone_gc(struct snd_clone *c)
  309 {
  310         struct snd_clone_entry *ce, *tce;
  311         struct timespec now;
  312         int pruned;
  313 
  314         SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
  315 
  316         if (!(c->flags & SND_CLONE_GC_ENABLE) || c->size == 0)
  317                 return (0);
  318 
  319         getnanouptime(&now);
  320 
  321         /*
  322          * Bail out if the last clone handler was invoked below the deadline
  323          * threshold.
  324          */
  325         if ((c->flags & SND_CLONE_GC_EXPIRED) &&
  326             !SND_CLONE_EXPIRED(c, &now, &c->tsp))
  327                 return (0);
  328 
  329         pruned = 0;
  330 
  331         /*
  332          * Visit each object in reverse order. If the object is still being
  333          * referenced by a valid open(), skip it. Look for expired objects
  334          * and either revoke its clone invocation status or mercilessly
  335          * throw it away.
  336          */
  337         TAILQ_FOREACH_REVERSE_SAFE(ce, &c->head, link_head, link, tce) {
  338                 if (!(ce->flags & SND_CLONE_BUSY) &&
  339                     (!(ce->flags & SND_CLONE_INVOKE) ||
  340                     SND_CLONE_EXPIRED(c, &now, &ce->tsp))) {
  341                         if ((c->flags & SND_CLONE_GC_REVOKE) ||
  342                             ce->devt->si_threadcount != 0) {
  343                                 ce->flags &= ~SND_CLONE_INVOKE;
  344                                 ce->pid = -1;
  345                         } else {
  346                                 TAILQ_REMOVE(&c->head, ce, link);
  347                                 destroy_dev(ce->devt);
  348                                 free(ce, M_DEVBUF);
  349                                 c->size--;
  350                         }
  351                         pruned++;
  352                 }
  353         }
  354 
  355         /* return total pruned objects */
  356         return (pruned);
  357 }
  358 
  359 void
  360 snd_clone_destroy(struct snd_clone *c)
  361 {
  362         struct snd_clone_entry *ce, *tmp;
  363 
  364         SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
  365 
  366         ce = TAILQ_FIRST(&c->head);
  367         while (ce != NULL) {
  368                 tmp = TAILQ_NEXT(ce, link);
  369                 if (ce->devt != NULL)
  370                         destroy_dev(ce->devt);
  371                 free(ce, M_DEVBUF);
  372                 ce = tmp;
  373         }
  374 
  375         free(c, M_DEVBUF);
  376 }
  377 
  378 /*
  379  * snd_clone_acquire() : The vital part of concurrency management. Must be
  380  * called somewhere at the beginning of open() handler. ENODEV is not really
  381  * fatal since it just tell the caller that this is not cloned stuff.
  382  * EBUSY is *real*, don't forget that!
  383  */
  384 int
  385 snd_clone_acquire(struct cdev *dev)
  386 {
  387         struct snd_clone_entry *ce;
  388 
  389         SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
  390 
  391         ce = dev->si_drv2;
  392         if (ce == NULL)
  393                 return (ENODEV);
  394 
  395         SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
  396 
  397         ce->flags &= ~SND_CLONE_INVOKE;
  398 
  399         if (ce->flags & SND_CLONE_BUSY)
  400                 return (EBUSY);
  401 
  402         ce->flags |= SND_CLONE_BUSY;
  403 
  404         return (0);
  405 }
  406 
  407 /*
  408  * snd_clone_release() : Release busy status. Must be called somewhere at
  409  * the end of close() handler, or somewhere after fail open().
  410  */
  411 int
  412 snd_clone_release(struct cdev *dev)
  413 {
  414         struct snd_clone_entry *ce;
  415 
  416         SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
  417 
  418         ce = dev->si_drv2;
  419         if (ce == NULL)
  420                 return (ENODEV);
  421 
  422         SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
  423 
  424         ce->flags &= ~SND_CLONE_INVOKE;
  425 
  426         if (!(ce->flags & SND_CLONE_BUSY))
  427                 return (EBADF);
  428 
  429         ce->flags &= ~SND_CLONE_BUSY;
  430         ce->pid = -1;
  431 
  432         return (0);
  433 }
  434 
  435 /*
  436  * snd_clone_ref/unref() : Garbage collector reference counter. To make
  437  * garbage collector run automatically, the sequence must be something like
  438  * this (both in open() and close() handlers):
  439  *
  440  *  open() - 1) snd_clone_acquire()
  441  *           2) .... check check ... if failed, snd_clone_release()
  442  *           3) Success. Call snd_clone_ref()
  443  *
  444  * close() - 1) .... check check check ....
  445  *           2) Success. snd_clone_release()
  446  *           3) snd_clone_unref() . Garbage collector will run at this point
  447  *              if this is the last referenced object.
  448  */
  449 int
  450 snd_clone_ref(struct cdev *dev)
  451 {
  452         struct snd_clone_entry *ce;
  453         struct snd_clone *c;
  454 
  455         SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
  456 
  457         ce = dev->si_drv2;
  458         if (ce == NULL)
  459                 return (0);
  460 
  461         c = ce->parent;
  462         SND_CLONE_ASSERT(c != NULL, ("NULL parent"));
  463         SND_CLONE_ASSERT(c->refcount >= 0, ("refcount < 0"));
  464 
  465         return (++c->refcount);
  466 }
  467 
  468 int
  469 snd_clone_unref(struct cdev *dev)
  470 {
  471         struct snd_clone_entry *ce;
  472         struct snd_clone *c;
  473 
  474         SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
  475 
  476         ce = dev->si_drv2;
  477         if (ce == NULL)
  478                 return (0);
  479 
  480         c = ce->parent;
  481         SND_CLONE_ASSERT(c != NULL, ("NULL parent"));
  482         SND_CLONE_ASSERT(c->refcount > 0, ("refcount <= 0"));
  483 
  484         c->refcount--;
  485 
  486         /* 
  487          * Run automatic garbage collector, if needed.
  488          */
  489         if ((c->flags & SND_CLONE_GC_UNREF) &&
  490             (!(c->flags & SND_CLONE_GC_LASTREF) ||
  491             (c->refcount == 0 && (c->flags & SND_CLONE_GC_LASTREF))))
  492                 (void)snd_clone_gc(c);
  493 
  494         return (c->refcount);
  495 }
  496 
  497 void
  498 snd_clone_register(struct snd_clone_entry *ce, struct cdev *dev)
  499 {
  500         SND_CLONE_ASSERT(ce != NULL, ("NULL snd_clone_entry"));
  501         SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
  502         SND_CLONE_ASSERT(dev->si_drv2 == NULL, ("dev->si_drv2 not NULL"));
  503         SND_CLONE_ASSERT((ce->flags & SND_CLONE_ALLOC) == SND_CLONE_ALLOC,
  504             ("invalid clone alloc flags=0x%08x", ce->flags));
  505         SND_CLONE_ASSERT(ce->devt == NULL, ("ce->devt not NULL"));
  506         SND_CLONE_ASSERT(ce->unit == dev2unit(dev),
  507             ("invalid unit ce->unit=0x%08x dev2unit=0x%08x",
  508             ce->unit, dev2unit(dev)));
  509 
  510         SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
  511 
  512         dev->si_drv2 = ce;
  513         ce->devt = dev;
  514         ce->flags &= ~SND_CLONE_ALLOC;
  515         ce->flags |= SND_CLONE_INVOKE;
  516 }
  517 
  518 struct snd_clone_entry *
  519 snd_clone_alloc(struct snd_clone *c, struct cdev **dev, int *unit, int tmask)
  520 {
  521         struct snd_clone_entry *ce, *after, *bce, *cce, *nce, *tce;
  522         struct timespec now;
  523         int cunit, allocunit;
  524         pid_t curpid;
  525 
  526         SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
  527         SND_CLONE_ASSERT(dev != NULL, ("NULL dev pointer"));
  528         SND_CLONE_ASSERT((c->typemask & tmask) == tmask,
  529             ("invalid tmask: typemask=0x%08x tmask=0x%08x",
  530             c->typemask, tmask));
  531         SND_CLONE_ASSERT(unit != NULL, ("NULL unit pointer"));
  532         SND_CLONE_ASSERT(*unit == -1 || !(*unit & (c->typemask | tmask)),
  533             ("typemask collision: typemask=0x%08x tmask=0x%08x *unit=%d",
  534             c->typemask, tmask, *unit));
  535 
  536         if (!(c->flags & SND_CLONE_ENABLE) ||
  537             (*unit != -1 && *unit > c->maxunit))
  538                 return (NULL);
  539 
  540         ce = NULL;
  541         after = NULL;
  542         bce = NULL;     /* "b"usy candidate */
  543         cce = NULL;     /* "c"urthread/proc candidate */
  544         nce = NULL;     /* "n"ull, totally unbusy candidate */
  545         tce = NULL;     /* Last "t"ry candidate */
  546         cunit = 0;
  547         allocunit = (*unit == -1) ? 0 : *unit;
  548         curpid = curthread->td_proc->p_pid;
  549 
  550         getnanouptime(&now);
  551 
  552         TAILQ_FOREACH(ce, &c->head, link) {
  553                 /*
  554                  * Sort incrementally according to device type.
  555                  */
  556                 if (tmask > (ce->unit & c->typemask)) {
  557                         if (cunit == 0)
  558                                 after = ce;
  559                         continue;
  560                 } else if (tmask < (ce->unit & c->typemask))
  561                         break;
  562 
  563                 /*
  564                  * Shoot.. this is where the grumpiness begin. Just
  565                  * return immediately.
  566                  */
  567                 if (*unit != -1 && *unit == (ce->unit & ~tmask))
  568                         goto snd_clone_alloc_out;
  569 
  570                 cunit++;
  571                 /*
  572                  * Simmilar device type. Sort incrementally according
  573                  * to allocation unit. While here, look for free slot
  574                  * and possible collision for new / future allocation.
  575                  */
  576                 if (*unit == -1 && (ce->unit & ~tmask) == allocunit)
  577                         allocunit++;
  578                 if ((ce->unit & ~tmask) < allocunit)
  579                         after = ce;
  580                 /*
  581                  * Clone logic:
  582                  *   1. Look for non busy, but keep track of the best
  583                  *      possible busy cdev.
  584                  *   2. Look for the best (oldest referenced) entry that is
  585                  *      in a same process / thread.
  586                  *   3. Look for the best (oldest referenced), absolute free
  587                  *      entry.
  588                  *   4. Lastly, look for the best (oldest referenced)
  589                  *      any entries that doesn't fit with anything above.
  590                  */
  591                 if (ce->flags & SND_CLONE_BUSY) {
  592                         if (ce->devt != NULL && (bce == NULL ||
  593                             timespeccmp(&ce->tsp, &bce->tsp, <)))
  594                                 bce = ce;
  595                         continue;
  596                 }
  597                 if (ce->pid == curpid &&
  598                     (cce == NULL || timespeccmp(&ce->tsp, &cce->tsp, <)))
  599                         cce = ce;
  600                 else if (!(ce->flags & SND_CLONE_INVOKE) &&
  601                     (nce == NULL || timespeccmp(&ce->tsp, &nce->tsp, <)))
  602                         nce = ce;
  603                 else if (tce == NULL || timespeccmp(&ce->tsp, &tce->tsp, <))
  604                         tce = ce;
  605         }
  606         if (*unit != -1)
  607                 goto snd_clone_alloc_new;
  608         else if (cce != NULL) {
  609                 /* Same proc entry found, go for it */
  610                 ce = cce;
  611                 goto snd_clone_alloc_out;
  612         } else if (nce != NULL) {
  613                 /*
  614                  * Next, try absolute free entry. If the calculated
  615                  * allocunit is smaller, create new entry instead.
  616                  */
  617                 if (allocunit < (nce->unit & ~tmask))
  618                         goto snd_clone_alloc_new;
  619                 ce = nce;
  620                 goto snd_clone_alloc_out;
  621         } else if (allocunit > c->maxunit) {
  622                 /*
  623                  * Maximum allowable unit reached. Try returning any
  624                  * available cdev and hope for the best. If the lookup is
  625                  * done for things like stat(), mtime() etc. , things should
  626                  * be ok. Otherwise, open() handler should do further checks
  627                  * and decide whether to return correct error code or not.
  628                  */
  629                 if (tce != NULL) {
  630                         ce = tce;
  631                         goto snd_clone_alloc_out;
  632                 } else if (bce != NULL) {
  633                         ce = bce;
  634                         goto snd_clone_alloc_out;
  635                 }
  636                 return (NULL);
  637         }
  638 
  639 snd_clone_alloc_new:
  640         /*
  641          * No free entries found, and we still haven't reached maximum
  642          * allowable units. Allocate, setup a minimal unique entry with busy
  643          * status so nobody will monkey on this new entry. Unit magic is set
  644          * right here to avoid collision with other contesting handler.
  645          * The caller must be carefull here to maintain its own
  646          * synchronization, as long as it will not conflict with malloc(9)
  647          * operations.
  648          *
  649          * That said, go figure.
  650          */
  651         ce = malloc(sizeof(*ce), M_DEVBUF,
  652             ((c->flags & SND_CLONE_WAITOK) ? M_WAITOK : M_NOWAIT) | M_ZERO);
  653         if (ce == NULL) {
  654                 if (*unit != -1)
  655                         return (NULL);
  656                 /*
  657                  * We're being dense, ignorance is bliss,
  658                  * Super Regulatory Measure (TM).. TRY AGAIN!
  659                  */
  660                 if (nce != NULL) {
  661                         ce = nce;
  662                         goto snd_clone_alloc_out;
  663                 } else if (tce != NULL) {
  664                         ce = tce;
  665                         goto snd_clone_alloc_out;
  666                 } else if (bce != NULL) {
  667                         ce = bce;
  668                         goto snd_clone_alloc_out;
  669                 }
  670                 return (NULL);
  671         }
  672         /* Setup new entry */
  673         ce->parent = c;
  674         ce->unit = tmask | allocunit;
  675         ce->pid = curpid;
  676         ce->tsp = now;
  677         ce->flags |= SND_CLONE_ALLOC;
  678         if (after != NULL) {
  679                 TAILQ_INSERT_AFTER(&c->head, after, ce, link);
  680         } else {
  681                 TAILQ_INSERT_HEAD(&c->head, ce, link);
  682         }
  683         c->size++;
  684         c->tsp = now;
  685         /*
  686          * Save new allocation unit for caller which will be used
  687          * by make_dev().
  688          */
  689         *unit = allocunit;
  690 
  691         return (ce);
  692 
  693 snd_clone_alloc_out:
  694         /*
  695          * Set, mark, timestamp the entry if this is a truly free entry.
  696          * Leave busy entry alone.
  697          */
  698         if (!(ce->flags & SND_CLONE_BUSY)) {
  699                 ce->pid = curpid;
  700                 ce->tsp = now;
  701                 ce->flags |= SND_CLONE_INVOKE;
  702         }
  703         c->tsp = now;
  704         *dev = ce->devt;
  705 
  706         return (NULL);
  707 }

Cache object: 01452266098592f5ba57b71f1ecf2315


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