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: releng/12.0/sys/dev/sound/clone.c 326255 2017-11-27 14:52:40Z pfg $
   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  * Shamelessly ripped off from vfs_subr.c
  104  * We need at least 1/HZ precision as default timestamping.
  105  */
  106 enum { SND_TSP_SEC, SND_TSP_HZ, SND_TSP_USEC, SND_TSP_NSEC };
  107 
  108 static int snd_timestamp_precision = SND_TSP_HZ;
  109 TUNABLE_INT("hw.snd.timestamp_precision", &snd_timestamp_precision);
  110 
  111 void
  112 snd_timestamp(struct timespec *tsp)
  113 {
  114         struct timeval tv;
  115 
  116         switch (snd_timestamp_precision) {
  117         case SND_TSP_SEC:
  118                 tsp->tv_sec = time_second;
  119                 tsp->tv_nsec = 0;
  120                 break;
  121         case SND_TSP_HZ:
  122                 getnanouptime(tsp);
  123                 break;
  124         case SND_TSP_USEC:
  125                 microuptime(&tv);
  126                 TIMEVAL_TO_TIMESPEC(&tv, tsp);
  127                 break;
  128         case SND_TSP_NSEC:
  129                 nanouptime(tsp);
  130                 break;
  131         default:
  132                 snd_timestamp_precision = SND_TSP_HZ;
  133                 getnanouptime(tsp);
  134                 break;
  135         }
  136 }
  137 
  138 #if defined(SND_DIAGNOSTIC) || defined(SND_DEBUG)
  139 static int
  140 sysctl_hw_snd_timestamp_precision(SYSCTL_HANDLER_ARGS)
  141 {
  142         int err, val;
  143 
  144         val = snd_timestamp_precision;
  145         err = sysctl_handle_int(oidp, &val, 0, req);
  146         if (err == 0 && req->newptr != NULL) {
  147                 switch (val) {
  148                 case SND_TSP_SEC:
  149                 case SND_TSP_HZ:
  150                 case SND_TSP_USEC:
  151                 case SND_TSP_NSEC:
  152                         snd_timestamp_precision = val;
  153                         break;
  154                 default:
  155                         break;
  156                 }
  157         }
  158 
  159         return (err);
  160 }
  161 SYSCTL_PROC(_hw_snd, OID_AUTO, timestamp_precision, CTLTYPE_INT | CTLFLAG_RW,
  162     0, sizeof(int), sysctl_hw_snd_timestamp_precision, "I",
  163     "timestamp precision (0=s 1=hz 2=us 3=ns)");
  164 #endif
  165 
  166 /*
  167  * snd_clone_create() : Return opaque allocated clone manager.
  168  */
  169 struct snd_clone *
  170 snd_clone_create(int typemask, int maxunit, int deadline, uint32_t flags)
  171 {
  172         struct snd_clone *c;
  173 
  174         SND_CLONE_ASSERT(!(typemask & ~SND_CLONE_MAXUNIT),
  175             ("invalid typemask: 0x%08x", typemask));
  176         SND_CLONE_ASSERT(maxunit == -1 ||
  177             !(maxunit & ~(~typemask & SND_CLONE_MAXUNIT)),
  178             ("maxunit overflow: typemask=0x%08x maxunit=%d",
  179             typemask, maxunit));
  180         SND_CLONE_ASSERT(!(flags & ~SND_CLONE_MASK),
  181             ("invalid clone flags=0x%08x", flags));
  182 
  183         c = malloc(sizeof(*c), M_DEVBUF, M_WAITOK | M_ZERO);
  184         c->refcount = 0;
  185         c->size = 0;
  186         c->typemask = typemask;
  187         c->maxunit = (maxunit == -1) ? (~typemask & SND_CLONE_MAXUNIT) :
  188             maxunit;
  189         c->deadline = deadline;
  190         c->flags = flags;
  191         snd_timestamp(&c->tsp);
  192         TAILQ_INIT(&c->head);
  193 
  194         return (c);
  195 }
  196 
  197 int
  198 snd_clone_busy(struct snd_clone *c)
  199 {
  200         struct snd_clone_entry *ce;
  201 
  202         SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
  203 
  204         if (c->size == 0)
  205                 return (0);
  206 
  207         TAILQ_FOREACH(ce, &c->head, link) {
  208                 if ((ce->flags & SND_CLONE_BUSY) ||
  209                     (ce->devt != NULL && ce->devt->si_threadcount != 0))
  210                         return (EBUSY);
  211         }
  212 
  213         return (0);
  214 }
  215 
  216 /*
  217  * snd_clone_enable()/disable() : Suspend/resume clone allocation through
  218  * snd_clone_alloc(). Everything else will not be affected by this.
  219  */
  220 int
  221 snd_clone_enable(struct snd_clone *c)
  222 {
  223         SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
  224 
  225         if (c->flags & SND_CLONE_ENABLE)
  226                 return (EINVAL);
  227 
  228         c->flags |= SND_CLONE_ENABLE;
  229 
  230         return (0);
  231 }
  232 
  233 int
  234 snd_clone_disable(struct snd_clone *c)
  235 {
  236         SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
  237 
  238         if (!(c->flags & SND_CLONE_ENABLE))
  239                 return (EINVAL);
  240 
  241         c->flags &= ~SND_CLONE_ENABLE;
  242 
  243         return (0);
  244 }
  245 
  246 /*
  247  * Getters / Setters. Not worth explaining :)
  248  */
  249 int
  250 snd_clone_getsize(struct snd_clone *c)
  251 {
  252         SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
  253 
  254         return (c->size);
  255 }
  256 
  257 int
  258 snd_clone_getmaxunit(struct snd_clone *c)
  259 {
  260         SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
  261 
  262         return (c->maxunit);
  263 }
  264 
  265 int
  266 snd_clone_setmaxunit(struct snd_clone *c, int maxunit)
  267 {
  268         SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
  269         SND_CLONE_ASSERT(maxunit == -1 ||
  270             !(maxunit & ~(~c->typemask & SND_CLONE_MAXUNIT)),
  271             ("maxunit overflow: typemask=0x%08x maxunit=%d",
  272             c->typemask, maxunit));
  273 
  274         c->maxunit = (maxunit == -1) ? (~c->typemask & SND_CLONE_MAXUNIT) :
  275             maxunit;
  276 
  277         return (c->maxunit);
  278 }
  279 
  280 int
  281 snd_clone_getdeadline(struct snd_clone *c)
  282 {
  283         SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
  284 
  285         return (c->deadline);
  286 }
  287 
  288 int
  289 snd_clone_setdeadline(struct snd_clone *c, int deadline)
  290 {
  291         SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
  292 
  293         c->deadline = deadline;
  294 
  295         return (c->deadline);
  296 }
  297 
  298 int
  299 snd_clone_gettime(struct snd_clone *c, struct timespec *tsp)
  300 {
  301         SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
  302         SND_CLONE_ASSERT(tsp != NULL, ("NULL timespec"));
  303 
  304         *tsp = c->tsp;
  305 
  306         return (0);
  307 }
  308 
  309 uint32_t
  310 snd_clone_getflags(struct snd_clone *c)
  311 {
  312         SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
  313 
  314         return (c->flags);
  315 }
  316 
  317 uint32_t
  318 snd_clone_setflags(struct snd_clone *c, uint32_t flags)
  319 {
  320         SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
  321         SND_CLONE_ASSERT(!(flags & ~SND_CLONE_MASK),
  322             ("invalid clone flags=0x%08x", flags));
  323 
  324         c->flags = flags;
  325 
  326         return (c->flags);
  327 }
  328 
  329 int
  330 snd_clone_getdevtime(struct cdev *dev, struct timespec *tsp)
  331 {
  332         struct snd_clone_entry *ce;
  333 
  334         SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
  335         SND_CLONE_ASSERT(tsp != NULL, ("NULL timespec"));
  336 
  337         ce = dev->si_drv2;
  338         if (ce == NULL)
  339                 return (ENODEV);
  340 
  341         SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
  342 
  343         *tsp = ce->tsp;
  344 
  345         return (0);
  346 }
  347 
  348 uint32_t
  349 snd_clone_getdevflags(struct cdev *dev)
  350 {
  351         struct snd_clone_entry *ce;
  352 
  353         SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
  354 
  355         ce = dev->si_drv2;
  356         if (ce == NULL)
  357                 return (0xffffffff);
  358 
  359         SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
  360 
  361         return (ce->flags);
  362 }
  363 
  364 uint32_t
  365 snd_clone_setdevflags(struct cdev *dev, uint32_t flags)
  366 {
  367         struct snd_clone_entry *ce;
  368 
  369         SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
  370         SND_CLONE_ASSERT(!(flags & ~SND_CLONE_DEVMASK),
  371             ("invalid clone dev flags=0x%08x", flags));
  372 
  373         ce = dev->si_drv2;
  374         if (ce == NULL)
  375                 return (0xffffffff);
  376 
  377         SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
  378 
  379         ce->flags = flags;
  380 
  381         return (ce->flags);
  382 }
  383 
  384 /* Elapsed time conversion to ms */
  385 #define SND_CLONE_ELAPSED(x, y)                                         \
  386         ((((x)->tv_sec - (y)->tv_sec) * 1000) +                         \
  387         (((y)->tv_nsec > (x)->tv_nsec) ?                                \
  388         (((1000000000L + (x)->tv_nsec -                                 \
  389         (y)->tv_nsec) / 1000000) - 1000) :                              \
  390         (((x)->tv_nsec - (y)->tv_nsec) / 1000000)))
  391 
  392 #define SND_CLONE_EXPIRED(x, y, z)                                      \
  393         ((x)->deadline < 1 ||                                           \
  394         ((y)->tv_sec - (z)->tv_sec) > ((x)->deadline / 1000) ||         \
  395         SND_CLONE_ELAPSED(y, z) > (x)->deadline)
  396 
  397 /*
  398  * snd_clone_gc() : Garbage collector for stalled, expired objects. Refer to
  399  * clone.h for explanations on GC settings.
  400  */
  401 int
  402 snd_clone_gc(struct snd_clone *c)
  403 {
  404         struct snd_clone_entry *ce, *tce;
  405         struct timespec now;
  406         int pruned;
  407 
  408         SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
  409 
  410         if (!(c->flags & SND_CLONE_GC_ENABLE) || c->size == 0)
  411                 return (0);
  412 
  413         snd_timestamp(&now);
  414 
  415         /*
  416          * Bail out if the last clone handler was invoked below the deadline
  417          * threshold.
  418          */
  419         if ((c->flags & SND_CLONE_GC_EXPIRED) &&
  420             !SND_CLONE_EXPIRED(c, &now, &c->tsp))
  421                 return (0);
  422 
  423         pruned = 0;
  424 
  425         /*
  426          * Visit each object in reverse order. If the object is still being
  427          * referenced by a valid open(), skip it. Look for expired objects
  428          * and either revoke its clone invocation status or mercilessly
  429          * throw it away.
  430          */
  431         TAILQ_FOREACH_REVERSE_SAFE(ce, &c->head, link_head, link, tce) {
  432                 if (!(ce->flags & SND_CLONE_BUSY) &&
  433                     (!(ce->flags & SND_CLONE_INVOKE) ||
  434                     SND_CLONE_EXPIRED(c, &now, &ce->tsp))) {
  435                         if ((c->flags & SND_CLONE_GC_REVOKE) ||
  436                             ce->devt->si_threadcount != 0) {
  437                                 ce->flags &= ~SND_CLONE_INVOKE;
  438                                 ce->pid = -1;
  439                         } else {
  440                                 TAILQ_REMOVE(&c->head, ce, link);
  441                                 destroy_dev(ce->devt);
  442                                 free(ce, M_DEVBUF);
  443                                 c->size--;
  444                         }
  445                         pruned++;
  446                 }
  447         }
  448 
  449         /* return total pruned objects */
  450         return (pruned);
  451 }
  452 
  453 void
  454 snd_clone_destroy(struct snd_clone *c)
  455 {
  456         struct snd_clone_entry *ce, *tmp;
  457 
  458         SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
  459 
  460         ce = TAILQ_FIRST(&c->head);
  461         while (ce != NULL) {
  462                 tmp = TAILQ_NEXT(ce, link);
  463                 if (ce->devt != NULL)
  464                         destroy_dev(ce->devt);
  465                 free(ce, M_DEVBUF);
  466                 ce = tmp;
  467         }
  468 
  469         free(c, M_DEVBUF);
  470 }
  471 
  472 /*
  473  * snd_clone_acquire() : The vital part of concurrency management. Must be
  474  * called somewhere at the beginning of open() handler. ENODEV is not really
  475  * fatal since it just tell the caller that this is not cloned stuff.
  476  * EBUSY is *real*, don't forget that!
  477  */
  478 int
  479 snd_clone_acquire(struct cdev *dev)
  480 {
  481         struct snd_clone_entry *ce;
  482 
  483         SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
  484 
  485         ce = dev->si_drv2;
  486         if (ce == NULL)
  487                 return (ENODEV);
  488 
  489         SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
  490 
  491         ce->flags &= ~SND_CLONE_INVOKE;
  492 
  493         if (ce->flags & SND_CLONE_BUSY)
  494                 return (EBUSY);
  495 
  496         ce->flags |= SND_CLONE_BUSY;
  497 
  498         return (0);
  499 }
  500 
  501 /*
  502  * snd_clone_release() : Release busy status. Must be called somewhere at
  503  * the end of close() handler, or somewhere after fail open().
  504  */
  505 int
  506 snd_clone_release(struct cdev *dev)
  507 {
  508         struct snd_clone_entry *ce;
  509 
  510         SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
  511 
  512         ce = dev->si_drv2;
  513         if (ce == NULL)
  514                 return (ENODEV);
  515 
  516         SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
  517 
  518         ce->flags &= ~SND_CLONE_INVOKE;
  519 
  520         if (!(ce->flags & SND_CLONE_BUSY))
  521                 return (EBADF);
  522 
  523         ce->flags &= ~SND_CLONE_BUSY;
  524         ce->pid = -1;
  525 
  526         return (0);
  527 }
  528 
  529 /*
  530  * snd_clone_ref/unref() : Garbage collector reference counter. To make
  531  * garbage collector run automatically, the sequence must be something like
  532  * this (both in open() and close() handlers):
  533  *
  534  *  open() - 1) snd_clone_acquire()
  535  *           2) .... check check ... if failed, snd_clone_release()
  536  *           3) Success. Call snd_clone_ref()
  537  *
  538  * close() - 1) .... check check check ....
  539  *           2) Success. snd_clone_release()
  540  *           3) snd_clone_unref() . Garbage collector will run at this point
  541  *              if this is the last referenced object.
  542  */
  543 int
  544 snd_clone_ref(struct cdev *dev)
  545 {
  546         struct snd_clone_entry *ce;
  547         struct snd_clone *c;
  548 
  549         SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
  550 
  551         ce = dev->si_drv2;
  552         if (ce == NULL)
  553                 return (0);
  554 
  555         c = ce->parent;
  556         SND_CLONE_ASSERT(c != NULL, ("NULL parent"));
  557         SND_CLONE_ASSERT(c->refcount >= 0, ("refcount < 0"));
  558 
  559         return (++c->refcount);
  560 }
  561 
  562 int
  563 snd_clone_unref(struct cdev *dev)
  564 {
  565         struct snd_clone_entry *ce;
  566         struct snd_clone *c;
  567 
  568         SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
  569 
  570         ce = dev->si_drv2;
  571         if (ce == NULL)
  572                 return (0);
  573 
  574         c = ce->parent;
  575         SND_CLONE_ASSERT(c != NULL, ("NULL parent"));
  576         SND_CLONE_ASSERT(c->refcount > 0, ("refcount <= 0"));
  577 
  578         c->refcount--;
  579 
  580         /* 
  581          * Run automatic garbage collector, if needed.
  582          */
  583         if ((c->flags & SND_CLONE_GC_UNREF) &&
  584             (!(c->flags & SND_CLONE_GC_LASTREF) ||
  585             (c->refcount == 0 && (c->flags & SND_CLONE_GC_LASTREF))))
  586                 (void)snd_clone_gc(c);
  587 
  588         return (c->refcount);
  589 }
  590 
  591 void
  592 snd_clone_register(struct snd_clone_entry *ce, struct cdev *dev)
  593 {
  594         SND_CLONE_ASSERT(ce != NULL, ("NULL snd_clone_entry"));
  595         SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
  596         SND_CLONE_ASSERT(dev->si_drv2 == NULL, ("dev->si_drv2 not NULL"));
  597         SND_CLONE_ASSERT((ce->flags & SND_CLONE_ALLOC) == SND_CLONE_ALLOC,
  598             ("invalid clone alloc flags=0x%08x", ce->flags));
  599         SND_CLONE_ASSERT(ce->devt == NULL, ("ce->devt not NULL"));
  600         SND_CLONE_ASSERT(ce->unit == dev2unit(dev),
  601             ("invalid unit ce->unit=0x%08x dev2unit=0x%08x",
  602             ce->unit, dev2unit(dev)));
  603 
  604         SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
  605 
  606         dev->si_drv2 = ce;
  607         ce->devt = dev;
  608         ce->flags &= ~SND_CLONE_ALLOC;
  609         ce->flags |= SND_CLONE_INVOKE;
  610 }
  611 
  612 struct snd_clone_entry *
  613 snd_clone_alloc(struct snd_clone *c, struct cdev **dev, int *unit, int tmask)
  614 {
  615         struct snd_clone_entry *ce, *after, *bce, *cce, *nce, *tce;
  616         struct timespec now;
  617         int cunit, allocunit;
  618         pid_t curpid;
  619 
  620         SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
  621         SND_CLONE_ASSERT(dev != NULL, ("NULL dev pointer"));
  622         SND_CLONE_ASSERT((c->typemask & tmask) == tmask,
  623             ("invalid tmask: typemask=0x%08x tmask=0x%08x",
  624             c->typemask, tmask));
  625         SND_CLONE_ASSERT(unit != NULL, ("NULL unit pointer"));
  626         SND_CLONE_ASSERT(*unit == -1 || !(*unit & (c->typemask | tmask)),
  627             ("typemask collision: typemask=0x%08x tmask=0x%08x *unit=%d",
  628             c->typemask, tmask, *unit));
  629 
  630         if (!(c->flags & SND_CLONE_ENABLE) ||
  631             (*unit != -1 && *unit > c->maxunit))
  632                 return (NULL);
  633 
  634         ce = NULL;
  635         after = NULL;
  636         bce = NULL;     /* "b"usy candidate */
  637         cce = NULL;     /* "c"urthread/proc candidate */
  638         nce = NULL;     /* "n"ull, totally unbusy candidate */
  639         tce = NULL;     /* Last "t"ry candidate */
  640         cunit = 0;
  641         allocunit = (*unit == -1) ? 0 : *unit;
  642         curpid = curthread->td_proc->p_pid;
  643 
  644         snd_timestamp(&now);
  645 
  646         TAILQ_FOREACH(ce, &c->head, link) {
  647                 /*
  648                  * Sort incrementally according to device type.
  649                  */
  650                 if (tmask > (ce->unit & c->typemask)) {
  651                         if (cunit == 0)
  652                                 after = ce;
  653                         continue;
  654                 } else if (tmask < (ce->unit & c->typemask))
  655                         break;
  656 
  657                 /*
  658                  * Shoot.. this is where the grumpiness begin. Just
  659                  * return immediately.
  660                  */
  661                 if (*unit != -1 && *unit == (ce->unit & ~tmask))
  662                         goto snd_clone_alloc_out;
  663 
  664                 cunit++;
  665                 /*
  666                  * Simmilar device type. Sort incrementally according
  667                  * to allocation unit. While here, look for free slot
  668                  * and possible collision for new / future allocation.
  669                  */
  670                 if (*unit == -1 && (ce->unit & ~tmask) == allocunit)
  671                         allocunit++;
  672                 if ((ce->unit & ~tmask) < allocunit)
  673                         after = ce;
  674                 /*
  675                  * Clone logic:
  676                  *   1. Look for non busy, but keep track of the best
  677                  *      possible busy cdev.
  678                  *   2. Look for the best (oldest referenced) entry that is
  679                  *      in a same process / thread.
  680                  *   3. Look for the best (oldest referenced), absolute free
  681                  *      entry.
  682                  *   4. Lastly, look for the best (oldest referenced)
  683                  *      any entries that doesn't fit with anything above.
  684                  */
  685                 if (ce->flags & SND_CLONE_BUSY) {
  686                         if (ce->devt != NULL && (bce == NULL ||
  687                             timespeccmp(&ce->tsp, &bce->tsp, <)))
  688                                 bce = ce;
  689                         continue;
  690                 }
  691                 if (ce->pid == curpid &&
  692                     (cce == NULL || timespeccmp(&ce->tsp, &cce->tsp, <)))
  693                         cce = ce;
  694                 else if (!(ce->flags & SND_CLONE_INVOKE) &&
  695                     (nce == NULL || timespeccmp(&ce->tsp, &nce->tsp, <)))
  696                         nce = ce;
  697                 else if (tce == NULL || timespeccmp(&ce->tsp, &tce->tsp, <))
  698                         tce = ce;
  699         }
  700         if (*unit != -1)
  701                 goto snd_clone_alloc_new;
  702         else if (cce != NULL) {
  703                 /* Same proc entry found, go for it */
  704                 ce = cce;
  705                 goto snd_clone_alloc_out;
  706         } else if (nce != NULL) {
  707                 /*
  708                  * Next, try absolute free entry. If the calculated
  709                  * allocunit is smaller, create new entry instead.
  710                  */
  711                 if (allocunit < (nce->unit & ~tmask))
  712                         goto snd_clone_alloc_new;
  713                 ce = nce;
  714                 goto snd_clone_alloc_out;
  715         } else if (allocunit > c->maxunit) {
  716                 /*
  717                  * Maximum allowable unit reached. Try returning any
  718                  * available cdev and hope for the best. If the lookup is
  719                  * done for things like stat(), mtime() etc. , things should
  720                  * be ok. Otherwise, open() handler should do further checks
  721                  * and decide whether to return correct error code or not.
  722                  */
  723                 if (tce != NULL) {
  724                         ce = tce;
  725                         goto snd_clone_alloc_out;
  726                 } else if (bce != NULL) {
  727                         ce = bce;
  728                         goto snd_clone_alloc_out;
  729                 }
  730                 return (NULL);
  731         }
  732 
  733 snd_clone_alloc_new:
  734         /*
  735          * No free entries found, and we still haven't reached maximum
  736          * allowable units. Allocate, setup a minimal unique entry with busy
  737          * status so nobody will monkey on this new entry. Unit magic is set
  738          * right here to avoid collision with other contesting handler.
  739          * The caller must be carefull here to maintain its own
  740          * synchronization, as long as it will not conflict with malloc(9)
  741          * operations.
  742          *
  743          * That said, go figure.
  744          */
  745         ce = malloc(sizeof(*ce), M_DEVBUF,
  746             ((c->flags & SND_CLONE_WAITOK) ? M_WAITOK : M_NOWAIT) | M_ZERO);
  747         if (ce == NULL) {
  748                 if (*unit != -1)
  749                         return (NULL);
  750                 /*
  751                  * We're being dense, ignorance is bliss,
  752                  * Super Regulatory Measure (TM).. TRY AGAIN!
  753                  */
  754                 if (nce != NULL) {
  755                         ce = nce;
  756                         goto snd_clone_alloc_out;
  757                 } else if (tce != NULL) {
  758                         ce = tce;
  759                         goto snd_clone_alloc_out;
  760                 } else if (bce != NULL) {
  761                         ce = bce;
  762                         goto snd_clone_alloc_out;
  763                 }
  764                 return (NULL);
  765         }
  766         /* Setup new entry */
  767         ce->parent = c;
  768         ce->unit = tmask | allocunit;
  769         ce->pid = curpid;
  770         ce->tsp = now;
  771         ce->flags |= SND_CLONE_ALLOC;
  772         if (after != NULL) {
  773                 TAILQ_INSERT_AFTER(&c->head, after, ce, link);
  774         } else {
  775                 TAILQ_INSERT_HEAD(&c->head, ce, link);
  776         }
  777         c->size++;
  778         c->tsp = now;
  779         /*
  780          * Save new allocation unit for caller which will be used
  781          * by make_dev().
  782          */
  783         *unit = allocunit;
  784 
  785         return (ce);
  786 
  787 snd_clone_alloc_out:
  788         /*
  789          * Set, mark, timestamp the entry if this is a truly free entry.
  790          * Leave busy entry alone.
  791          */
  792         if (!(ce->flags & SND_CLONE_BUSY)) {
  793                 ce->pid = curpid;
  794                 ce->tsp = now;
  795                 ce->flags |= SND_CLONE_INVOKE;
  796         }
  797         c->tsp = now;
  798         *dev = ce->devt;
  799 
  800         return (NULL);
  801 }

Cache object: 01cfc9e92379899123c76ee5e4694c8c


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