|
|
1.1 ! root 1: #define _DDI_DKI 1 ! 2: #define _DDI_DKI_IMPL 1 ! 3: #define _SYSV4 1 ! 4: ! 5: /* ! 6: * This file contains functions relating to the implementation of DDI/DKI ! 7: * timeout functions. ! 8: */ ! 9: /* ! 10: *-IMPORTS: ! 11: * <common/ccompat.h> ! 12: * __USE_PROTO__ ! 13: * __ARGS () ! 14: * <kernel/ddi_glob.h> ! 15: * ddi_global_data () ! 16: * <kernel/ddi_cpu.h> ! 17: * ddi_cpu_ref () ! 18: * ddi_cpu_unref () ! 19: * ddi_cpu_id () ! 20: * <kernel/ddi_lock.h> ! 21: * timeout_global_hierarchy ! 22: * timeout_global_priority ! 23: * <kernel/defer.h> ! 24: * defer_int_cpu () ! 25: * defer_int_here () ! 26: * <sys/debug.h> ! 27: * ASSERT () ! 28: * <sys/types.h> ! 29: * plhi ! 30: * pl_t ! 31: * toid_t ! 32: * __tfuncp_t ! 33: * <sys/inline.h> ! 34: * splx () ! 35: * <sys/ksynch.h> ! 36: * LOCK_ALLOC () ! 37: * LOCK_DEALLOC () ! 38: * LOCK () ! 39: * UNLOCK () ! 40: * <sys/kmem.h> ! 41: * KM_NOSLEEP ! 42: * kmem_alloc () ! 43: * kmem_free () ! 44: * <sys/cmn_err.h> ! 45: * CE_PANIC ! 46: * cmn_err () ! 47: */ ! 48: ! 49: #include <common/ccompat.h> ! 50: #include <kernel/ddi_glob.h> ! 51: #include <kernel/ddi_cpu.h> ! 52: #include <kernel/ddi_lock.h> ! 53: #include <kernel/defer.h> ! 54: #include <sys/debug.h> ! 55: #include <sys/types.h> ! 56: #include <sys/inline.h> ! 57: #include <sys/ksynch.h> ! 58: #include <sys/kmem.h> ! 59: #include <sys/cmn_err.h> ! 60: ! 61: /* ! 62: * This code doesn't (yet) use the ddi_global_data () and ddi_cpu_data () ! 63: * mechanisms. It will need to in order to support the dtimeout () call to ! 64: * bind a timeout to a specific CPU. However, this has been left pending ! 65: * investigation of how we want to specify CPU ids... ! 66: * ! 67: * So, at some stage the TIMEOUT_GLOBAL_... (), SCHEDULE_TIMEOUT () and ! 68: * STREAMS_TIMEOUT () functions may be parameterized. ! 69: */ ! 70: ! 71: ! 72: /* ! 73: * All the global data this file uses in one handy place. ! 74: * ! 75: * How to do timeouts? I've always been a big fan of delta-queues. ! 76: * How to manage untimeout ()? ! 77: * ! 78: * The cancellation procedure is seriously complicated by the need for cancel ! 79: * attempts to block until completion if the timeout has begun. What we ! 80: * implement here is similar to a ticket lock, although we use a single global ! 81: * basic lock to guarantee write atomicity rather than a test-and-set lock. ! 82: * t_ticket t_ticket_holder ! 83: * 0 0 Cell pending activation. ! 84: * 1 0 Cell activated. ! 85: * 1 1 Cell processed. ! 86: * m n m != n, cancel attempts n + 1 ! 87: * through m pending. ! 88: * ! 89: * Whoever finds "t_ticket" and "t_ticket_holder" equal (to their number, of ! 90: * course) has responsibility for deallocating the lock structure. The timeout ! 91: * activation code always has responsibility for dequeuing the lock once it has ! 92: * been activated; this avoids having the pending cancels rescan the list to ! 93: * dequeue the cell, and also ensures that untimeout () calls will simply ! 94: * return rather than joining the queue if the timeout function has finished. ! 95: * ! 96: * Using a simple state code might seem simpler... but we have a special ! 97: * problem here, in that we want to return the memory occupied by the event ! 98: * cell (and its state code) to the heap. Using the ticket system, we get ! 99: * acknowledgement from all the contexts that were interested in the state of ! 100: * the cell that they are no longer interested in it. ! 101: */ ! 102: ! 103: struct timeout { ! 104: timeout_t * t_next; ! 105: __clock_t t_delta; ! 106: toid_t t_id; ! 107: processorid_t t_cpu; ! 108: ! 109: atomic_uchar_t t_ticket; /* next ticket */ ! 110: atomic_uchar_t t_ticket_holder; /* who holds the ticket */ ! 111: ! 112: __tfuncp_t t_funcp; /* function to call */ ! 113: _VOID * t_arg; /* argument for function */ ! 114: lock_t * t_lockp; /* basic lock to acquire */ ! 115: pl_t t_pl; /* priority level for call */ ! 116: }; ! 117: ! 118: __LOCAL__ lkinfo_t _timeout_lkinfo = { ! 119: "timeout global lock", INTERNAL_LOCK ! 120: }; ! 121: ! 122: ! 123: #define TIMEDATA() (& ddi_global_data ()->dg_timeouts) ! 124: ! 125: #define TIMEOUT_GLOBAL_LOCK() \ ! 126: (ASSERT (TIMEDATA ()->td_lock != NULL), \ ! 127: LOCK (TIMEDATA ()->td_lock, timeout_global_priority)) ! 128: ! 129: #define TIMEOUT_GLOBAL_UNLOCK(p) UNLOCK (TIMEDATA ()->td_lock, (p)) ! 130: ! 131: ! 132: ! 133: /* ! 134: * This internal function is called whenever there are timeout routines ! 135: * waiting to be processed by a particular CPU. ! 136: */ ! 137: ! 138: #if __USE_PROTO__ ! 139: __LOCAL__ void (RUN_TIMEOUTS) (void) ! 140: #else ! 141: __LOCAL__ void ! 142: RUN_TIMEOUTS __ARGS (()) ! 143: #endif ! 144: { ! 145: pl_t prev_pl; ! 146: timeout_t * scan; ! 147: timeout_t * next; ! 148: timeout_t * free_list; ! 149: processorid_t my_id = ddi_cpu_id (); ! 150: int lock_failed = 0; ! 151: ! 152: prev_pl = TIMEOUT_GLOBAL_LOCK (); ! 153: ! 154: ! 155: /* ! 156: * Run all the events that belong to this CPU. ! 157: */ ! 158: ! 159: for (scan = TIMEDATA ()->td_run ; scan != TIMEDATA ()->td_first ; ! 160: scan = scan->t_next) { ! 161: ! 162: if (scan->t_cpu != my_id) ! 163: continue; ! 164: ! 165: if (ATOMIC_FETCH_UCHAR (scan->t_ticket) > 0) { ! 166: /* ! 167: * This entry is going to be deleted. Don't run it. ! 168: */ ! 169: ! 170: continue; ! 171: } ! 172: ! 173: ! 174: /* ! 175: * Before we commit to running the timeout function, we need ! 176: * to check that the timeout function can acquire the basic ! 177: * lock that it needs. ! 178: */ ! 179: ! 180: if (scan->t_lockp != NULL && ! 181: TRYLOCK (scan->t_lockp, plhi) == invpl) { ! 182: /* ! 183: * Try the next event cell, after we remember that an ! 184: * event cell failed to get a lock. ! 185: */ ! 186: ! 187: lock_failed = 1; ! 188: continue; ! 189: } ! 190: ! 191: ! 192: /* ! 193: * OK, stop other CPUs from processing this entry and make ! 194: * cancel requests block until we are finished. ! 195: */ ! 196: ! 197: ATOMIC_STORE_UCHAR (scan->t_ticket, 1); ! 198: ! 199: TIMEOUT_GLOBAL_UNLOCK (scan->t_pl); ! 200: ! 201: ! 202: /* ! 203: * Now execute the user function as requested. Note that we ! 204: * set the interrupt priority level to "scan->t_pl" even ! 205: * though we passed that value to UNLOCK (), since an ! 206: * implementation is permitted to ignore the "pl" argument to ! 207: * UNLOCK (). ! 208: * ! 209: * If we acquired a lock on behalf of the timeout function, we ! 210: * release it after the function runs. ! 211: */ ! 212: ! 213: (void) splx (scan->t_pl); ! 214: ! 215: (* scan->t_funcp) (scan->t_arg); ! 216: ! 217: if (scan->t_lockp != NULL) ! 218: UNLOCK (scan->t_lockp, prev_pl); ! 219: ! 220: (void) splx (prev_pl); ! 221: ! 222: ! 223: /* ! 224: * Now we relock the list and mark this cell as finished. If ! 225: * a delete request for the cell has come in, let it proceed ! 226: * now that the function has been run. ! 227: */ ! 228: ! 229: prev_pl = TIMEOUT_GLOBAL_LOCK (); ! 230: ! 231: ATOMIC_STORE_UCHAR (scan->t_ticket_holder, 1); ! 232: } ! 233: ! 234: ! 235: /* ! 236: * Run over the list of expired events and move any that have been ! 237: * completed to a work list for deletion. ! 238: */ ! 239: ! 240: free_list = NULL; ! 241: ! 242: for (scan = TIMEDATA ()->td_run ; scan != TIMEDATA ()->td_first ; ! 243: scan = next) { ! 244: ! 245: /* ! 246: * If this event cell is in the throes of deletion, try again ! 247: * later. If there is an event which has not yet been run, ! 248: * don't clean up any more. ! 249: */ ! 250: ! 251: if (ATOMIC_FETCH_UCHAR (scan->t_ticket_holder) != ! 252: ATOMIC_FETCH_UCHAR (scan->t_ticket)) { ! 253: ! 254: lock_failed = 1; ! 255: break; ! 256: } ! 257: ! 258: if (ATOMIC_FETCH_UCHAR (scan->t_ticket) == 0) ! 259: break; ! 260: ! 261: /* ! 262: * Move the cell to the work list. ! 263: */ ! 264: ! 265: next = scan->t_next; ! 266: scan->t_next = free_list; ! 267: free_list = scan; ! 268: } ! 269: ! 270: TIMEDATA ()->td_run = scan; ! 271: ! 272: ! 273: /* ! 274: * If we have run (and freed) all the events that we can, clear the ! 275: * run flag, otherwise defer this function again by way of backing off ! 276: * from the locks we failed. ! 277: */ ! 278: ! 279: if (lock_failed) ! 280: defer_int_here (RUN_TIMEOUTS); ! 281: else ! 282: ATOMIC_STORE_UCHAR (ddi_cpu_data ()->dc_run_timeouts, 0); ! 283: ! 284: TIMEOUT_GLOBAL_UNLOCK (prev_pl); ! 285: ! 286: ! 287: /* ! 288: * Free any timeout cells that we were able to reap. ! 289: */ ! 290: ! 291: while ((scan = free_list) != NULL) { ! 292: ! 293: free_list = scan->t_next; ! 294: ! 295: kmem_free (scan, sizeof (* scan)); ! 296: } ! 297: } ! 298: ! 299: ! 300: /* ! 301: * Code from STREAMS_TIMEOUT () to schedule a single timeout event, factored ! 302: * out here to keep STREAMS_TIMEOUT () manageable. ! 303: */ ! 304: ! 305: #if __USE_PROTO__ ! 306: __LOCAL__ __INLINE__ void SCHEDULE_TIMEOUT (timeout_t * timep) ! 307: #else ! 308: __LOCAL__ __INLINE__ void ! 309: SCHEDULE_TIMEOUT __ARGS ((timep)) ! 310: timeout_t * timep; ! 311: #endif ! 312: { ! 313: dcdata_t * dcdatap; ! 314: ! 315: if (timep->t_cpu == NOCPU) ! 316: timep->t_cpu = ddi_cpu_id (); ! 317: ! 318: dcdatap = ddi_cpu_ref (timep->t_cpu); ! 319: ! 320: ASSERT (dcdatap != NULL); ! 321: ! 322: if (ATOMIC_FETCH_UCHAR (dcdatap->dc_run_timeouts) == 0) { ! 323: /* ! 324: * Schedule the routine to actually run the timeouts. ! 325: */ ! 326: ! 327: defer_int_cpu (RUN_TIMEOUTS, timep->t_cpu); ! 328: ATOMIC_STORE_UCHAR (dcdatap->dc_run_timeouts, 1); ! 329: } ! 330: } ! 331: ! 332: ! 333: /* ! 334: * This internal function is called once for each clock tick of real time that ! 335: * passes in the system overall. It may be that several instances get to run ! 336: * simultaneously in different CPUs if things get bogged down, but the basic ! 337: * idea is that the function gets called once per clock tick. ! 338: */ ! 339: ! 340: __EXTERN_C__ ! 341: #if __USE_PROTO__ ! 342: void (STREAMS_TIMEOUT) (void) ! 343: #else ! 344: void ! 345: STREAMS_TIMEOUT __ARGS (()) ! 346: #endif ! 347: { ! 348: pl_t prev_pl; ! 349: timeout_t * scan; ! 350: ! 351: prev_pl = TIMEOUT_GLOBAL_LOCK (); ! 352: ! 353: /* ! 354: * Now that we have the timeout queue locked, decrement the delta ! 355: * value of the entry at the front of the queue. After that, work out ! 356: * whether any entries have been triggered as a result. ! 357: */ ! 358: ! 359: if ((scan = TIMEDATA ()->td_first) != NULL) { ! 360: ! 361: ASSERT (scan->t_delta > 0); ! 362: ! 363: if (-- scan->t_delta == 0) { ! 364: ! 365: if (TIMEDATA ()->td_run == NULL) ! 366: TIMEDATA ()->td_run = scan; ! 367: ! 368: /* ! 369: * At least one entry has been set off. Loop over the ! 370: * entries, notifying each CPU that has an event bound ! 371: * to it. If an event is not bound to any CPU, bind it ! 372: * to the current CPU. ! 373: */ ! 374: ! 375: do ! 376: SCHEDULE_TIMEOUT (scan); ! 377: while ((scan = scan->t_next) != NULL && ! 378: scan->t_delta == 0); ! 379: ! 380: TIMEDATA ()->td_first = scan; ! 381: } ! 382: } ! 383: ! 384: TIMEOUT_GLOBAL_UNLOCK (prev_pl); ! 385: } ! 386: ! 387: ! 388: /* ! 389: * This internal function factors out the common elements of timer event ! 390: * scheduling from itimeout () and ltimeout (). ! 391: */ ! 392: ! 393: #if __USE_PROTO__ ! 394: __LOCAL__ toid_t (QUEUE_TIMEOUT) (__tfuncp_t fn, _VOID * arg, __clock_t ticks, ! 395: lock_t * lockp, processorid_t cpu, pl_t pl) ! 396: #else ! 397: __LOCAL__ toid_t ! 398: QUEUE_TIMEOUT __ARGS ((fn, arg, ticks, lockp, cpu, pl)) ! 399: __tfuncp_t fn; ! 400: _VOID * arg; ! 401: __clock_t ticks; ! 402: lock_t * lockp; ! 403: processorid_t cpu; ! 404: pl_t pl; ! 405: #endif ! 406: { ! 407: pl_t prev_pl; ! 408: timeout_t * timep; ! 409: timeout_t * scan; ! 410: timeout_t * prev; ! 411: ! 412: if ((timep = (timeout_t *) kmem_alloc (sizeof (* timep), ! 413: KM_NOSLEEP)) == NULL) ! 414: return 0; ! 415: ! 416: timep->t_funcp = fn; ! 417: timep->t_arg = arg; ! 418: timep->t_pl = pl; ! 419: timep->t_lockp = lockp; ! 420: timep->t_cpu = cpu; ! 421: ATOMIC_STORE_UCHAR (timep->t_ticket, 0); ! 422: ATOMIC_STORE_UCHAR (timep->t_ticket_holder, 0); ! 423: ! 424: if (ticks == 0) ! 425: ticks = 1; ! 426: ! 427: /* ! 428: * Having created and mostly filled in the timeout structure, we now ! 429: * lock the delta-queue and try and find the place where our structure ! 430: * needs to be inserted. ! 431: * ! 432: * We also use the delta-queue lock to protect our access to the ! 433: * timeout ID generator. Since it is in theory possible to wrap around ! 434: * the ID space, the delta-queue walk will look for a duplicate ID. ! 435: * We make the simplifying assumption that anything that has lived ! 436: * that long isn't going to be cancelled anytime soon, so that it ! 437: * suffices to protect the newer entry from having it's cancel code ! 438: * shadowed. ! 439: */ ! 440: ! 441: prev_pl = TIMEOUT_GLOBAL_LOCK (); ! 442: ! 443: id_scan: ! 444: if (TIMEDATA ()->td_id == 0) ! 445: TIMEDATA ()->td_id = 1; ! 446: ! 447: timep->t_id = TIMEDATA ()->td_id ++; ! 448: ! 449: /* ! 450: * We have to deal with the fact that the true first element in the ! 451: * timeout delta-queue may be represented by either of two pointers. ! 452: * Since timeouts that have been scheduled to run have a zero ! 453: * "t_delta", searching those entries won't disrupt the delta-queue, ! 454: * and until they have been dequeued their timeout id's are still ! 455: * valid. ! 456: */ ! 457: ! 458: if ((scan = TIMEDATA ()->td_run) == NULL) ! 459: scan = TIMEDATA ()->td_first; ! 460: ! 461: for (prev = NULL ; scan != NULL ; scan = (prev = scan)->t_next) { ! 462: ! 463: if (scan->t_id == timep->t_id) ! 464: goto id_scan; ! 465: ! 466: if (ticks < scan->t_delta) { ! 467: /* ! 468: * OK, we have found our spot. Now we insert our new ! 469: * cell in front of the one we have just found. ! 470: */ ! 471: ! 472: scan->t_delta -= ticks; ! 473: break; ! 474: } ! 475: ! 476: ticks -= scan->t_delta; ! 477: } ! 478: ! 479: timep->t_delta = ticks; ! 480: timep->t_next = scan; ! 481: ! 482: if (scan == TIMEDATA ()->td_first) ! 483: TIMEDATA ()->td_first = timep; ! 484: ! 485: if (prev != NULL) ! 486: prev->t_next = timep; ! 487: ! 488: TIMEOUT_GLOBAL_UNLOCK (prev_pl); ! 489: ! 490: return timep->t_id; ! 491: } ! 492: ! 493: ! 494: /* ! 495: * This internal function is a direct equivalent to itimeout (), with the ! 496: * following additional behaviour; the caller supplies the address of a basic ! 497: * lock which this code will attempt to acquire before running the timeout ! 498: * function. If the lock cannot be acquired immediately, the next timeout ! 499: * function will be considered. ! 500: * ! 501: * This behaviour allows untimeout () to be safely used to cancel the timeout ! 502: * request while holding the basic lock that this function will attempt to ! 503: * acquire. ! 504: * ! 505: * A "timeout" value of NULL yields identical behaviour to itimeout (). ! 506: */ ! 507: ! 508: #if __USE_PROTO__ ! 509: toid_t (ltimeout) (__tfuncp_t fn, _VOID * arg, __clock_t ticks, ! 510: lock_t * lockp, pl_t pl) ! 511: #else ! 512: toid_t ! 513: ltimeout __ARGS ((fn, arg, ticks, lockp, pl)) ! 514: __tfuncp_t fn; ! 515: _VOID * arg; ! 516: __clock_t ticks; ! 517: lock_t * lockp; ! 518: pl_t pl; ! 519: #endif ! 520: { ! 521: ASSERT (fn != (__tfuncp_t) NULL); ! 522: ASSERT (lockp != NULL); ! 523: ! 524: return QUEUE_TIMEOUT (fn, arg, ticks, lockp, NOCPU, pl); ! 525: } ! 526: ! 527: ! 528: /* ! 529: *-STATUS: ! 530: * DDI/DKI ! 531: * ! 532: *-NAME: ! 533: * itimeout Execute a function after a specified length of time. ! 534: * ! 535: *-SYNOPSIS: ! 536: * #include <sys/types.h> ! 537: * ! 538: * toid_t itimeout (void (* fn) (), void * arg, long ticks, pl_t pl); ! 539: * ! 540: *-ARGUMENTS: ! 541: * fn Function to execute when the time interval expires. ! 542: * ! 543: * arg Argument to the function. ! 544: * ! 545: * ticks Number of clock ticks to wait before the function is ! 546: * called. ! 547: * ! 548: * pl The interrupt priority level at which the function ! 549: * will be called. "pl" must specify a priority level ! 550: * greater than or equal to "pltimeout"; thus, "plbase" ! 551: * cannot not be used. See LOCK_ALLOC () for a list of ! 552: * values for "pl". ! 553: * ! 554: *-DESCRIPTION: ! 555: * itimeout () causes the function specified by "fn" to be called after ! 556: * the time interval specified by "ticks", at the interrupt priority ! 557: * specified by "pl". "arg" will be passed as the only argument to ! 558: * function "fn". The itimeout () call returns immediately without ! 559: * waiting for the specified function to execute. ! 560: * ! 561: * The length of time before the function is called is not guaranteed to ! 562: * be exactly equal to the requested time, but will be at least "ticks-1" ! 563: * clock ticks in length. The function specified by "fn" must neither ! 564: * sleep nor reference process context. ! 565: * ! 566: *-RETURN VALUE: ! 567: * If the function specfied by "fn" is successfully scheduled, ! 568: * itimeout () returns a non-zero identifier that can be passed to ! 569: * untimeout () to cancel the request. If the function could not be ! 570: * scheduled, itimeout () returns a value of 0. ! 571: * ! 572: *-LEVEL: ! 573: * Base or Interrupt. ! 574: * ! 575: *-NOTES: ! 576: * Does not sleep. ! 577: * ! 578: * Driver-defined basic locks, read/write locks, and sleep locks may be ! 579: * held across calls to this function. ! 580: * ! 581: * Drivers should be careful to cancel any pending itimeout () functions ! 582: * that access data structures before these structures are de-initialized ! 583: * or deallocated. ! 584: * ! 585: * After the time interval has expired, "fn" only runs if the processor ! 586: * is at base level. Otherwise, "fn" is deferred until sometime in the ! 587: * near future. ! 588: * ! 589: * If itimeout () is called holding a lock that is contended for by "fn", ! 590: * the caller must hold the lock at a processor level greater than the ! 591: * base processor level. ! 592: * ! 593: * A "ticks" argument of 0 has the same effect as a "ticks" argument of ! 594: * 1. Both will result in an approximate wait of between 0 and 1 tick ! 595: * (possibly longer). ! 596: * ! 597: *-SEE ALSO: ! 598: * LOCK_ALLOC (), untimeout () ! 599: */ ! 600: ! 601: #if __USE_PROTO__ ! 602: toid_t (itimeout) (__tfuncp_t fn, _VOID * arg, __clock_t ticks, pl_t pl) ! 603: #else ! 604: toid_t ! 605: itimeout __ARGS ((fn, arg, ticks, pl)) ! 606: __tfuncp_t * fn; ! 607: _VOID * arg; ! 608: __clock_t ticks; ! 609: pl_t pl; ! 610: #endif ! 611: { ! 612: ASSERT (fn != (__tfuncp_t) NULL); ! 613: ! 614: return QUEUE_TIMEOUT (fn, arg, ticks, NULL, NOCPU, pl); ! 615: } ! 616: ! 617: ! 618: /* ! 619: *-STATUS: ! 620: * DDI/DKI ! 621: * ! 622: *-NAME: ! 623: * untimeout Cancel previous timeout request. ! 624: * ! 625: *-SYNOPSIS: ! 626: * #include <sys/types.h> ! 627: * ! 628: * void untimeout (toid_t id); ! 629: * ! 630: *-ARGUMENTS: ! 631: * id Identifier returned from a previous call to ! 632: * itimeout (). ! 633: * ! 634: *-DESCRIPTION: ! 635: * untimeout () cancels a previous timeout request. If the untimeout () ! 636: * is called while the function is running, then untimeout () will not ! 637: * return until the function has completed. The function that runs as a ! 638: * result of itimeout () cannot use untimeout () to cancel itself. ! 639: * ! 640: *-RETURN VALUE: ! 641: * None. ! 642: * ! 643: *-LEVEL: ! 644: * Base or interrupt, with the following exception; the untimeout () can ! 645: * only be performed from interrupt levels less than, or equal to, the ! 646: * level specified when the function was scheduled. ! 647: * ! 648: *-NOTES: ! 649: * Does not sleep. ! 650: * ! 651: * Driver-defined basic locks, read/write locks, and sleep locks may not ! 652: * be held across calls to this function if these locks are contended by ! 653: * the function being scheduled. ! 654: * ! 655: *-SEE ALSO: ! 656: * delay (), itimeout (), unbufcall () ! 657: */ ! 658: ! 659: #if __USE_PROTO__ ! 660: void (untimeout) (toid_t id) ! 661: #else ! 662: void ! 663: untimeout __ARGS ((id)) ! 664: toid_t id; ! 665: #endif ! 666: { ! 667: pl_t prev_pl; ! 668: timeout_t * scan; ! 669: ! 670: prev_pl = TIMEOUT_GLOBAL_LOCK (); ! 671: ! 672: for (scan = TIMEDATA ()->td_first ; scan != NULL ; ! 673: scan = scan->t_next) { ! 674: unsigned short ticket; ! 675: ! 676: if (scan->t_id != id) ! 677: continue; ! 678: ! 679: /* ! 680: * Take a ticket, and wait for the cell's event routine to ! 681: * finish. If it was not running before we took a ticket, it ! 682: * never will. ! 683: */ ! 684: ! 685: ticket = ATOMIC_FETCH_UCHAR (scan->t_ticket); ! 686: ATOMIC_STORE_UCHAR (scan->t_ticket, ticket + 1); ! 687: ! 688: scan->t_cpu = ddi_cpu_id (); ! 689: ! 690: if (ticket != ATOMIC_FETCH_UCHAR (scan->t_ticket_holder)) { ! 691: /* ! 692: * Wait for the event routine to complete. ! 693: */ ! 694: ! 695: TIMEOUT_GLOBAL_UNLOCK (prev_pl); ! 696: ! 697: while (ATOMIC_FETCH_UCHAR (scan->t_ticket_holder) ! 698: != ticket) ! 699: ; /* DO NOTHING */ ! 700: ! 701: ! 702: prev_pl = TIMEOUT_GLOBAL_LOCK (); ! 703: } ! 704: ! 705: ! 706: /* ! 707: * Pass it on to the next guy. ! 708: */ ! 709: ! 710: ATOMIC_STORE_UCHAR (scan->t_ticket_holder, ticket + 1); ! 711: } ! 712: ! 713: TIMEOUT_GLOBAL_UNLOCK (prev_pl); ! 714: } ! 715: ! 716: ! 717: /* ! 718: * Set up the timeout globals. This function is called via the 'mdevice' init ! 719: * function table. ! 720: */ ! 721: ! 722: __EXTERN_C__ ! 723: #if __USE_PROTO__ ! 724: int (timeout_init) (void) ! 725: #else ! 726: int ! 727: timeout_init __ARGS (()) ! 728: #endif ! 729: { ! 730: TIMEDATA ()->td_lock = LOCK_ALLOC (timeout_global_hierarchy, ! 731: timeout_global_priority, ! 732: & _timeout_lkinfo, KM_SLEEP); ! 733: ! 734: return TIMEDATA ()->td_lock == NULL; ! 735: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.