clone.c revision 170719
1170159Sariff/*- 2170159Sariff * Copyright (c) 2007 Ariff Abdullah <ariff@FreeBSD.org> 3170159Sariff * All rights reserved. 4170159Sariff * 5170159Sariff * Redistribution and use in source and binary forms, with or without 6170159Sariff * modification, are permitted provided that the following conditions 7170159Sariff * are met: 8170159Sariff * 1. Redistributions of source code must retain the above copyright 9170159Sariff * notice, this list of conditions and the following disclaimer. 10170159Sariff * 2. Redistributions in binary form must reproduce the above copyright 11170159Sariff * notice, this list of conditions and the following disclaimer in the 12170159Sariff * documentation and/or other materials provided with the distribution. 13170159Sariff * 14170159Sariff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15170159Sariff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16170159Sariff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17170159Sariff * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18170159Sariff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19170159Sariff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20170159Sariff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21170159Sariff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22170159Sariff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23170159Sariff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24170159Sariff * SUCH DAMAGE. 25170159Sariff * 26170159Sariff * $FreeBSD: head/sys/dev/sound/clone.c 170719 2007-06-14 11:10:21Z ariff $ 27170159Sariff */ 28170159Sariff 29170159Sariff#include <sys/param.h> 30170159Sariff#include <sys/systm.h> 31170159Sariff#include <sys/conf.h> 32170159Sariff#include <sys/kernel.h> 33170159Sariff#include <sys/malloc.h> 34170159Sariff#include <sys/proc.h> 35170159Sariff 36170159Sariff#if defined(SND_DIAGNOSTIC) || defined(SND_DEBUG) 37170159Sariff#include <dev/sound/pcm/sound.h> 38170159Sariff#endif 39170159Sariff 40170159Sariff#include <dev/sound/clone.h> 41170159Sariff 42170159Sariff/* 43170159Sariff * So here we go again, another clonedevs manager. Unlike default clonedevs, 44170159Sariff * this clone manager is designed to withstand various abusive behavior 45170159Sariff * (such as 'while : ; do ls /dev/whatever ; done', etc.), reusable object 46170159Sariff * after reaching certain expiration threshold, aggressive garbage collector, 47170159Sariff * transparent device allocator and concurrency handling across multiple 48170159Sariff * thread/proc. Due to limited information given by dev_clone EVENTHANDLER, 49170159Sariff * we don't have much clues whether the caller wants a real open() or simply 50170159Sariff * making fun of us with things like stat(), mtime() etc. Assuming that: 51170159Sariff * 1) Time window between dev_clone EH <-> real open() should be small 52170159Sariff * enough and 2) mtime()/stat() etc. always looks like a half way / stalled 53170159Sariff * operation, we can decide whether a new cdev must be created, old 54170159Sariff * (expired) cdev can be reused or an existing cdev can be shared. 55170159Sariff * 56170159Sariff * Most of the operations and logics are generic enough and can be applied 57170159Sariff * on other places (such as if_tap, snp, etc). Perhaps this can be 58170159Sariff * rearranged to complement clone_*(). However, due to this still being 59170159Sariff * specific to the sound driver (and as a proof of concept on how it can be 60170159Sariff * done), si_drv2 is used to keep the pointer of the clone list entry to 61170159Sariff * avoid expensive lookup. 62170159Sariff */ 63170159Sariff 64170159Sariff/* clone entry */ 65170159Sariffstruct snd_clone_entry { 66170159Sariff TAILQ_ENTRY(snd_clone_entry) link; 67170159Sariff struct snd_clone *parent; 68170159Sariff struct cdev *devt; 69170159Sariff struct timespec tsp; 70170159Sariff uint32_t flags; 71170159Sariff pid_t pid; 72170159Sariff int unit; 73170159Sariff}; 74170159Sariff 75170159Sariff/* clone manager */ 76170159Sariffstruct snd_clone { 77170159Sariff TAILQ_HEAD(link_head, snd_clone_entry) head; 78170159Sariff struct timespec tsp; 79170159Sariff int refcount; 80170159Sariff int size; 81170159Sariff int typemask; 82170159Sariff int maxunit; 83170159Sariff int deadline; 84170159Sariff uint32_t flags; 85170159Sariff}; 86170159Sariff 87170159Sariff#ifdef SND_DIAGNOSTIC 88170159Sariff#define SND_CLONE_ASSERT(x, y) do { \ 89170159Sariff if (!(x)) \ 90170159Sariff panic y; \ 91170159Sariff} while(0) 92170159Sariff#else 93170159Sariff#define SND_CLONE_ASSERT(x...) KASSERT(x) 94170159Sariff#endif 95170159Sariff 96170159Sariff/* 97170159Sariff * Shamelessly ripped off from vfs_subr.c 98170159Sariff * We need at least 1/HZ precision as default timestamping. 99170159Sariff */ 100170159Sariffenum { SND_TSP_SEC, SND_TSP_HZ, SND_TSP_USEC, SND_TSP_NSEC }; 101170159Sariff 102170159Sariffstatic int snd_timestamp_precision = SND_TSP_HZ; 103170159SariffTUNABLE_INT("hw.snd.timestamp_precision", &snd_timestamp_precision); 104170159Sariff 105170159Sariffvoid 106170159Sariffsnd_timestamp(struct timespec *tsp) 107170159Sariff{ 108170159Sariff struct timeval tv; 109170159Sariff 110170159Sariff switch (snd_timestamp_precision) { 111170159Sariff case SND_TSP_SEC: 112170159Sariff tsp->tv_sec = time_second; 113170159Sariff tsp->tv_nsec = 0; 114170159Sariff break; 115170159Sariff case SND_TSP_HZ: 116170159Sariff getnanouptime(tsp); 117170159Sariff break; 118170159Sariff case SND_TSP_USEC: 119170159Sariff microuptime(&tv); 120170159Sariff TIMEVAL_TO_TIMESPEC(&tv, tsp); 121170159Sariff break; 122170159Sariff case SND_TSP_NSEC: 123170159Sariff nanouptime(tsp); 124170159Sariff break; 125170159Sariff default: 126170159Sariff snd_timestamp_precision = SND_TSP_HZ; 127170159Sariff getnanouptime(tsp); 128170159Sariff break; 129170159Sariff } 130170159Sariff} 131170159Sariff 132170159Sariff#if defined(SND_DIAGNOSTIC) || defined(SND_DEBUG) 133170159Sariffstatic int 134170159Sariffsysctl_hw_snd_timestamp_precision(SYSCTL_HANDLER_ARGS) 135170159Sariff{ 136170159Sariff int err, val; 137170159Sariff 138170159Sariff val = snd_timestamp_precision; 139170289Sdwmalone err = sysctl_handle_int(oidp, &val, 0, req); 140170159Sariff if (err == 0 && req->newptr != NULL) { 141170159Sariff switch (val) { 142170159Sariff case SND_TSP_SEC: 143170159Sariff case SND_TSP_HZ: 144170159Sariff case SND_TSP_USEC: 145170159Sariff case SND_TSP_NSEC: 146170159Sariff snd_timestamp_precision = val; 147170159Sariff break; 148170159Sariff default: 149170159Sariff break; 150170159Sariff } 151170159Sariff } 152170159Sariff 153170159Sariff return (err); 154170159Sariff} 155170159SariffSYSCTL_PROC(_hw_snd, OID_AUTO, timestamp_precision, CTLTYPE_INT | CTLFLAG_RW, 156170159Sariff 0, sizeof(int), sysctl_hw_snd_timestamp_precision, "I", 157170159Sariff "timestamp precision (0=s 1=hz 2=us 3=ns)"); 158170159Sariff#endif 159170159Sariff 160170159Sariff/* 161170719Sariff * snd_clone_create() : Return opaque allocated clone manager. 162170159Sariff */ 163170159Sariffstruct snd_clone * 164170719Sariffsnd_clone_create(int typemask, int maxunit, int deadline, uint32_t flags) 165170159Sariff{ 166170159Sariff struct snd_clone *c; 167170159Sariff 168170159Sariff SND_CLONE_ASSERT(!(typemask & ~SND_CLONE_MAXUNIT), 169170159Sariff ("invalid typemask: 0x%08x", typemask)); 170170159Sariff SND_CLONE_ASSERT(maxunit == -1 || 171170159Sariff !(maxunit & ~(~typemask & SND_CLONE_MAXUNIT)), 172170159Sariff ("maxunit overflow: typemask=0x%08x maxunit=%d", 173170159Sariff typemask, maxunit)); 174170159Sariff SND_CLONE_ASSERT(!(flags & ~SND_CLONE_MASK), 175170159Sariff ("invalid clone flags=0x%08x", flags)); 176170159Sariff 177170159Sariff c = malloc(sizeof(*c), M_DEVBUF, M_WAITOK | M_ZERO); 178170159Sariff c->refcount = 0; 179170159Sariff c->size = 0; 180170159Sariff c->typemask = typemask; 181170159Sariff c->maxunit = (maxunit == -1) ? (~typemask & SND_CLONE_MAXUNIT) : 182170159Sariff maxunit; 183170159Sariff c->deadline = deadline; 184170159Sariff c->flags = flags; 185170159Sariff snd_timestamp(&c->tsp); 186170159Sariff TAILQ_INIT(&c->head); 187170159Sariff 188170159Sariff return (c); 189170159Sariff} 190170159Sariff 191170159Sariffint 192170159Sariffsnd_clone_busy(struct snd_clone *c) 193170159Sariff{ 194170159Sariff struct snd_clone_entry *ce; 195170159Sariff 196170159Sariff SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); 197170159Sariff 198170159Sariff if (c->size == 0) 199170159Sariff return (0); 200170159Sariff 201170159Sariff TAILQ_FOREACH(ce, &c->head, link) { 202170159Sariff if ((ce->flags & SND_CLONE_BUSY) || 203170159Sariff (ce->devt != NULL && ce->devt->si_threadcount != 0)) 204170159Sariff return (EBUSY); 205170159Sariff } 206170159Sariff 207170159Sariff return (0); 208170159Sariff} 209170159Sariff 210170159Sariff/* 211170159Sariff * snd_clone_enable()/disable() : Suspend/resume clone allocation through 212170159Sariff * snd_clone_alloc(). Everything else will not be affected by this. 213170159Sariff */ 214170159Sariffint 215170159Sariffsnd_clone_enable(struct snd_clone *c) 216170159Sariff{ 217170159Sariff SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); 218170159Sariff 219170159Sariff if (c->flags & SND_CLONE_ENABLE) 220170159Sariff return (EINVAL); 221170159Sariff 222170159Sariff c->flags |= SND_CLONE_ENABLE; 223170159Sariff 224170159Sariff return (0); 225170159Sariff} 226170159Sariff 227170159Sariffint 228170159Sariffsnd_clone_disable(struct snd_clone *c) 229170159Sariff{ 230170159Sariff SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); 231170159Sariff 232170159Sariff if (!(c->flags & SND_CLONE_ENABLE)) 233170159Sariff return (EINVAL); 234170159Sariff 235170159Sariff c->flags &= ~SND_CLONE_ENABLE; 236170159Sariff 237170159Sariff return (0); 238170159Sariff} 239170159Sariff 240170159Sariff/* 241170159Sariff * Getters / Setters. Not worth explaining :) 242170159Sariff */ 243170159Sariffint 244170159Sariffsnd_clone_getsize(struct snd_clone *c) 245170159Sariff{ 246170159Sariff SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); 247170159Sariff 248170159Sariff return (c->size); 249170159Sariff} 250170159Sariff 251170159Sariffint 252170159Sariffsnd_clone_getmaxunit(struct snd_clone *c) 253170159Sariff{ 254170159Sariff SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); 255170159Sariff 256170159Sariff return (c->maxunit); 257170159Sariff} 258170159Sariff 259170159Sariffint 260170159Sariffsnd_clone_setmaxunit(struct snd_clone *c, int maxunit) 261170159Sariff{ 262170159Sariff SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); 263170159Sariff SND_CLONE_ASSERT(maxunit == -1 || 264170159Sariff !(maxunit & ~(~c->typemask & SND_CLONE_MAXUNIT)), 265170159Sariff ("maxunit overflow: typemask=0x%08x maxunit=%d", 266170159Sariff c->typemask, maxunit)); 267170159Sariff 268170159Sariff c->maxunit = (maxunit == -1) ? (~c->typemask & SND_CLONE_MAXUNIT) : 269170159Sariff maxunit; 270170159Sariff 271170159Sariff return (c->maxunit); 272170159Sariff} 273170159Sariff 274170159Sariffint 275170159Sariffsnd_clone_getdeadline(struct snd_clone *c) 276170159Sariff{ 277170159Sariff SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); 278170159Sariff 279170159Sariff return (c->deadline); 280170159Sariff} 281170159Sariff 282170159Sariffint 283170159Sariffsnd_clone_setdeadline(struct snd_clone *c, int deadline) 284170159Sariff{ 285170159Sariff SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); 286170159Sariff 287170159Sariff c->deadline = deadline; 288170159Sariff 289170159Sariff return (c->deadline); 290170159Sariff} 291170159Sariff 292170159Sariffint 293170159Sariffsnd_clone_gettime(struct snd_clone *c, struct timespec *tsp) 294170159Sariff{ 295170159Sariff SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); 296170159Sariff SND_CLONE_ASSERT(tsp != NULL, ("NULL timespec")); 297170159Sariff 298170159Sariff *tsp = c->tsp; 299170159Sariff 300170159Sariff return (0); 301170159Sariff} 302170159Sariff 303170159Sariffuint32_t 304170159Sariffsnd_clone_getflags(struct snd_clone *c) 305170159Sariff{ 306170159Sariff SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); 307170159Sariff 308170159Sariff return (c->flags); 309170159Sariff} 310170159Sariff 311170159Sariffuint32_t 312170159Sariffsnd_clone_setflags(struct snd_clone *c, uint32_t flags) 313170159Sariff{ 314170159Sariff SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); 315170159Sariff SND_CLONE_ASSERT(!(flags & ~SND_CLONE_MASK), 316170159Sariff ("invalid clone flags=0x%08x", flags)); 317170159Sariff 318170159Sariff c->flags = flags; 319170159Sariff 320170159Sariff return (c->flags); 321170159Sariff} 322170159Sariff 323170159Sariffint 324170159Sariffsnd_clone_getdevtime(struct cdev *dev, struct timespec *tsp) 325170159Sariff{ 326170159Sariff struct snd_clone_entry *ce; 327170159Sariff 328170159Sariff SND_CLONE_ASSERT(dev != NULL, ("NULL dev")); 329170159Sariff SND_CLONE_ASSERT(tsp != NULL, ("NULL timespec")); 330170159Sariff 331170159Sariff ce = dev->si_drv2; 332170159Sariff if (ce == NULL) 333170159Sariff return (ENODEV); 334170159Sariff 335170159Sariff SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent")); 336170159Sariff 337170159Sariff *tsp = ce->tsp; 338170159Sariff 339170159Sariff return (0); 340170159Sariff} 341170159Sariff 342170159Sariffuint32_t 343170159Sariffsnd_clone_getdevflags(struct cdev *dev) 344170159Sariff{ 345170159Sariff struct snd_clone_entry *ce; 346170159Sariff 347170159Sariff SND_CLONE_ASSERT(dev != NULL, ("NULL dev")); 348170159Sariff 349170159Sariff ce = dev->si_drv2; 350170159Sariff if (ce == NULL) 351170159Sariff return (0xffffffff); 352170159Sariff 353170159Sariff SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent")); 354170159Sariff 355170159Sariff return (ce->flags); 356170159Sariff} 357170159Sariff 358170159Sariffuint32_t 359170159Sariffsnd_clone_setdevflags(struct cdev *dev, uint32_t flags) 360170159Sariff{ 361170159Sariff struct snd_clone_entry *ce; 362170159Sariff 363170159Sariff SND_CLONE_ASSERT(dev != NULL, ("NULL dev")); 364170159Sariff SND_CLONE_ASSERT(!(flags & ~SND_CLONE_DEVMASK), 365170159Sariff ("invalid clone dev flags=0x%08x", flags)); 366170159Sariff 367170159Sariff ce = dev->si_drv2; 368170159Sariff if (ce == NULL) 369170159Sariff return (0xffffffff); 370170159Sariff 371170159Sariff SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent")); 372170159Sariff 373170159Sariff ce->flags = flags; 374170159Sariff 375170159Sariff return (ce->flags); 376170159Sariff} 377170159Sariff 378170159Sariff/* Elapsed time conversion to ms */ 379170159Sariff#define SND_CLONE_ELAPSED(x, y) \ 380170159Sariff ((((x)->tv_sec - (y)->tv_sec) * 1000) + \ 381170159Sariff (((y)->tv_nsec > (x)->tv_nsec) ? \ 382170159Sariff (((1000000000L + (x)->tv_nsec - \ 383170159Sariff (y)->tv_nsec) / 1000000) - 1000) : \ 384170159Sariff (((x)->tv_nsec - (y)->tv_nsec) / 1000000))) 385170159Sariff 386170159Sariff#define SND_CLONE_EXPIRED(x, y, z) \ 387170159Sariff ((x)->deadline < 1 || \ 388170159Sariff ((y)->tv_sec - (z)->tv_sec) > ((x)->deadline / 1000) || \ 389170159Sariff SND_CLONE_ELAPSED(y, z) > (x)->deadline) 390170159Sariff 391170159Sariff/* 392170159Sariff * snd_clone_gc() : Garbage collector for stalled, expired objects. Refer to 393170159Sariff * clone.h for explanations on GC settings. 394170159Sariff */ 395170159Sariffint 396170159Sariffsnd_clone_gc(struct snd_clone *c) 397170159Sariff{ 398170159Sariff struct snd_clone_entry *ce, *tce; 399170159Sariff struct timespec now; 400170159Sariff int pruned; 401170159Sariff 402170159Sariff SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); 403170159Sariff 404170159Sariff if (!(c->flags & SND_CLONE_GC_ENABLE) || c->size == 0) 405170159Sariff return (0); 406170159Sariff 407170159Sariff snd_timestamp(&now); 408170159Sariff 409170159Sariff /* 410170159Sariff * Bail out if the last clone handler was invoked below the deadline 411170159Sariff * threshold. 412170159Sariff */ 413170159Sariff if ((c->flags & SND_CLONE_GC_EXPIRED) && 414170159Sariff !SND_CLONE_EXPIRED(c, &now, &c->tsp)) 415170159Sariff return (0); 416170159Sariff 417170159Sariff pruned = 0; 418170159Sariff 419170159Sariff /* 420170159Sariff * Visit each object in reverse order. If the object is still being 421170159Sariff * referenced by a valid open(), skip it. Look for expired objects 422170159Sariff * and either revoke its clone invocation status or mercilessly 423170159Sariff * throw it away. 424170159Sariff */ 425170159Sariff TAILQ_FOREACH_REVERSE_SAFE(ce, &c->head, link_head, link, tce) { 426170159Sariff if (!(ce->flags & SND_CLONE_BUSY) && 427170159Sariff (!(ce->flags & SND_CLONE_INVOKE) || 428170159Sariff SND_CLONE_EXPIRED(c, &now, &ce->tsp))) { 429170159Sariff if ((c->flags & SND_CLONE_GC_REVOKE) || 430170159Sariff ce->devt->si_threadcount != 0) { 431170159Sariff ce->flags &= ~SND_CLONE_INVOKE; 432170159Sariff ce->pid = -1; 433170159Sariff } else { 434170159Sariff TAILQ_REMOVE(&c->head, ce, link); 435170159Sariff destroy_dev(ce->devt); 436170159Sariff free(ce, M_DEVBUF); 437170159Sariff c->size--; 438170159Sariff } 439170159Sariff pruned++; 440170159Sariff } 441170159Sariff } 442170159Sariff 443170159Sariff /* return total pruned objects */ 444170159Sariff return (pruned); 445170159Sariff} 446170159Sariff 447170159Sariffvoid 448170159Sariffsnd_clone_destroy(struct snd_clone *c) 449170159Sariff{ 450170719Sariff struct snd_clone_entry *ce, *tmp; 451170159Sariff 452170159Sariff SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); 453170159Sariff 454170719Sariff ce = TAILQ_FIRST(&c->head); 455170719Sariff while (ce != NULL) { 456170719Sariff tmp = TAILQ_NEXT(ce, link); 457170159Sariff if (ce->devt != NULL) 458170159Sariff destroy_dev(ce->devt); 459170159Sariff free(ce, M_DEVBUF); 460170719Sariff ce = tmp; 461170159Sariff } 462170159Sariff 463170159Sariff free(c, M_DEVBUF); 464170159Sariff} 465170159Sariff 466170159Sariff/* 467170159Sariff * snd_clone_acquire() : The vital part of concurrency management. Must be 468170159Sariff * called somewhere at the beginning of open() handler. ENODEV is not really 469170159Sariff * fatal since it just tell the caller that this is not cloned stuff. 470170159Sariff * EBUSY is *real*, don't forget that! 471170159Sariff */ 472170159Sariffint 473170159Sariffsnd_clone_acquire(struct cdev *dev) 474170159Sariff{ 475170159Sariff struct snd_clone_entry *ce; 476170159Sariff 477170159Sariff SND_CLONE_ASSERT(dev != NULL, ("NULL dev")); 478170159Sariff 479170159Sariff ce = dev->si_drv2; 480170159Sariff if (ce == NULL) 481170159Sariff return (ENODEV); 482170159Sariff 483170159Sariff SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent")); 484170159Sariff 485170159Sariff ce->flags &= ~SND_CLONE_INVOKE; 486170159Sariff 487170159Sariff if (ce->flags & SND_CLONE_BUSY) 488170159Sariff return (EBUSY); 489170159Sariff 490170159Sariff ce->flags |= SND_CLONE_BUSY; 491170159Sariff 492170159Sariff return (0); 493170159Sariff} 494170159Sariff 495170159Sariff/* 496170159Sariff * snd_clone_release() : Release busy status. Must be called somewhere at 497170159Sariff * the end of close() handler, or somewhere after fail open(). 498170159Sariff */ 499170159Sariffint 500170159Sariffsnd_clone_release(struct cdev *dev) 501170159Sariff{ 502170159Sariff struct snd_clone_entry *ce; 503170159Sariff 504170159Sariff SND_CLONE_ASSERT(dev != NULL, ("NULL dev")); 505170159Sariff 506170159Sariff ce = dev->si_drv2; 507170159Sariff if (ce == NULL) 508170159Sariff return (ENODEV); 509170159Sariff 510170159Sariff SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent")); 511170159Sariff 512170159Sariff ce->flags &= ~SND_CLONE_INVOKE; 513170159Sariff 514170159Sariff if (!(ce->flags & SND_CLONE_BUSY)) 515170159Sariff return (EBADF); 516170159Sariff 517170159Sariff ce->flags &= ~SND_CLONE_BUSY; 518170159Sariff ce->pid = -1; 519170159Sariff 520170159Sariff return (0); 521170159Sariff} 522170159Sariff 523170159Sariff/* 524170159Sariff * snd_clone_ref/unref() : Garbage collector reference counter. To make 525170159Sariff * garbage collector run automatically, the sequence must be something like 526170159Sariff * this (both in open() and close() handlers): 527170159Sariff * 528170159Sariff * open() - 1) snd_clone_acquire() 529170159Sariff * 2) .... check check ... if failed, snd_clone_release() 530170159Sariff * 3) Success. Call snd_clone_ref() 531170159Sariff * 532170159Sariff * close() - 1) .... check check check .... 533170159Sariff * 2) Success. snd_clone_release() 534170159Sariff * 3) snd_clone_unref() . Garbage collector will run at this point 535170159Sariff * if this is the last referenced object. 536170159Sariff */ 537170159Sariffint 538170159Sariffsnd_clone_ref(struct cdev *dev) 539170159Sariff{ 540170159Sariff struct snd_clone_entry *ce; 541170159Sariff struct snd_clone *c; 542170159Sariff 543170159Sariff SND_CLONE_ASSERT(dev != NULL, ("NULL dev")); 544170159Sariff 545170159Sariff ce = dev->si_drv2; 546170159Sariff if (ce == NULL) 547170159Sariff return (0); 548170159Sariff 549170159Sariff c = ce->parent; 550170159Sariff SND_CLONE_ASSERT(c != NULL, ("NULL parent")); 551170159Sariff SND_CLONE_ASSERT(c->refcount >= 0, ("refcount < 0")); 552170159Sariff 553170159Sariff return (++c->refcount); 554170159Sariff} 555170159Sariff 556170159Sariffint 557170159Sariffsnd_clone_unref(struct cdev *dev) 558170159Sariff{ 559170159Sariff struct snd_clone_entry *ce; 560170159Sariff struct snd_clone *c; 561170159Sariff 562170159Sariff SND_CLONE_ASSERT(dev != NULL, ("NULL dev")); 563170159Sariff 564170159Sariff ce = dev->si_drv2; 565170159Sariff if (ce == NULL) 566170159Sariff return (0); 567170159Sariff 568170159Sariff c = ce->parent; 569170159Sariff SND_CLONE_ASSERT(c != NULL, ("NULL parent")); 570170159Sariff SND_CLONE_ASSERT(c->refcount > 0, ("refcount <= 0")); 571170159Sariff 572170159Sariff c->refcount--; 573170159Sariff 574170159Sariff /* 575170159Sariff * Run automatic garbage collector, if needed. 576170159Sariff */ 577170159Sariff if ((c->flags & SND_CLONE_GC_UNREF) && 578170159Sariff (!(c->flags & SND_CLONE_GC_LASTREF) || 579170159Sariff (c->refcount == 0 && (c->flags & SND_CLONE_GC_LASTREF)))) 580170159Sariff (void)snd_clone_gc(c); 581170159Sariff 582170159Sariff return (c->refcount); 583170159Sariff} 584170159Sariff 585170159Sariffvoid 586170159Sariffsnd_clone_register(struct snd_clone_entry *ce, struct cdev *dev) 587170159Sariff{ 588170159Sariff SND_CLONE_ASSERT(ce != NULL, ("NULL snd_clone_entry")); 589170159Sariff SND_CLONE_ASSERT(dev != NULL, ("NULL dev")); 590170159Sariff SND_CLONE_ASSERT(dev->si_drv2 == NULL, ("dev->si_drv2 not NULL")); 591170159Sariff SND_CLONE_ASSERT((ce->flags & SND_CLONE_ALLOC) == SND_CLONE_ALLOC, 592170159Sariff ("invalid clone alloc flags=0x%08x", ce->flags)); 593170159Sariff SND_CLONE_ASSERT(ce->devt == NULL, ("ce->devt not NULL")); 594170159Sariff SND_CLONE_ASSERT(ce->unit == dev2unit(dev), 595170159Sariff ("invalid unit ce->unit=0x%08x dev2unit=0x%08x", 596170159Sariff ce->unit, dev2unit(dev))); 597170159Sariff 598170159Sariff SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent")); 599170159Sariff 600170159Sariff dev->si_drv2 = ce; 601170159Sariff ce->devt = dev; 602170159Sariff ce->flags &= ~SND_CLONE_ALLOC; 603170159Sariff ce->flags |= SND_CLONE_INVOKE; 604170159Sariff} 605170159Sariff 606170159Sariffstruct snd_clone_entry * 607170159Sariffsnd_clone_alloc(struct snd_clone *c, struct cdev **dev, int *unit, int tmask) 608170159Sariff{ 609170159Sariff struct snd_clone_entry *ce, *after, *bce, *cce, *nce, *tce; 610170159Sariff struct timespec now; 611170159Sariff int cunit, allocunit; 612170159Sariff pid_t curpid; 613170159Sariff 614170159Sariff SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); 615170159Sariff SND_CLONE_ASSERT(dev != NULL, ("NULL dev pointer")); 616170159Sariff SND_CLONE_ASSERT((c->typemask & tmask) == tmask, 617170159Sariff ("invalid tmask: typemask=0x%08x tmask=0x%08x", 618170159Sariff c->typemask, tmask)); 619170159Sariff SND_CLONE_ASSERT(unit != NULL, ("NULL unit pointer")); 620170159Sariff SND_CLONE_ASSERT(*unit == -1 || !(*unit & (c->typemask | tmask)), 621170159Sariff ("typemask collision: typemask=0x%08x tmask=0x%08x *unit=%d", 622170159Sariff c->typemask, tmask, *unit)); 623170159Sariff 624170159Sariff if (!(c->flags & SND_CLONE_ENABLE) || 625170159Sariff (*unit != -1 && *unit > c->maxunit)) 626170159Sariff return (NULL); 627170159Sariff 628170159Sariff ce = NULL; 629170159Sariff after = NULL; 630170159Sariff bce = NULL; /* "b"usy candidate */ 631170159Sariff cce = NULL; /* "c"urthread/proc candidate */ 632170159Sariff nce = NULL; /* "n"ull, totally unbusy candidate */ 633170159Sariff tce = NULL; /* Last "t"ry candidate */ 634170159Sariff cunit = 0; 635170159Sariff allocunit = (*unit == -1) ? 0 : *unit; 636170159Sariff curpid = curthread->td_proc->p_pid; 637170159Sariff 638170159Sariff snd_timestamp(&now); 639170159Sariff 640170159Sariff TAILQ_FOREACH(ce, &c->head, link) { 641170159Sariff /* 642170159Sariff * Sort incrementally according to device type. 643170159Sariff */ 644170159Sariff if (tmask > (ce->unit & c->typemask)) { 645170159Sariff if (cunit == 0) 646170159Sariff after = ce; 647170159Sariff continue; 648170159Sariff } else if (tmask < (ce->unit & c->typemask)) 649170159Sariff break; 650170159Sariff 651170159Sariff /* 652170159Sariff * Shoot.. this is where the grumpiness begin. Just 653170159Sariff * return immediately. 654170159Sariff */ 655170159Sariff if (*unit != -1 && *unit == (ce->unit & ~tmask)) 656170159Sariff goto snd_clone_alloc_out; 657170159Sariff 658170159Sariff cunit++; 659170159Sariff /* 660170159Sariff * Simmilar device type. Sort incrementally according 661170159Sariff * to allocation unit. While here, look for free slot 662170159Sariff * and possible collision for new / future allocation. 663170159Sariff */ 664170159Sariff if (*unit == -1 && (ce->unit & ~tmask) == allocunit) 665170159Sariff allocunit++; 666170159Sariff if ((ce->unit & ~tmask) < allocunit) 667170159Sariff after = ce; 668170159Sariff /* 669170159Sariff * Clone logic: 670170159Sariff * 1. Look for non busy, but keep track of the best 671170159Sariff * possible busy cdev. 672170159Sariff * 2. Look for the best (oldest referenced) entry that is 673170159Sariff * in a same process / thread. 674170159Sariff * 3. Look for the best (oldest referenced), absolute free 675170159Sariff * entry. 676170159Sariff * 4. Lastly, look for the best (oldest referenced) 677170159Sariff * any entries that doesn't fit with anything above. 678170159Sariff */ 679170159Sariff if (ce->flags & SND_CLONE_BUSY) { 680170159Sariff if (ce->devt != NULL && (bce == NULL || 681170159Sariff timespeccmp(&ce->tsp, &bce->tsp, <))) 682170159Sariff bce = ce; 683170159Sariff continue; 684170159Sariff } 685170159Sariff if (ce->pid == curpid && 686170159Sariff (cce == NULL || timespeccmp(&ce->tsp, &cce->tsp, <))) 687170159Sariff cce = ce; 688170159Sariff else if (!(ce->flags & SND_CLONE_INVOKE) && 689170159Sariff (nce == NULL || timespeccmp(&ce->tsp, &nce->tsp, <))) 690170159Sariff nce = ce; 691170159Sariff else if (tce == NULL || timespeccmp(&ce->tsp, &tce->tsp, <)) 692170159Sariff tce = ce; 693170159Sariff } 694170159Sariff if (*unit != -1) 695170159Sariff goto snd_clone_alloc_new; 696170159Sariff else if (cce != NULL) { 697170159Sariff /* Same proc entry found, go for it */ 698170159Sariff ce = cce; 699170159Sariff goto snd_clone_alloc_out; 700170159Sariff } else if (nce != NULL) { 701170159Sariff /* 702170159Sariff * Next, try absolute free entry. If the calculated 703170159Sariff * allocunit is smaller, create new entry instead. 704170159Sariff */ 705170159Sariff if (allocunit < (nce->unit & ~tmask)) 706170159Sariff goto snd_clone_alloc_new; 707170159Sariff ce = nce; 708170159Sariff goto snd_clone_alloc_out; 709170159Sariff } else if (allocunit > c->maxunit) { 710170159Sariff /* 711170159Sariff * Maximum allowable unit reached. Try returning any 712170159Sariff * available cdev and hope for the best. If the lookup is 713170159Sariff * done for things like stat(), mtime() etc. , things should 714170159Sariff * be ok. Otherwise, open() handler should do further checks 715170159Sariff * and decide whether to return correct error code or not. 716170159Sariff */ 717170159Sariff if (tce != NULL) { 718170159Sariff ce = tce; 719170159Sariff goto snd_clone_alloc_out; 720170159Sariff } else if (bce != NULL) { 721170159Sariff ce = bce; 722170159Sariff goto snd_clone_alloc_out; 723170159Sariff } 724170159Sariff return (NULL); 725170159Sariff } 726170159Sariff 727170159Sariffsnd_clone_alloc_new: 728170159Sariff /* 729170159Sariff * No free entries found, and we still haven't reached maximum 730170159Sariff * allowable units. Allocate, setup a minimal unique entry with busy 731170719Sariff * status so nobody will monkey on this new entry. Unit magic is set 732170719Sariff * right here to avoid collision with other contesting handler. 733170719Sariff * The caller must be carefull here to maintain its own 734170719Sariff * synchronization, as long as it will not conflict with malloc(9) 735170719Sariff * operations. 736170719Sariff * 737170719Sariff * That said, go figure. 738170159Sariff */ 739170719Sariff ce = malloc(sizeof(*ce), M_DEVBUF, 740170719Sariff ((c->flags & SND_CLONE_WAITOK) ? M_WAITOK : M_NOWAIT) | M_ZERO); 741170159Sariff if (ce == NULL) { 742170159Sariff if (*unit != -1) 743170159Sariff return (NULL); 744170159Sariff /* 745170159Sariff * We're being dense, ignorance is bliss, 746170159Sariff * Super Regulatory Measure (TM).. TRY AGAIN! 747170159Sariff */ 748170159Sariff if (nce != NULL) { 749170159Sariff ce = nce; 750170159Sariff goto snd_clone_alloc_out; 751170159Sariff } else if (tce != NULL) { 752170159Sariff ce = tce; 753170159Sariff goto snd_clone_alloc_out; 754170159Sariff } else if (bce != NULL) { 755170159Sariff ce = bce; 756170159Sariff goto snd_clone_alloc_out; 757170159Sariff } 758170159Sariff return (NULL); 759170159Sariff } 760170159Sariff /* Setup new entry */ 761170159Sariff ce->parent = c; 762170159Sariff ce->unit = tmask | allocunit; 763170159Sariff ce->pid = curpid; 764170159Sariff ce->tsp = now; 765170159Sariff ce->flags |= SND_CLONE_ALLOC; 766170159Sariff if (after != NULL) { 767170159Sariff TAILQ_INSERT_AFTER(&c->head, after, ce, link); 768170159Sariff } else { 769170159Sariff TAILQ_INSERT_HEAD(&c->head, ce, link); 770170159Sariff } 771170159Sariff c->size++; 772170159Sariff c->tsp = now; 773170159Sariff /* 774170159Sariff * Save new allocation unit for caller which will be used 775170159Sariff * by make_dev(). 776170159Sariff */ 777170159Sariff *unit = allocunit; 778170159Sariff 779170159Sariff return (ce); 780170159Sariff 781170159Sariffsnd_clone_alloc_out: 782170159Sariff /* 783170159Sariff * Set, mark, timestamp the entry if this is a truly free entry. 784170159Sariff * Leave busy entry alone. 785170159Sariff */ 786170159Sariff if (!(ce->flags & SND_CLONE_BUSY)) { 787170159Sariff ce->pid = curpid; 788170159Sariff ce->tsp = now; 789170159Sariff ce->flags |= SND_CLONE_INVOKE; 790170159Sariff } 791170159Sariff c->tsp = now; 792170159Sariff *dev = ce->devt; 793170159Sariff 794170159Sariff return (NULL); 795170159Sariff} 796