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