|
|
1.1 ! root 1: #define _DDI_DKI 1 ! 2: #define _SYSV4 1 ! 3: ! 4: ! 5: /* ! 6: * System V DDI/DKI compatible synchronisation functions ! 7: * ! 8: * This implements the synchronisation functions introduced in the System V ! 9: * DDI/DKI multiprocessor edition for a simple uniprocessor. The locking ! 10: * implementations given here are totally unsuitable for multiprocessor use. ! 11: * ! 12: * Some good multiprocessor lock algorithms can be found in: ! 13: * "Synchronisation Without Contention" ! 14: * John M. Mellor-Crummey & Michael L. Scott, ! 15: * Proceedings 4th International Conference on Architectural Support for ! 16: * Programming Languages and Operating Systems (ASPLOS 4) ! 17: * 1991, ACM ! 18: */ ! 19: ! 20: /* ! 21: *-IMPORTS: ! 22: * <common/ccompat.h> ! 23: * __CONST__ ! 24: * __USE_PROTO__ ! 25: * __ARGS () ! 26: * <common/xdebug.h> ! 27: * __LOCAL__ ! 28: * <kernel/x86lock.h> ! 29: * atomic_uchar_t ! 30: * atomic_ushort_t ! 31: * __atomic_uchar_t ! 32: * ATOMIC_TEST_AND_SET_UCHAR () ! 33: * ATOMIC_FETCH_AND_STORE_USHORT (); ! 34: * ATOMIC_FETCH_UCHAR (); ! 35: * ATOMIC_FETCH_USHORT (); ! 36: * ATOMIC_CLEAR_UCHAR (); ! 37: * ATOMIC_CLEAR_USHORT (); ! 38: * <kernel/ddi_cpu.h> ! 39: * ASSERT_BASE_LEVEL (); ! 40: * ddi_cpu_data () ! 41: * <kernel/v_proc.h> ! 42: * plist_t ! 43: * PROCESS_WOKEN ! 44: * PROCESS_SIGNALLED ! 45: * MAKE_SLEEPING () ! 46: * WAKE_ONE () ! 47: * WAKE_ALL () ! 48: * PROC_HANDLE () ! 49: * <sys/debug.h> ! 50: * ASSERT () ! 51: * <sys/types.h> ! 52: * pl_t ! 53: * uchar_t ! 54: * uint_t ! 55: * <sys/inline.h> ! 56: * splhi () ! 57: * splx () ! 58: * splcmp () ! 59: * <sys/cmn_err.h> ! 60: * cmn_err () ! 61: * <sys/kmem.h> ! 62: * KM_SLEEP ! 63: * KM_NOSLEEP ! 64: */ ! 65: ! 66: #include <common/ccompat.h> ! 67: #include <kernel/ddi_cpu.h> ! 68: #include <kernel/ddi_lock.h> ! 69: #include <kernel/v_proc.h> ! 70: #include <sys/debug.h> ! 71: #include <sys/types.h> ! 72: #include <sys/inline.h> ! 73: #include <sys/cmn_err.h> ! 74: #include <sys/kmem.h> ! 75: ! 76: #include <sys/ksynch.h> ! 77: ! 78: ! 79: /* ! 80: * The actual content definitions of the following data structures has been ! 81: * kept totally private to this file in order to reduce the temptation of ! 82: * allocating or accessing the definitions in other subsystems. ! 83: * ! 84: * Note that for efficiency considerations it might eventually become ! 85: * necessary to export these definitions elsewhere to allow locks to be ! 86: * statically defined (to break dependencies) or incorporated directly as ! 87: * members of other data structures (to reduce allocation overheads). ! 88: * (Of course, that means we have to define ...._INIT () routines). ! 89: * ! 90: * For now, we'll try very very hard to resist the temptation to optimize ! 91: * too early and violate our encapsulation. ! 92: */ ! 93: ! 94: /* ! 95: * Configuration issue: DDI/DKI sleep locks don't really map well onto any ! 96: * old-style Unix kernel functionality, nor does the "wake one" feature of ! 97: * DDI/DKI synchronization variables. Sleep locks and synchronization ! 98: * variables need to interface intimately with the kernel scheduling services ! 99: * (in ways that were usually not anticipated by the original authors of those ! 100: * services). This requires us to confront some key aspects of the difference ! 101: * between the old and new styles to discover why we really *need* to ! 102: * introduce a different way of doing things. ! 103: * ! 104: * Some features of the old sleep ()/wakeup () model: ! 105: * a) These functions correspond exactly to the SV_WAIT[_SIG] () and ! 106: * SV_BROADCAST () functions in the DDI/DKI, except that they do not ! 107: * incorporate the basic-lock functionality of SV_WAIT[_SIG] (). ! 108: * ! 109: * b) Whether sleep was interruptible or not was related to the ! 110: * "priority" indication passed to SV_WAIT[_SIG] () rather than being ! 111: * orthogonal issue. This need not be vital, except that the ! 112: * "priority" for sleep ()/wakeup () calls was expressed in magic, ! 113: * highly implementation-dependent numbers. [*] ! 114: * ! 115: * c) On many systems, if sleep () was interrupted by a signal the result ! 116: * was that the sleep was aborted by a non-local return. This is not ! 117: * necessarily a problem *if* the caller was allowed to form a chain ! 118: * of handlers to provide unwind-protection facilities. Sadly, this ! 119: * is usually not the case. ! 120: * ! 121: * d) The ability to awaken only *one* process at a time was not ! 122: * considered an issue; firstly, because contention was normally only ! 123: * associated with actions involving long delay, such as I/O, and ! 124: * because processes could only be run sequentially anyway. In other ! 125: * words, even if contention happened, the fact that after a wakeup () ! 126: * processes were run one at a time meant that often by the time a ! 127: * process was run after a wakeup () the lock had been acquired *and* ! 128: * released by any process with higher priority. ! 129: * ! 130: * [*] For instance, I have no idea what the three (!) different numbers that ! 131: * have to be passed to a sleep () call in Coherent really do. There is no ! 132: * internal documentation in the kernel source that clearly explicates the ! 133: * roles that each number plays, nor what effect it has on the overall picture ! 134: * of process scheduling. ! 135: * ! 136: * Each of features requires modification to the sleep ()/wakeup () model, as ! 137: * outlined below: ! 138: * a) In a uniprocessor kernel, sleep ()/wakeup () could function without ! 139: * basic locks since the caller could gain equivalent protection by ! 140: * simply manipulating the interrupt priority level, which would be ! 141: * reset during the course of the dispatch process after the caller ! 142: * had been safely put to sleep. In a multiprocessor, an unlock call ! 143: * occuring *during* a call to sleep () might occur before the ! 144: * process status had been changed, thus causing the caller to never ! 145: * see the unlock... ! 146: * ! 147: * This suggests the following basic model for sleep locks: ! 148: * ! 149: * for (;;) { ! 150: * acquire_basic_lock (); ! 151: * if (resource_available ()) ! 152: * break; ! 153: * if (MAKE_SLEEPING () == PROCESS_SLEPT) { ! 154: * release_basic_lock (); ! 155: * RUN_NEXT (); // does not return ! 156: * } ! 157: * } ! 158: * ! 159: * where MAKE_SLEEPING () and RUN_NEXT () together form the same kind ! 160: * of actions as sleep (), but broken into a pair that makes the ! 161: * setjmp ()-style behaviour of rescheduling apparent and useful. In ! 162: * particular, this keeps the desirable property that the low-level ! 163: * scheduling system leaves the locking system in the hands of the ! 164: * client code. ! 165: * ! 166: * Of course, there are various ways that this can be achieved. The ! 167: * preferred style would be for MAKE_SLEEPING () to (optionally) test ! 168: * for signals and to return some discriminating value. The values ! 169: * returned from MAKE_SLEEPING () could be ! 170: * ! 171: * PROCESS_SLEPT slept ! 172: * PROCESS_WOKEN woken normally ! 173: * PROCESS_SIGNALLED woken by signal ! 174: * ! 175: * but for practical reasons this is difficult. While it is desirable ! 176: * to expose at least part of the context-saving behaviour of the ! 177: * inner scheduling layer, most of these facilities have the same ! 178: * limitations as the setjmp () facility in that if the context which ! 179: * directly called the context-save routine exits, all bets are off. ! 180: * See (c) below for further discussion, and the final form will be ! 181: * introduced at the end. ! 182: * ! 183: * b) This can be generally dealt with as-is by introducing appropriate ! 184: * magic to map the abstract priorities into numbers for passing to ! 185: * MAKE_SLEEPING (). ! 186: * ! 187: * c) The old style of signal handling is forbidden in the DDI/DKI ! 188: * envinronment due to the locking style in use. In fact, the only ! 189: * way that a driver can detect that a signal has been sent to a ! 190: * process is by performing SV_WAIT_SIG () or SLEEP_LOCK_SIG () and ! 191: * testing the return value. ! 192: * ! 193: * Moreover, signalling introduces one deficiency in the model ! 194: * presented in a) above, namely that if a process is signalled, the ! 195: * wakeup done in the signalling process knows nothing about the ! 196: * locking system being used above (and in fact, if we layer on top of ! 197: * old sleep ()/wakeup () code, our entire queueing system is ignored, ! 198: * which would be easily fixable but for that locking problem). ! 199: * ! 200: * The simple solution would be to introduce an extra lock on part of ! 201: * the process table than can ameliorate some of the problems, but ! 202: * too many basic locks leads to hierarchy/ordering problems, and is ! 203: * especially wasteful given that approriate row-level locking on ! 204: * list headers should be completely sufficient without having to ! 205: * introduce node-level locks. ! 206: * ! 207: * Essentially, we define the basic lock as being part of the list ! 208: * head that sleeping processes are queued on and put a back-pointer ! 209: * to the list head in the process table so that signal wakeup. This ! 210: * complicates the generic sleep-lock and synchronization variable ! 211: * functions a little, but we should be able to impose the semantics ! 212: * we want (namely being able to guarantee deterministic ordering ! 213: * on locks, which requires that the generic functions be accurately ! 214: * able to determine the status of processes threaded on the sleep ! 215: * queues). ! 216: * ! 217: * This definition of responsibilty for the queue head w.r.t. locking ! 218: * behaviour allows us to fold the release_basic_lock () and ! 219: * RUN_NEXT () behaviour into MAKE_SLEEPING (), which also helps to ! 220: * solve the problem of dealing with the scope of context-save ! 221: * introduced in (a). Note that we have to be careful about using the ! 222: * process-table pointer to the list head since in order to find the ! 223: * list head to lock it we have to read this information which is ! 224: * potentially being modified as we access it. ! 225: * ! 226: * [ Note that it is possible to write a macro for MAKE_SLEEPING () ! 227: * that does the context save in the caller's scope, but since it is ! 228: * possible for a routine to *not* sleep at all (due to a pending ! 229: * signal), that would mean it had to take the form: ! 230: * FINALIZE_SLEEP (SAVE_CONTEXT (BEGIN_SLEEP (...)), ...) ! 231: * in order to avoid introducing auxiliaries. ] ! 232: * ! 233: * d) The assumptions on which the old-style behaviour is based are no ! 234: * longer valid on multiprocessing systems. Firstly, the more con- ! 235: * current nature of multiprocessor systems increases the likelihood ! 236: * of contention for locks. Secondly, broadcast wakeup is likely to ! 237: * cause several of the woken processes to be run simultaneously on ! 238: * different processors, resulting in timelines like this (measured ! 239: * from a broadcast wakeup of processes waiting for a resource): ! 240: * ! 241: * CPU A : |-<acquire----------release>............... ! 242: * CPU n : .|--*|--*|--*|--*...........|--<acquire --- ! 243: * ! 244: * where '|' indicates selection of a process, '-' indicates CPU time ! 245: * consumed, and '*' indicates a process sleeping due to resource ! 246: * unavailability, and '.' represents CPU time spent on unrelated ! 247: * activity. Clearly, broadcast wakeup is not an optimal way of ! 248: * implementing one-at-a-time lock functionality on a multiprocessor. ! 249: * ! 250: * Of course, implementing one-at-a-time wakeup facilities is not a ! 251: * trivial matter. While awakening a single waiting process is fairly ! 252: * simple, the matter of selecting *which* process to awaken is a ! 253: * complex one that can have considerable impact on other kernel ! 254: * systems such as the virtual memory system. In addition, there is ! 255: * extra information available in the DDI/DKI system whose relation ! 256: * to scheduling has not been studied (such as the number of locks ! 257: * held by a context). Clearly, the internal layering of the DDI/DKI ! 258: * implementation should be designed to make it easy to export this ! 259: * information to the low-level kernel scheduling system. ! 260: * ! 261: * Of course, the worst case is that normal kernel scheduling policy ! 262: * is completely supplanted in the DDI/DKI case. There looks to be ! 263: * little alternative for the multiprocessor case, but it seems to ! 264: * be a good idea to design the DDI/DKI implementation to support the ! 265: * old-style broadcast functionality as a fallback. ! 266: */ ! 267: ! 268: ! 269: /* ! 270: * Common inital part of a lock that is used to queue locks and hold the ! 271: * statistics information. We use this definition to simplify the process of ! 272: * keeping lists of all the allocated locks and to permit simple generic ! 273: * operations on such lists. ! 274: * ! 275: * Downcast operators should be provided for the lock structures so that the ! 276: * mapping from the node to the lock can be done in a fully portable fashion. ! 277: */ ! 278: ! 279: typedef struct lock_node lnode_t; ! 280: ! 281: struct lock_node { ! 282: lnode_t * ln_next; /* pointer to successor */ ! 283: lnode_t * ln_prev; /* pointer to predecessor */ ! 284: lkinfo_t * ln_lkinfo; /* statistics information */ ! 285: }; ! 286: ! 287: ! 288: /* ! 289: * Internal structure of a basic lock ! 290: */ ! 291: ! 292: struct __basic_lock { ! 293: lnode_t bl_node; /* generic information */ ! 294: ! 295: #ifdef __TICKET_LOCK__ ! 296: atomic_ushort_t bl_next_ticket; /* next ticket number to be granted */ ! 297: atomic_ushort_t bl_lock_holder; /* ticket number of lock holder */ ! 298: #endif ! 299: ! 300: atomic_uchar_t bl_locked; /* is the basic lock acquired? */ ! 301: ! 302: pl_t bl_min_pl; /* minimum pl to be used in LOCK () */ ! 303: ! 304: uchar_t bl_hierarchy; /* acquisition order constraint */ ! 305: }; ! 306: ! 307: ! 308: /* ! 309: * Internal structure of a read/write lock. ! 310: * ! 311: * Due to the shared nature of read locks, they are naturally expressed by ! 312: * a count of outstanding readers maintained by atomic increment and ! 313: * decrement operations. The exclusive (write) mode of such locks are best ! 314: * expressed via the "ticket" mechanism if FIFO ordering is desired and/or ! 315: * contention is to be minimized. ! 316: * ! 317: * The following implementation consists of a basic (exclusive) lock simply ! 318: * augmented by a count; the use of a "ticket gate" test-and-set lock to ! 319: * control access to the lock internal data reduces dependency on atomic ! 320: * facilities such as fetch_and_increment and compare_and_swap that are not ! 321: * widely available. If such facilities are available, consider using the ! 322: * lock algorithm outlined in the paper referenced in the file header comment ! 323: * above. ! 324: */ ! 325: ! 326: struct readwrite_lock { ! 327: lnode_t rw_node; /* generic information */ ! 328: ! 329: atomic_ushort_t rw_next_ticket; /* next ticket number to be granted */ ! 330: atomic_ushort_t rw_lock_holder; /* ticket number of lock holder */ ! 331: ! 332: atomic_ushort_t rw_readers; ! 333: ! 334: atomic_uchar_t rw_locked; ! 335: ! 336: pl_t rw_min_pl; /* min pl to acquire the lock with */ ! 337: ! 338: uchar_t rw_hierarchy; /* acquisition order constraint */ ! 339: }; ! 340: ! 341: ! 342: /* ! 343: * Internal structure of a synchronisation variable ! 344: */ ! 345: ! 346: struct synch_var { ! 347: lnode_t sv_node; /* generic information */ ! 348: ! 349: plist_t sv_plist [1]; /* list of blocked processes */ ! 350: }; ! 351: ! 352: ! 353: /* ! 354: * Internal structure of a sleep lock. ! 355: * ! 356: * Note that the "sl_holder" member exists purely to support the debugging ! 357: * SLEEP_LOCKOWNED () function, nothing else. What the System V documentation ! 358: * fails to discuss is that sleep locks don't have to be owned by *any* ! 359: * process. Sleep locks can be acquired at interrupt level via ! 360: * SLEEP_TRYLOCK (), and released by interrupts as well. There doesn't appear ! 361: * to be any mechanism to prevent locks being passed between processes by ! 362: * various methods, and so on and so forth. ! 363: * ! 364: * Therefore, the flag members are the only members actually used by the ! 365: * implementations of the sleep-lock functions, and the "sl_holder" member ! 366: * may not be correct if the lock was acquired by an interrupt context. ! 367: * ! 368: * As discussed elsewhere, the main focus of activity for sleep locks is the ! 369: * list of waiting processes, which contains a test-and-set lock that should ! 370: * be used to control most aspects of lock state. ! 371: */ ! 372: ! 373: struct sleep_lock { ! 374: lnode_t sl_node; /* generic information */ ! 375: ! 376: atomic_uchar_t sl_locked; /* is sleep lock held? */ ! 377: ! 378: plist_t sl_plist [1]; /* process list */ ! 379: ! 380: _VOID * sl_holder; /* ID of process holding lock */ ! 381: }; ! 382: ! 383: ! 384: /* ! 385: * Downcasting operators. Hopefully we won't need these. ! 386: */ ! 387: ! 388: #define lnode_to_basic(n) ((lock_t *) ((char *) (n) - \ ! 389: offsetof (lock_t, bl_node))) ! 390: ! 391: #define lnode_to_rw(n) ((rwlock_t *) ((char *) (n) - \ ! 392: offsetof (rwlock_t, rw_node))) ! 393: ! 394: #define lnode_to_sv(n) ((sv_t *) ((char *) (n) - \ ! 395: offsetof (sv_t, sv_node))) ! 396: ! 397: #define lnode_to_sleep(n) ((sleep_t *) ((char *) (n) - \ ! 398: offsetof (sleep_t, sl_node))) ! 399: ! 400: ! 401: /* ! 402: * For debugging purposes, we want to keep lists of all the allocated locks. ! 403: * ! 404: * In the absence of atomic compare-and-swap operations, it's hard to define ! 405: * good list-manipulation code, so we'll protect our list operations with ! 406: * simple test-and-set locks. As usual on a uniprocessor these are still ! 407: * useful for detecting potential inconsistency. ! 408: * ! 409: * There are no init or destroy methods/macros for this structure since there ! 410: * are only the following static instances defined. ! 411: */ ! 412: ! 413: __LOCAL__ struct lock_list { ! 414: __CONST__ char * ll_name; /* name of list */ ! 415: ! 416: atomic_uchar_t ll_locked; /* single-thread list operations */ ! 417: lnode_t * ll_head; /* head of list */ ! 418: } basic_locks = { "basic lock" }, ! 419: rw_locks = { "read/write lock" }, ! 420: synch_vars = { "synchronisation variable" }, ! 421: sleep_locks = { "sleep lock" }; ! 422: ! 423: #define LOCKLIST_LOCK(l,n) (TEST_AND_SET_LOCK ((l)->ll_locked, plhi, n)) ! 424: #define LOCKLIST_UNLOCK(l,p) (ATOMIC_CLEAR_UCHAR ((l)->ll_locked),\ ! 425: (void) splx (p)) ! 426: ! 427: /* ! 428: * We must abstract the linkage between locks and memory allocation; the lock ! 429: * system requires memory allocation services which require lock services... ! 430: * certain definitions below (and presumably similar definitions in the memory ! 431: * management system) can be used to resolve this dependency. In addition, ! 432: * the abstraction provided can be used to decouple the provided systems (and ! 433: * this aids portability) or to increase their coupling (to increase ! 434: * performance). ! 435: * ! 436: * The definitions (which may be macros): ! 437: * _lock_malloc () ! 438: * _lock_free () ! 439: * define an internal interface to the memory management system. If the memory ! 440: * allocation system uses LOCK_ALLOC () to allocate the locks used to ! 441: * coordinate access to the memory pool(s), the functions above will work as ! 442: * expected even during the memory system's call to LOCK_ALLOC (), and will ! 443: * coordinate access as necessary with other memory allocation interfaces ! 444: * thereafter. ! 445: */ ! 446: ! 447: __EXTERN_C_BEGIN__ ! 448: ! 449: _VOID * _lock_malloc __PROTO ((size_t size, int flag)); ! 450: void _lock_free __PROTO ((_VOID * mem, size_t size)); ! 451: ! 452: __EXTERN_C_END__ ! 453: ! 454: ! 455: /* ! 456: * For now, we just map these functions onto the kmem_ interface and deal ! 457: * with startup issues there. ! 458: */ ! 459: ! 460: #define _lock_malloc(s,f) kmem_alloc (s, f) ! 461: #define _lock_free(s,f) kmem_free (s, f) ! 462: ! 463: ! 464: /* ! 465: * This file-local function encapsulates the hierarchy-recording function that ! 466: * deals with the bookkeeping necessary to ensure that locks are acquired ! 467: * strictly in the order defined (to avoid deadlock). ! 468: * ! 469: * Due to the way in which the DDI/DKI locking functions are defined, it might ! 470: * be possible on some architectures for a basic or read/write lock acquired ! 471: * on one CPU to eventually be released on another. ! 472: * ! 473: * In a multi-processor system where this might be possible, it might be more ! 474: * sensible to record the hierarchy information in some per-priority-level ! 475: * fashion (as the DDI/DKI alludes to). Since this is currently not ! 476: * necessary, we'll elect to design that ability later. ! 477: */ ! 478: ! 479: #if __USE_PROTO__ ! 480: __LOCAL__ void (LOCK_COUNT_HIERARCHY) (__lkhier_t hierarchy) ! 481: #else ! 482: __LOCAL__ void ! 483: LOCK_COUNT_HIERARCHY __ARGS ((hierarchy)) ! 484: __lkhier_t hierarchy; ! 485: #endif ! 486: { ! 487: dcdata_t * dcdata = ddi_cpu_data (); ! 488: pl_t prev_pl = splhi (); ! 489: ! 490: /* ! 491: * We'll skip the usual paranoid ASSERT () tests since this is purely ! 492: * a local function. ! 493: */ ! 494: ! 495: dcdata->dc_hierarchy_cnt [hierarchy - __MIN_HIERARCHY__] ++; ! 496: ! 497: if (dcdata->dc_max_hierarchy < hierarchy) ! 498: dcdata->dc_max_hierarchy = hierarchy; ! 499: ! 500: (void) splx (prev_pl); ! 501: } ! 502: ! 503: ! 504: /* ! 505: * This file-local function is the dual to the above, for adjusting the ! 506: * hierarchy information for a lock release. ! 507: */ ! 508: ! 509: #define ARRAY_MAX(array) (sizeof (array) / sizeof ((array) [0]) ! 510: ! 511: #if __USE_PROTO__ ! 512: __LOCAL__ void (LOCK_FREE_HIERARCHY) (__lkhier_t hierarchy) ! 513: #else ! 514: __LOCAL__ void ! 515: LOCK_FREE_HIERARCHY __ARGS ((hierarchy)) ! 516: __lkhier_t hierarchy; ! 517: #endif ! 518: { ! 519: dcdata_t * dcdata = ddi_cpu_data (); ! 520: pl_t prev_pl = splhi (); ! 521: ! 522: /* ! 523: * We'll skip the usual paranoid ASSERT () tests since this is purely ! 524: * a local function. ! 525: */ ! 526: ! 527: dcdata->dc_hierarchy_cnt [hierarchy - __MIN_HIERARCHY__] --; ! 528: ! 529: /* ! 530: * Work out the lowest occupied priority level. ! 531: */ ! 532: ! 533: do { ! 534: if (dcdata->dc_hierarchy_cnt [dcdata->dc_max_hierarchy - ! 535: __MIN_HIERARCHY__] > 0) ! 536: break; ! 537: } while (-- dcdata->dc_max_hierarchy >= __MIN_HIERARCHY__); ! 538: ! 539: (void) splx (prev_pl); ! 540: } ! 541: ! 542: ! 543: #if 0 ! 544: /* ! 545: * This file-local function takes care of recording debugging information and ! 546: * statistics relating to lock acquisition. ! 547: */ ! 548: ! 549: #if __USE_PROTO__ ! 550: __LOCAL__ void (TRACE_BASIC_LOCK) (lock_t * lockp) ! 551: #else ! 552: __LOCAL__ void ! 553: TRACE_BASIC_LOCK __ARGS ((lockp)) ! 554: lock_t * lockp; ! 555: #endif ! 556: { ! 557: /* ! 558: * We'll skip the usual paranoid ASSERT () tests since this is purely ! 559: * a local function. ! 560: */ ! 561: } ! 562: ! 563: ! 564: ! 565: /* ! 566: * This file-local function takes care of undoing any data-structure changes ! 567: * made by the above and recording additional statistics when a lock is ! 568: * released ! 569: */ ! 570: ! 571: #if __USE_PROTO__ ! 572: __LOCAL__ void (UNTRACE_BASIC_LOCK) (lock_t * lockp) ! 573: #else ! 574: __LOCAL__ void ! 575: UNTRACE_BASIC_LOCK __ARGS ((lockp)) ! 576: lock_t * lockp; ! 577: #endif ! 578: { ! 579: /* ! 580: * We'll skip the usual paranoid ASSERT () tests since this is purely ! 581: * a local function. ! 582: */ ! 583: } ! 584: ! 585: ! 586: /* ! 587: * This file-local function takes care of recording debugging information and ! 588: * statistics relating to lock acquisition. ! 589: */ ! 590: ! 591: #if __USE_PROTO__ ! 592: __LOCAL__ void (TRACE_RW_LOCK) (rwlock_t * lockp) ! 593: #else ! 594: __LOCAL__ void ! 595: TRACE_RW_LOCK __ARGS ((lockp)) ! 596: rwlock_t * lockp; ! 597: #endif ! 598: { ! 599: /* ! 600: * We'll skip the usual paranoid ASSERT () tests since this is purely ! 601: * a local function. ! 602: */ ! 603: } ! 604: ! 605: ! 606: ! 607: /* ! 608: * This file-local function takes care of undoing any data-structure changes ! 609: * made by the above and recording additional statistics when a lock is ! 610: * released. ! 611: */ ! 612: ! 613: #if __USE_PROTO__ ! 614: __LOCAL__ void (UNTRACE_RW_LOCK) (rwlock_t * lockp) ! 615: #else ! 616: __LOCAL__ void ! 617: UNTRACE_RW_LOCK __ARGS ((lockp)) ! 618: rwlock_t * lockp; ! 619: #endif ! 620: { ! 621: /* ! 622: * We'll skip the usual paranoid ASSERT () tests since this is purely ! 623: * a local function. ! 624: */ ! 625: } ! 626: ! 627: #else ! 628: ! 629: /* ! 630: * Since all the above functions have empty bodies, we'll define empty macros ! 631: * to get around the warnings. ! 632: */ ! 633: ! 634: #define TRACE_BASIC_LOCK(l) ! 635: #define UNTRACE_BASIC_LOCK(l) ! 636: ! 637: #define TRACE_RW_LOCK(l) ! 638: #define UNTRACE_RW_LOCK(l) ! 639: ! 640: #endif ! 641: ! 642: ! 643: ! 644: /* ! 645: * Simple common function to acquire a simple test-and-set lock. ! 646: * ! 647: * TEST_AND_SET_LOCK () uses the splraise () private function defined in ! 648: * <sys/inline.h> to raise the processor priority level to "pl". This ensures ! 649: * maximum safety in the use of this function, and certain DDI/DKI facilities ! 650: * such as freezestr () require this behaviour. ! 651: */ ! 652: ! 653: #if __USE_PROTO__ ! 654: pl_t (TEST_AND_SET_LOCK) (__atomic_uchar_t locked, pl_t pl, ! 655: __CONST__ char * name) ! 656: #else ! 657: pl_t ! 658: TEST_AND_SET_LOCK __ARGS ((locked, pl, name)) ! 659: __atomic_uchar_t locked; ! 660: pl_t pl; ! 661: __CONST__ char * name; ! 662: #endif ! 663: { ! 664: for (;;) { ! 665: pl_t prev_pl = splraise (pl); ! 666: ! 667: if (ATOMIC_TEST_AND_SET_UCHAR (locked) == 0) ! 668: return prev_pl; /* all OK */ ! 669: ! 670: ! 671: /* ! 672: * While we spin for the lock, we can allow interrupts that ! 673: * were permissible at entry to this routine. ! 674: */ ! 675: ! 676: (void) splx (prev_pl); ! 677: ! 678: #ifdef __UNIPROCESSOR__ ! 679: cmn_err (CE_PANIC, "%s : test-and-set deadlock", name); ! 680: #elif defined (__TEST_AND_TEST__) ! 681: /* ! 682: * To reduce contention, we wait until the test-and-set lock ! 683: * is free before attempting to re-acquire it. Of course, more ! 684: * sophisticated backoff schemes might also help, even for ! 685: * this approach. ! 686: */ ! 687: ! 688: while (ATOMIC_FETCH_UCHAR (locked) != 0) ! 689: /* DO NOTHING */; ! 690: #endif ! 691: } ! 692: } ! 693: ! 694: ! 695: ! 696: /* ! 697: * File-local function to initialise the generic part of a lock; this normally ! 698: * enqueues the lock on a list. This function also has the responsibility of ! 699: * allocating any needed statistics buffers, which requires that it be passed ! 700: * the flag which indicates whether it is allowed to block or not. ! 701: */ ! 702: ! 703: #if __USE_PROTO__ ! 704: __LOCAL__ void (INIT_LNODE) (lnode_t * lnode, lkinfo_t * lkinfop, ! 705: struct lock_list * list, int __NOTUSED (flag)) ! 706: #else ! 707: __LOCAL__ void ! 708: INIT_LNODE __ARGS ((lnode, lkinfop, list, flag)) ! 709: lnode_t * lnode; ! 710: lkinfo_t * lkinfop; ! 711: struct lock_list ! 712: * list; ! 713: int flag; ! 714: #endif ! 715: { ! 716: pl_t prev_pl; ! 717: ! 718: ASSERT (lnode != NULL && list != NULL); ! 719: ! 720: /* ! 721: * Here we allocate and initialise any needed statistics information, ! 722: * currently nothing. ! 723: */ ! 724: ! 725: lnode->ln_lkinfo = lkinfop; ! 726: lnode->ln_prev = NULL; ! 727: ! 728: ! 729: /* ! 730: * Lock the list, enqueue the node, and unlock the list. ! 731: */ ! 732: ! 733: prev_pl = LOCKLIST_LOCK (list, list->ll_name); ! 734: ! 735: if ((lnode->ln_next = list->ll_head) != NULL) ! 736: lnode->ln_next->ln_prev = lnode; ! 737: list->ll_head = lnode; ! 738: ! 739: LOCKLIST_UNLOCK (list, prev_pl); ! 740: } ! 741: ! 742: ! 743: ! 744: /* ! 745: * The dual to the above, this file-local function dequeues the node from the ! 746: * given list of locks and frees any statistics buffer memory. ! 747: */ ! 748: ! 749: #if __USE_PROTO__ ! 750: __LOCAL__ void (FREE_LNODE) (lnode_t * lnode, struct lock_list * list) ! 751: #else ! 752: __LOCAL__ void ! 753: FREE_LNODE __ARGS ((lnode, list)) ! 754: lnode_t * lnode; ! 755: struct lock_list ! 756: * list; ! 757: #endif ! 758: { ! 759: pl_t prev_pl; ! 760: ! 761: /* ! 762: * We'll skip the paranoid assertions on this one, since the function ! 763: * is file-local. ! 764: * ! 765: * Lock the list, dequeue the node, unlock the list. After that we ! 766: * can free any statistics buffers. ! 767: */ ! 768: ! 769: prev_pl = LOCKLIST_LOCK (list, list->ll_name); ! 770: ! 771: if (lnode->ln_prev == NULL) ! 772: list->ll_head = lnode->ln_next; ! 773: else ! 774: lnode->ln_prev->ln_next = lnode->ln_next; ! 775: ! 776: if (lnode->ln_next != NULL) ! 777: lnode->ln_next->ln_prev = lnode->ln_prev; ! 778: ! 779: LOCKLIST_UNLOCK (list, prev_pl); ! 780: } ! 781: ! 782: ! 783: /* ! 784: *-STATUS: ! 785: * DDI/DKI ! 786: * ! 787: *-NAME: ! 788: * LOCK_ALLOC () Allocate a basic lock ! 789: * ! 790: *-SYNOPSIS: ! 791: * #include <sys/types.h> ! 792: * #include <sys/kmem.h> ! 793: * #include <sys/ksynch.h> ! 794: * ! 795: * lock_t * LOCK_ALLOC (uchar_t hierarchy, pl_t min_pl, ! 796: * lkinfo_t * lkinfop, int flag); ! 797: * ! 798: *-ARGUMENTS: ! 799: * hierarchy Hierarchy value which asserts the order in which this ! 800: * lock will be acquired relative to other basic and ! 801: * read/write locks. "hierarchy" must be within the range ! 802: * of 1 through 32 inclusive and must be chosen such that ! 803: * locks are normally acquired in order of increasing ! 804: * "hierarchy" number. In other words, when acquiring a ! 805: * basic lock using any function other than TRYLOCK (), ! 806: * the lock being acquired must have a "hierarchy" value ! 807: * that is strictly greater than the "hierarchy" values ! 808: * associated with all locks currently held by the ! 809: * calling context. ! 810: * ! 811: * Implementations of lock testing may differ in whether ! 812: * they assume a separate range of "hierarchy" values for ! 813: * each interrupt priority level or a single range that ! 814: * spans all interrupt priority levels. In order to be ! 815: * portable across different implementations, drivers ! 816: * which may acquire locks at more than one interrupt ! 817: * priority level should define the "hierarchy" among ! 818: * those locks such that the "hierarchy" is strictly ! 819: * increasing with increasing priority level (eg. if M ! 820: * is the maximum "hierarchy" value defined for any lock ! 821: * that may be acquired at priority level N, then M + 1 ! 822: * should be the minimum hierarchy value for any lock ! 823: * that may be acquired at any priority level greater ! 824: * than N). ! 825: * ! 826: * min_pl Minimum priority level argument which asserts the ! 827: * minimum priority leel that will be passed in with any ! 828: * attempt to acquire this lock [see LOCK ()]. ! 829: * Implementations which do not require that the ! 830: * interrupt priority level be raised during lock ! 831: * acquisition may choose not to enforce the "min_pl" ! 832: * assertion. The valid values for this argument are as ! 833: * follows: ! 834: * ! 835: * plbase Block no interrupts ! 836: * pltimeout Block functions scheduled by itimeout ! 837: * and dtimeout ! 838: * pldisk Block disk device interrupts ! 839: * plstr Block STREAMS interrupts ! 840: * plhi Block all interrupts ! 841: * ! 842: * The notion of a "min_pl" assumes a defined order of ! 843: * priority levels. The following partial order is ! 844: * defined: ! 845: * plbase < pltimeout <= pldisk, plstr <= plhi ! 846: * ! 847: * The ordering of pldisk and plstr relative to each ! 848: * other is not defined. ! 849: * ! 850: * Setting a given priority level will block interrupts ! 851: * associated with that level as well as all levels that ! 852: * are defined to be less than or equal to the specified ! 853: * level. In order to be portable a driver should not ! 854: * acquire locks at different priority levels where the ! 855: * relative order of those priority levels is not defined ! 856: * above. ! 857: * ! 858: * The "min_pl" argument should specify a priority level ! 859: * that would be sufficient to block out any interrupt ! 860: * handler that might attempt to acquire this lock. In ! 861: * addition, potential deadlock problems involving ! 862: * multiple locks should be considered when defining the ! 863: * "min_pl" value. For example, if the normal order of ! 864: * acquisition of locks A and B (as defined by the lock ! 865: * hierarchy) is to acquire A first and then B, lock B ! 866: * should never be acquired at a priority level less ! 867: * than the "min_pl" for lock A. Therefore, the "min_pl" ! 868: * for lock B should be greater than or equal to the ! 869: * "min_pl" for lock A. ! 870: * ! 871: * Note that the specification of a "min_pl" with a ! 872: * LOCK_ALLOC () call does not actually cause any ! 873: * interrupts to be blocked upon lock acquisition, it ! 874: * simply asserts that subsequent LOCK () calls to ! 875: * acquire this lock will pass in a priority level at ! 876: * least as great as "min_pl". ! 877: * ! 878: * lkinfop Pointer to a lkinfo structure. The "lk_name" member of ! 879: * the "lkinfo" structure points to a character string ! 880: * defining a name that will be associated with the lock ! 881: * for the purposes of statistics gathering. The name ! 882: * should begin with the driver prefix and should be ! 883: * unique to the lock or group of locks for which the ! 884: * driver wishes to collect a uniquely identifiable set ! 885: * of statistics (ie, if a given name is shared by a ! 886: * group of locks, the statistics of the individual locks ! 887: * within the group will not be uniquely identifiable). ! 888: * There are no flags within the lk_flags member of the ! 889: * lkinfo structure defined for use with LOCK_ALLOC (). ! 890: * ! 891: * A given lkinfo structure may be shared among multiple ! 892: * basic locks and read/write locks but a lkinfo ! 893: * structure may not be shared between a basic lock and ! 894: * a sleep lock. The called must ensure that the lk_flags ! 895: * and lk_pad members of the lkinfo structure are zeroed ! 896: * out before passing it to LOCK_ALLOC (). ! 897: * ! 898: * flag Specifies whether the caller is willing to sleep ! 899: * waiting for memory. If "flag" is set to KM_SLEEP, the ! 900: * caller will sleep if necessary until sufficient memory ! 901: * is available. If "flag" is set to KM_NOSLEEP, the ! 902: * caller will not sleep, but LOCK_ALLOC () will return ! 903: * NULL if sufficient memory is not immediately ! 904: * available. ! 905: * ! 906: *-DESCRIPTION: ! 907: * LOCK_ALLOC () dynamically allocates and initialises an instance of a ! 908: * basic lock. The lock is initialised to the unlocked state. ! 909: * ! 910: *-RETURN VALUE: ! 911: * Upon successful completion, LOCK_ALLOC () returns a pointer to the ! 912: * newly allocated lock. If KM_NOSLEEP is specified and sufficient ! 913: * memory is not immediately available, LOCK_ALLOC () returns a NULL ! 914: * pointer. ! 915: * ! 916: *-LEVEL: ! 917: * Base only if "flag" is set to KM_SLEEP. Base or Interrupt if "flag" is ! 918: * set to KM_NOSLEEP. ! 919: * ! 920: *-NOTES: ! 921: * May sleep if "flag" is set to KM_NOSLEEP. ! 922: * ! 923: * Driver-defined basic locks and read/write locks may be held across ! 924: * calls to this function if "flag" is set to KM_NOSLEEP but may not be ! 925: * held if "flag" is KM_SLEEP. ! 926: * ! 927: * Driver-defined sleep locks may be held across calls to this function ! 928: * regardless of the value of "flag". ! 929: * ! 930: *-SEE ALSO: ! 931: * LOCK (), LOCK_DEALLOC (), TRYLOCK (), UNLOCK (), lkinfo ! 932: */ ! 933: ! 934: #define ASSERT_HIERARCHY_OK(h,lk) \ ! 935: ASSERT ((lk) != NULL), \ ! 936: ASSERT ((h) >= __MIN_HIERARCHY__ && \ ! 937: (h) <= (((lk)->lk_flags & INTERNAL_LOCK) != 0 ? \ ! 938: __MAX_HIERARCHY__ : __MAX_DDI_HIERARCHY__)) ! 939: ! 940: #if __USE_PROTO__ ! 941: lock_t * (LOCK_ALLOC) (__lkhier_t hierarchy, pl_t min_pl, lkinfo_t * lkinfop, ! 942: int flag) ! 943: #else ! 944: lock_t * ! 945: LOCK_ALLOC __ARGS ((hierarchy, min_pl, lkinfop, flag)) ! 946: __lkhier_t hierarchy; ! 947: pl_t min_pl; ! 948: lkinfo_t * lkinfop; ! 949: int flag; ! 950: #endif ! 951: { ! 952: lock_t * lockp; ! 953: ! 954: ASSERT_HIERARCHY_OK (hierarchy, lkinfop); ! 955: ASSERT (splcmp (plbase, min_pl) <= 0 && splcmp (min_pl, plhi) <= 0); ! 956: ASSERT (flag == KM_SLEEP || flag == KM_NOSLEEP); ! 957: ! 958: /* ! 959: * Allocate and initialise the data, possibly waiting for enough ! 960: * memory to become available. ! 961: */ ! 962: ! 963: if ((lockp = (lock_t *) _lock_malloc (sizeof (* lockp), ! 964: flag)) != NULL) { ! 965: INIT_LNODE (& lockp->bl_node, lkinfop, & basic_locks, flag); ! 966: ! 967: lockp->bl_min_pl = min_pl; ! 968: lockp->bl_hierarchy = hierarchy; ! 969: ! 970: ATOMIC_CLEAR_UCHAR (lockp->bl_locked); ! 971: ! 972: #ifdef __TICKET_LOCK__ ! 973: ATOMIC_CLEAR_USHORT (lockp->bl_next_ticket); ! 974: ATOMIC_CLEAR_USHORT (lockp->bl_lock_holder); ! 975: #endif ! 976: } ! 977: ! 978: return lockp; ! 979: } ! 980: ! 981: ! 982: /* ! 983: *-STATUS: ! 984: * DDI/DKI ! 985: * ! 986: *-NAME: ! 987: * LOCK_DEALLOC () Deallocate an instance of a basic lock. ! 988: * ! 989: *-SYNOPSIS: ! 990: * #include <sys/ksynch.h> ! 991: * ! 992: * void LOCK_DEALLOC (lock_t * lockp); ! 993: * ! 994: *-ARGUMENTS: ! 995: * lockp Pointer to the basic lock to be deallocated. ! 996: * ! 997: *-DESCRIPTION: ! 998: * LOCK_DEALLOC () deallocates the basic lock specified by "lockp". ! 999: * ! 1000: *-RETURN VALUE: ! 1001: * None. ! 1002: * ! 1003: *-LEVEL: ! 1004: * Base or Interrupt. ! 1005: * ! 1006: *-NOTES: ! 1007: * Does not sleep. ! 1008: * ! 1009: * Attempting to deallocate a lock that is currently locked or is being ! 1010: * waited for is an error and will result in undefined behavior. ! 1011: * ! 1012: * Driver-defined basic locks (other than the one being deallocated), ! 1013: * read/write locks and sleep locks may be held across calls to this ! 1014: * function. ! 1015: * ! 1016: *-SEE ALSO: ! 1017: * LOCK (), LOCK_ALLOC (), TRYLOCK (), UNLOCK () ! 1018: */ ! 1019: ! 1020: #if __USE_PROTO__ ! 1021: void (LOCK_DEALLOC) (lock_t * lockp) ! 1022: #else ! 1023: void ! 1024: LOCK_DEALLOC __ARGS ((lockp)) ! 1025: lock_t * lockp; ! 1026: #endif ! 1027: { ! 1028: ASSERT (lockp != NULL); ! 1029: ASSERT (ATOMIC_FETCH_UCHAR (lockp->bl_locked) == 0); ! 1030: ! 1031: #ifdef __TICKET_LOCK__ ! 1032: ASSERT (ATOMIC_FETCH_USHORT (lockp->bl_next_ticket) == ! 1033: ATOMIC_FETCH_USHORT (lockp->bl_lock_holder)); ! 1034: #endif ! 1035: ! 1036: /* ! 1037: * Remove from the list of all basic locks and free any statistics ! 1038: * buffer space before freeing the lock itself. ! 1039: */ ! 1040: ! 1041: FREE_LNODE (& lockp->bl_node, & basic_locks); ! 1042: ! 1043: _lock_free (lockp, sizeof (* lockp)); ! 1044: } ! 1045: ! 1046: ! 1047: /* ! 1048: *-STATUS: ! 1049: * DDI/DKI ! 1050: * ! 1051: *-NAME: ! 1052: * LOCK () Acquire a basic lock ! 1053: * ! 1054: *-SYNOPSIS: ! 1055: * #include <sys/types.h> ! 1056: * #include <sys/ksynch.h> ! 1057: * ! 1058: * pl_t LOCK (lock_t * lockp, pl_t pl); ! 1059: * ! 1060: *-ARGUMENTS: ! 1061: * lockp Pointer to the basic lock to be acquired. ! 1062: * ! 1063: * pl The interrupt priority level to be set while the lock ! 1064: * is held by the caller. Because some implementations ! 1065: * require that interrupts that might attempt to acquire ! 1066: * the lock be blocked on which the lock is held, ! 1067: * portable drivers must specify a "pl" value that is ! 1068: * sufficient to block out any interrupt handler that ! 1069: * might attempt to acquire this lock. See the ! 1070: * description of the "min_pl" argument to LOCK_ALLOC () ! 1071: * for additional discussion. Implementations that do ! 1072: * not require that the interrupt priority level be ! 1073: * raised during lock acquisition may choose to ignore ! 1074: * this argument. ! 1075: * ! 1076: *-DESCRIPTION: ! 1077: * LOCK () sets the interrupt priority level in accordance with the ! 1078: * value specified by "pl" (if required by the implementation) and ! 1079: * acquires the lock specified by "lockp". If the lock is not currently ! 1080: * available, the caller will wait until the lock is available. It is ! 1081: * implementation-defined whether the caller will block during the wait. ! 1082: * Some implementations may cause the caller to spin for the duration of ! 1083: * the wait, while on others the caller may block at some point. ! 1084: * ! 1085: *-RETURN VALUE: ! 1086: * Upon acquiring the lock, LOCK () returns the previous interrupt ! 1087: * priority level. ! 1088: * ! 1089: *-LEVEL: ! 1090: * Base or Interrupt. ! 1091: * ! 1092: *-NOTES: ! 1093: * Basic locks are not recursive. A call to LOCK () attempting to ! 1094: * acquire a lock that is already held by the calling context will ! 1095: * result in deadlock. ! 1096: * ! 1097: * Calls to LOCK () should honor the ordering defined by the lock ! 1098: * hierarchy [see LOCK_ALLOC ()] in order to avoid deadlock. ! 1099: * ! 1100: * Driver-defined sleep locks may be held across calls to this function. ! 1101: * ! 1102: * Driver-defined basic locks and read/write locks may be held across ! 1103: * calls to this function subject to the hierarchy and recursion ! 1104: * restrictions described above. ! 1105: * ! 1106: * When called from interrupt level, the "pl" argument must not specify ! 1107: * a priority below the level at which the interrupt handler is running. ! 1108: * ! 1109: *-SEE ALSO: ! 1110: * LOCK_ALLOC (), LOCK_DEALLOC (), TRYLOCK (), UNLOCK () ! 1111: */ ! 1112: ! 1113: #if __USE_PROTO__ ! 1114: pl_t (LOCK) (lock_t * lockp, pl_t pl) ! 1115: #else ! 1116: pl_t ! 1117: LOCK __ARGS ((lockp, pl)) ! 1118: lock_t * lockp; ! 1119: pl_t pl; ! 1120: #endif ! 1121: { ! 1122: pl_t prev_pl; ! 1123: #ifdef __TICKET_LOCK__ ! 1124: ushort_t ticket_no; ! 1125: #endif ! 1126: ! 1127: ASSERT (lockp != NULL); ! 1128: ASSERT (splcmp (pl, plhi) <= 0); ! 1129: ! 1130: ! 1131: /* ! 1132: * Enforce minimum-priority assertion, pl >= lockp->bl_min_pl. Note ! 1133: * that splcmp () abstracts subtraction-for-comparison of priority ! 1134: * levels, which explains the form of the assertion. ! 1135: */ ! 1136: ! 1137: ASSERT (splcmp (pl, lockp->bl_min_pl) >= 0); ! 1138: ! 1139: ! 1140: /* ! 1141: * On a uniprocessor, encountering a basic lock that is already ! 1142: * locked is *always* an error, even on machines with many different ! 1143: * interrupt priority levels. The hierarchy-assertion mechanism ! 1144: * cannot always deal with this, since TRYLOCK () can acquire locks ! 1145: * in a different order. ! 1146: * ! 1147: * On a multiprocessor, we can just spin here. Note that since LOCK () ! 1148: * is defined as requiring values of "pl" greater than the current ! 1149: * interrupt priority level, the use of splraise () by ! 1150: * TEST_AND_SET_LOCK () is legitimate. ! 1151: */ ! 1152: ! 1153: prev_pl = TEST_AND_SET_LOCK (lockp->bl_locked, pl, "LOCK"); ! 1154: ! 1155: #ifdef __TICKET_LOCK__ ! 1156: /* ! 1157: * If we are working with a ticket-lock scheme, we now take a ticket ! 1158: * and release the test-and-set lock. After that, we can just wait for ! 1159: * our number to come up... ! 1160: */ ! 1161: ! 1162: ticket_no = ATOMIC_FETCH_USHORT (lockp->bl_next_ticket); ! 1163: ! 1164: if (ATOMIC_FETCH_AND_STORE_USHORT (lockp->bl_next_ticket, ! 1165: ticket_no + 1) != ticket_no) { ! 1166: /* ! 1167: * If we didn't read the same number twice, then the basic ! 1168: * lock protection isn't doing its job. ! 1169: */ ! 1170: ! 1171: cmn_err (CE_PANIC, "LOCK : Ticket-lock gate failure"); ! 1172: } ! 1173: ! 1174: ATOMIC_CLEAR_UCHAR (lockp->bl_locked); ! 1175: ! 1176: while (ATOMIC_FETCH_USHORT (lockp->bl_lock_holder) != ticket_no) { ! 1177: /* ! 1178: * At this point we might want to implement some form of ! 1179: * proportional backoff to reduce memory traffic. Later. ! 1180: * ! 1181: * Another hot contender for this spot is detecting failures ! 1182: * on other CPUs... ! 1183: */ ! 1184: ! 1185: #ifdef __UNIPROCESSOR__ ! 1186: cmn_err (CE_PANIC, "LOCK : deadlock on ticket"); ! 1187: #endif ! 1188: } ! 1189: ! 1190: #endif /* defined (__TICKET_LOCK__) */ ! 1191: ! 1192: ! 1193: /* ! 1194: * Now would be an appropriate time to check that the requested level ! 1195: * is >= the current level on entry. The architectures that we are ! 1196: * likely to target require this to be true, although some novel ! 1197: * schemes which reduce interrupt latency do not. ! 1198: * ! 1199: * Since the TEST_AND_SET_LOCK () function which set our priority ! 1200: * level uses splraise (), we are safe making this test at this late ! 1201: * stage. ! 1202: */ ! 1203: ! 1204: ASSERT (splcmp (pl, prev_pl) >= 0); ! 1205: ! 1206: ! 1207: /* ! 1208: * Test the lock-acquisition-hierarchy assertions. ! 1209: */ ! 1210: ! 1211: ASSERT (ddi_cpu_data ()->dc_max_hierarchy < lockp->bl_hierarchy); ! 1212: ! 1213: LOCK_COUNT_HIERARCHY (lockp->bl_hierarchy); ! 1214: ! 1215: ! 1216: /* ! 1217: * Since it may be useful for post-mortem debugging to record some ! 1218: * information about the context which acquired the lock, we defer ! 1219: * to some generic recorder function or macro. ! 1220: * ! 1221: * Note that in general it does not appear to be possible to make ! 1222: * detailed assertions about the relation between the contexts in ! 1223: * which a lock is acquired and/or released. In particular, it ! 1224: * might be possible for a basic lock to be tied to some device ! 1225: * hardware-related operation where a lock might be acquired on one ! 1226: * CPU and released on another. ! 1227: * ! 1228: * In addition, any lock statistics are kept by this operation. ! 1229: */ ! 1230: ! 1231: TRACE_BASIC_LOCK (lockp); ! 1232: ! 1233: return prev_pl; ! 1234: } ! 1235: ! 1236: ! 1237: /* ! 1238: *-STATUS: ! 1239: * DDI/DKI ! 1240: * ! 1241: *-NAME: ! 1242: * TRYLOCK () Try to acquire a basic lock ! 1243: * ! 1244: *-SYNOPSIS: ! 1245: * #include <sys/types.h> ! 1246: * #include <sys/ksynch.h> ! 1247: * ! 1248: * pl_t TRYLOCK (lock_t * lockp, pl_t pl); ! 1249: * ! 1250: *-ARGUMENTS: ! 1251: * lockp Pointer to the basic lock to be acquired. ! 1252: * ! 1253: * pl The interrupt priority level to be set while the lock ! 1254: * is held by the caller. Because some implementations ! 1255: * require that interrupts that might attempt to acquire ! 1256: * the lock be blocked on the processor on which the ! 1257: * lock is held, portable drivers must specify a "pl" ! 1258: * value that is sufficient to block out and interrupt ! 1259: * handler that might attempt to acquire this lock. See ! 1260: * the description of LOCK_ALLOC () for additional ! 1261: * discussion and a list of the valid values for "pl". ! 1262: * Implementations that do not require that the interrupt ! 1263: * priority level be raised during lock acquisition may ! 1264: * choose to ignore this argument. ! 1265: * ! 1266: *-DESCRIPTION: ! 1267: * If the lock specified by "lockp" is immediately available (can be ! 1268: * acquired without waiting) TRYLOCK () sets the interrupt priority level ! 1269: * in accordance with the value specified by "pl" (if required by the ! 1270: * implementation) and acquires the lock. If the lock is not immediately ! 1271: * available, the function returns without acquiring the lock. ! 1272: * ! 1273: *-RETURN VALUE: ! 1274: * If the lock is acquired, TRYLOCK () returns the previous interrupt ! 1275: * priority level ("plbase" - "plhi"). If the lock is not acquired the ! 1276: * value "invpl" is returned. ! 1277: * ! 1278: *-LEVEL: ! 1279: * Base or Interrupt. ! 1280: * ! 1281: *-NOTES: ! 1282: * Does not sleep. ! 1283: * ! 1284: * TRYLOCK () may be used to acquire a lock in a different order from ! 1285: * the order defined by the lock hierarchy. ! 1286: * ! 1287: * Driver-defined basic locks, read/write locks, and sleep locks may be ! 1288: * held across calls to this function. ! 1289: * ! 1290: * When called from interrupt level, the "pl" argument must not specify ! 1291: * a priority level below the level at which the interrupt handler is ! 1292: * running. ! 1293: * ! 1294: *-SEE ALSO: ! 1295: * LOCK (), LOCK_ALLOC (), LOCK_DEALLOC (), UNLOCK () ! 1296: */ ! 1297: ! 1298: #if __USE_PROTO__ ! 1299: pl_t (TRYLOCK) (lock_t * lockp, pl_t pl) ! 1300: #else ! 1301: pl_t ! 1302: TRYLOCK __ARGS ((lockp, pl)) ! 1303: lock_t * lockp; ! 1304: pl_t pl; ! 1305: #endif ! 1306: { ! 1307: pl_t prev_pl; ! 1308: #ifdef __TICKET_LOCK__ ! 1309: ushort_t ticket_no; ! 1310: #endif ! 1311: ! 1312: ASSERT (lockp != NULL); ! 1313: ASSERT (splcmp (pl, plhi) <= 0); ! 1314: ! 1315: ! 1316: /* ! 1317: * Enforce minimum-priority assertion, pl >= lockp->bl_min_pl. Note ! 1318: * that splcmp () abstracts subtraction-for-comparison of priority ! 1319: * levels, which explains the form of the assertion. ! 1320: */ ! 1321: ! 1322: ASSERT (splcmp (pl, lockp->bl_min_pl) >= 0); ! 1323: ! 1324: ! 1325: #ifdef __TICKET_LOCK__ ! 1326: /* ! 1327: * If this is a ticket-lock, we test the ticket numbers to see ! 1328: * whether there is any reason to even try acquiring the test-and-set ! 1329: * lock that forms the ticket gate. ! 1330: * ! 1331: * We read the "lock holder" and "next ticket" entries in that ! 1332: * sequence to be pessimistic, since we can assume that other CPUs ! 1333: * might be looking at this... ! 1334: */ ! 1335: ! 1336: ticket_no = ATOMIC_FETCH_USHORT (lockp->bl_lock_holder); ! 1337: ! 1338: if (ticket_no != ATOMIC_FETCH_USHORT (lockp->bl_next_ticket)) ! 1339: return invpl; ! 1340: #endif ! 1341: ! 1342: ! 1343: /* ! 1344: * We block out interrupts at this point to allow the following ! 1345: * operations room to do their stuff in, since interrupts at an ! 1346: * inappropriate moment can cause deadlock... of course, if the ! 1347: * definition of the lock is such that interrupts can proceed, then ! 1348: * that's OK, since the lock acquisition is atomic. ! 1349: */ ! 1350: ! 1351: prev_pl = splx (pl); ! 1352: ! 1353: ! 1354: /* ! 1355: * Now would be an appropriate time to check that the ! 1356: * requested level is >= the current level on entry. Strictly ! 1357: * speaking, this does not have to be true if the processor is ! 1358: * currently at base level, but for now we'll discourage that ! 1359: * behaviour. ! 1360: */ ! 1361: ! 1362: ASSERT (splcmp (pl, prev_pl) >= 0); ! 1363: ! 1364: ! 1365: /* ! 1366: * Test to see whether the lock in question is already taken, and if ! 1367: * not, we take it. We don't spin if this is a ticket lock, since if ! 1368: * the basic lock is taken there is *no way* that the lock will be ! 1369: * free for us immediately. ! 1370: */ ! 1371: ! 1372: if (ATOMIC_TEST_AND_SET_UCHAR (lockp->bl_locked) == 0) { ! 1373: #ifdef __TICKET_LOCK__ ! 1374: /* ! 1375: * If this is a ticket lock, now would be a good time to ! 1376: * check the ticket numbers to ensure that the lock *really* ! 1377: * is free. ! 1378: */ ! 1379: ! 1380: ticket_no = ATOMIC_FETCH_USHORT (lockp->bl_next_ticket); ! 1381: ! 1382: if (ticket_no != ATOMIC_FETCH_USHORT (lockp->bl_lock_holder)) { ! 1383: ! 1384: ATOMIC_CLEAR_UCHAR (lockp->bl_locked); ! 1385: goto try_failed; ! 1386: } ! 1387: ! 1388: if (ATOMIC_FETCH_AND_STORE_USHORT (lockp->bl_next_ticket, ! 1389: ticket_no + 1) != ticket_no) { ! 1390: /* ! 1391: * If we didn't read the same number twice, then the ! 1392: * test-and-set lock protection isn't doing its job. ! 1393: */ ! 1394: ! 1395: cmn_err (CE_PANIC, "TRYLOCK : Ticket-lock gate failure"); ! 1396: } ! 1397: ! 1398: ! 1399: /* ! 1400: * Now we have the lock, we can release the ticket-gate lock. ! 1401: */ ! 1402: ! 1403: ATOMIC_CLEAR_UCHAR (lockp->bl_locked); ! 1404: ! 1405: #endif /* defined (__TICKET_LOCK__) */ ! 1406: ! 1407: ! 1408: /* ! 1409: * TRYLOCK () bypasses the hierarchy-assertion mechanism, ! 1410: * although we record the maximum acquired hierarchy level for ! 1411: * maximum strictness checking in inner LOCK () attempts. ! 1412: * ! 1413: * We also record debugging and statistics information here ! 1414: * with TRACE_BASIC_LOCK (). ! 1415: */ ! 1416: ! 1417: LOCK_COUNT_HIERARCHY (lockp->bl_hierarchy); ! 1418: ! 1419: TRACE_BASIC_LOCK (lockp); ! 1420: ! 1421: return prev_pl; ! 1422: } ! 1423: ! 1424: try_failed: ! 1425: /* ! 1426: * We cannot acquire the lock, so reset the priority and exit with ! 1427: * the flag value. ! 1428: */ ! 1429: ! 1430: (void) splx (prev_pl); ! 1431: ! 1432: return invpl; ! 1433: } ! 1434: ! 1435: ! 1436: /* ! 1437: *-STATUS: ! 1438: * DDI/DKI ! 1439: * ! 1440: *-NAME: ! 1441: * UNLOCK () Release a basic lock. ! 1442: * ! 1443: *-SYNOPSIS: ! 1444: * #include <sys/types.h> ! 1445: * #include <sys/ksynch.h> ! 1446: * ! 1447: * void UNLOCK (lock_t * lockp, pl_t pl); ! 1448: * ! 1449: *-ARGUMENTS: ! 1450: * lockp Pointer to the basic lock to be released. ! 1451: * ! 1452: * pl The interrupt priority level to be set after releasing ! 1453: * the lock. See the description of the "min_pl" argument ! 1454: * to LOCK_ALLOC () for a list of the valid values for ! 1455: * "pl". If lock calls are not being nested or if the ! 1456: * caller is unlocking in the reverse order that locks ! 1457: * were acquired, the "pl" argument will typically be the ! 1458: * value that was returned from the corresponding call to ! 1459: * acquire the lock. The caller may need to specify a ! 1460: * different value for "pl" if nested locks are being ! 1461: * released in some order other that the reverse order of ! 1462: * acquisition, so as to ensure that the interrupt ! 1463: * priority level is kept sufficiently high to block ! 1464: * interrupt code that might attempt to acquire locks ! 1465: * which are still held. Although portable drivers must ! 1466: * always specify an appropriate "pl" argument, ! 1467: * implementations which do not require that the ! 1468: * interrupt priority level be raised during lock ! 1469: * acquisition may choose to ignore this argument. ! 1470: * ! 1471: *-DESCRIPTION: ! 1472: * UNLOCK () releases the basic lock specified by "lockp" and then sets ! 1473: * the interrupt priority level in accordance with the value specified by ! 1474: * "pl" (if required by the implementation). ! 1475: * ! 1476: *-RETURN VALUE: ! 1477: * None. ! 1478: * ! 1479: *-LEVEL: ! 1480: * Base or Interrupt. ! 1481: * ! 1482: *-NOTES: ! 1483: * Does not sleep. ! 1484: * ! 1485: * Driver-defined basic locks, read/write locks, and sleep locks may be ! 1486: * held across calls to this function. ! 1487: * ! 1488: *-SEE ALSO: ! 1489: * LOCK (), LOCK_ALLOC (), LOCK_DEALLOC (), TRYLOCK () ! 1490: */ ! 1491: ! 1492: #if __USE_PROTO__ ! 1493: void (UNLOCK) (lock_t * lockp, pl_t pl) ! 1494: #else ! 1495: void ! 1496: UNLOCK __ARGS ((lockp, pl)) ! 1497: lock_t * lockp; ! 1498: pl_t pl; ! 1499: #endif ! 1500: { ! 1501: #ifdef __TICKET_LOCK__ ! 1502: ushort_t ticket_no; ! 1503: #endif ! 1504: ! 1505: ASSERT (lockp != NULL); ! 1506: ASSERT (splcmp (plbase, pl) <= 0 && splcmp (pl, plhi) <= 0); ! 1507: ! 1508: ! 1509: /* ! 1510: * We assert that the lock is actually held by someone... in the case ! 1511: * of the ticket lock, we fetch the ticket number now since we don't ! 1512: * have an increment instruction and we'll need it later anyway. ! 1513: */ ! 1514: ! 1515: #ifdef __TICKET_LOCK__ ! 1516: ticket_no = ATOMIC_FETCH_USHORT (lockp->bl_lock_holder); ! 1517: ! 1518: ASSERT (ticket_no != ATOMIC_FETCH_USHORT (lockp->bl_next_ticket)); ! 1519: #else ! 1520: ASSERT (ATOMIC_FETCH_UCHAR (lockp->bl_locked) != 0); ! 1521: #endif ! 1522: ! 1523: /* ! 1524: * Do whatever we need to do to undo the hierarchy-assertion and ! 1525: * debugging/statistics data structures. ! 1526: */ ! 1527: ! 1528: LOCK_FREE_HIERARCHY (lockp->bl_hierarchy); ! 1529: ! 1530: UNTRACE_BASIC_LOCK (lockp); ! 1531: ! 1532: ! 1533: /* ! 1534: * Now release the lock, either to the next ticket-holder or to ! 1535: * whoever gets in first, depending on the locking system. ! 1536: */ ! 1537: ! 1538: #ifdef __TICKET_LOCK__ ! 1539: if (ATOMIC_FETCH_AND_STORE_USHORT (lockp->bl_lock_holder, ! 1540: ticket_no + 1) != ticket_no) { ! 1541: /* ! 1542: * If we didn't read the same number twice, then someone has ! 1543: * released the lock that we own! ! 1544: */ ! 1545: ! 1546: cmn_err (CE_PANIC, "UNLOCK : Ticket-lock sequence problem"); ! 1547: } ! 1548: #else ! 1549: ATOMIC_CLEAR_UCHAR (lockp->bl_locked); ! 1550: #endif ! 1551: ! 1552: /* ! 1553: * And lower out priority level to finish up. ! 1554: */ ! 1555: ! 1556: (void) splx (pl); ! 1557: } ! 1558: ! 1559: ! 1560: /* ! 1561: *-STATUS: ! 1562: * DDI/DKI ! 1563: * ! 1564: *-NAME: ! 1565: * RW_ALLOC () Allocate and initialize a read/write lock ! 1566: * ! 1567: *-SYNOPSIS: ! 1568: * #include <sys/types.h> ! 1569: * #include <sys/kmem.h> ! 1570: * #include <sys/ksynch.h> ! 1571: * ! 1572: * rwlock_t * RW_ALLOC (uchar_t hierarchy, pl_t min_pl, ! 1573: * lkinfo_t * lkinfop, int flag); ! 1574: * ! 1575: *-ARGUMENTS: ! 1576: * hierarchy Hierarchy value which asserts the order in which this ! 1577: * lock will be acquired relative to other basic and ! 1578: * read/write locks. "hierarchy" must be within the range ! 1579: * of 1 through 32 inclusive and must be chosen such that ! 1580: * locks are normally acquired in order of increasing ! 1581: * "hierarchy" number. In other words, when acquiring a ! 1582: * basic lock using any function other than ! 1583: * RW_TRYRDLOCK () or RW_TRYWRLOCK () the lock being ! 1584: * acquired must have a "hierarchy" value that is ! 1585: * strictly greater than the "hierarchy" values ! 1586: * associated with all locks currently held by the ! 1587: * calling context. ! 1588: * ! 1589: * Implementations of lock testing may differ in whether ! 1590: * they assume a separate range of "hierarchy" values for ! 1591: * each interrupt priority level or a single range that ! 1592: * spans all interrupt priority levels. In order to be ! 1593: * portable across different implementations, drivers ! 1594: * which may acquire locks at more than one interrupt ! 1595: * priority level should define the "hierarchy" among ! 1596: * those locks such that the "hierarchy" is strictly ! 1597: * increasing with increasing priority level (eg. if M ! 1598: * is the maximum "hierarchy" value defined for any lock ! 1599: * that may be acquired at priority level N, then M + 1 ! 1600: * should be the minimum hierarchy value for any lock ! 1601: * that may be acquired at any priority level greater ! 1602: * than N). ! 1603: * ! 1604: * min_pl Minimum priority level argument which asserts the ! 1605: * minimum priority leel that will be passed in with any ! 1606: * attempt to acquire this lock [see RW_RDLOCK () and ! 1607: * RW_WRLOCK ()]. Implementations which do not require ! 1608: * that the interrupt priority level be raised during ! 1609: * lock acquisition may choose not to enforce the ! 1610: * "min_pl" assertion. The valid values for this ! 1611: * argument are as follows: ! 1612: * ! 1613: * plbase Block no interrupts ! 1614: * pltimeout Block functions scheduled by itimeout ! 1615: * and dtimeout ! 1616: * pldisk Block disk device interrupts ! 1617: * plstr Block STREAMS interrupts ! 1618: * plhi Block all interrupts ! 1619: * ! 1620: * The notion of a "min_pl" assumes a defined order of ! 1621: * priority levels. The following partial order is ! 1622: * defined: ! 1623: * plbase < pltimeout <= pldisk, plstr <= plhi ! 1624: * ! 1625: * The ordering of pldisk and plstr relative to each ! 1626: * other is not defined. ! 1627: * ! 1628: * Setting a given priority level will block interrupts ! 1629: * associated with that level as well as all levels that ! 1630: * are defined to be less than or equal to the specified ! 1631: * level. In order to be portable a driver should not ! 1632: * acquire locks at different priority levels where the ! 1633: * relative order of those priority levels is not defined ! 1634: * above. ! 1635: * ! 1636: * The "min_pl" argument should specify a priority level ! 1637: * that would be sufficient to block out any interrupt ! 1638: * handler that might attempt to acquire this lock. In ! 1639: * addition, potential deadlock problems involving ! 1640: * multiple locks should be considered when defining the ! 1641: * "min_pl" value. For example, if the normal order of ! 1642: * acquisition of locks A and B (as defined by the lock ! 1643: * hierarchy) is to acquire A first and then B, lock B ! 1644: * should never be acquired at a priority level less ! 1645: * than the "min_pl" for lock A. Therefore, the "min_pl" ! 1646: * for lock B should be greater than or equal to the ! 1647: * "min_pl" for lock A. ! 1648: * ! 1649: * Note that the specification of a "min_pl" with a ! 1650: * RW_ALLOC () call does not actually cause any ! 1651: * interrupts to be blocked upon lock acquisition, it ! 1652: * simply asserts that subsequent RW_RDLOCK () or ! 1653: * RW_WRLOCK () calls to acquire this lock will pass in a ! 1654: * priority level at least as great as "min_pl". ! 1655: * ! 1656: * lkinfop Pointer to a lkinfo structure. The lk_name member of ! 1657: * the lkinfo structure points to a character string ! 1658: * defining a name that will be associated with the lock ! 1659: * for the purposes of statistics gathering. The name ! 1660: * should begin with the driver prefix and should be ! 1661: * unique to the lock or group of locks for which the ! 1662: * driver wishes to collect a uniquely identifiable set ! 1663: * of statistics (ie, if a given name is shared by a ! 1664: * group of locks, the statistics of the individual locks ! 1665: * within the group will not be uniquely identifiable). ! 1666: * There are no flags within the lk_flags member of the ! 1667: * lkinfo structure defined for use with RW_ALLOC (). ! 1668: * ! 1669: * A given lkinfo structure may be shared among multiple ! 1670: * basic locks and read/write locks but a lkinfo ! 1671: * structure may not be shared between a basic lock and ! 1672: * a sleep lock. The called must ensure that the lk_flags ! 1673: * and lk_pad members of the lkinfo structure are zeroed ! 1674: * out before passing it to RW_ALLOC (). ! 1675: * ! 1676: * flag Specifies whether the caller is willing to sleep ! 1677: * waiting for memory. If "flag" is set to KM_SLEEP, the ! 1678: * caller will sleep if necessary until sufficient memory ! 1679: * is available. If "flag" is set to KM_NOSLEEP, the ! 1680: * caller will not sleep, but RW_ALLOC () will return ! 1681: * NULL if sufficient memory is not immediately ! 1682: * available. ! 1683: * ! 1684: *-DESCRIPTION: ! 1685: * RW_ALLOC () dynamically allocates and initialises an instance of a ! 1686: * read/write lock. The lock is initialised to the unlocked state. ! 1687: * ! 1688: *-RETURN VALUE: ! 1689: * Upon successful completion, RW_ALLOC () returns a pointer to the ! 1690: * newly allocated lock. If KM_NOSLEEP is specified and sufficient ! 1691: * memory is not immediately available, RW_ALLOC () returns a NULL ! 1692: * pointer. ! 1693: * ! 1694: *-LEVEL: ! 1695: * Base only if "flag" is set to KM_SLEEP. Base or Interrupt if "flag" is ! 1696: * set to KM_NOSLEEP. ! 1697: * ! 1698: *-NOTES: ! 1699: * May sleep if "flag" is set to KM_NOSLEEP. ! 1700: * ! 1701: * Driver-defined basic locks and read/write locks may be held across ! 1702: * calls to this function if "flag" is set to KM_NOSLEEP but may not be ! 1703: * held if "flag" is KM_SLEEP. ! 1704: * ! 1705: * Driver-defined sleep locks may be held across calls to this function ! 1706: * regardless of the value of "flag". ! 1707: * ! 1708: *-SEE_ALSO: ! 1709: * RW_DEALLOC (), RW_RDLOCK (), RW_TRYRDLOCK (), RW_TRYWRLOCK (), ! 1710: * RW_UNLOCK (), RW_WRLOCK (), lkinfo ! 1711: */ ! 1712: ! 1713: #if __USE_PROTO__ ! 1714: rwlock_t * (RW_ALLOC) (__lkhier_t hierarchy, pl_t min_pl, lkinfo_t * lkinfop, ! 1715: int flag) ! 1716: #else ! 1717: rwlock_t * ! 1718: RW_ALLOC __ARGS ((hierarchy, min_pl, lkinfop, flag)) ! 1719: __lkhier_t hierarchy; ! 1720: pl_t min_pl; ! 1721: lkinfo_t * lkinfop; ! 1722: int flag; ! 1723: #endif ! 1724: { ! 1725: rwlock_t * lockp; ! 1726: ! 1727: ASSERT_HIERARCHY_OK (hierarchy, lkinfop); ! 1728: ASSERT (splcmp (plbase, min_pl) <= 0 && splcmp (min_pl, plhi) <= 0); ! 1729: ASSERT (flag == KM_SLEEP || flag == KM_NOSLEEP); ! 1730: ! 1731: ! 1732: /* ! 1733: * Allocate and initialise the data, possibly waiting for enough ! 1734: * memory to become available. ! 1735: */ ! 1736: ! 1737: if ((lockp = (rwlock_t *) _lock_malloc (sizeof (* lockp), ! 1738: flag)) != NULL) { ! 1739: INIT_LNODE (& lockp->rw_node, lkinfop, & rw_locks, flag); ! 1740: ! 1741: lockp->rw_min_pl = min_pl; ! 1742: lockp->rw_hierarchy = hierarchy; ! 1743: ! 1744: ATOMIC_CLEAR_UCHAR (lockp->rw_locked); ! 1745: ! 1746: ATOMIC_CLEAR_USHORT (lockp->rw_readers); ! 1747: ! 1748: ATOMIC_CLEAR_USHORT (lockp->rw_next_ticket); ! 1749: ATOMIC_CLEAR_USHORT (lockp->rw_lock_holder); ! 1750: } ! 1751: ! 1752: return lockp; ! 1753: } ! 1754: ! 1755: ! 1756: /* ! 1757: *-STATUS: ! 1758: * DDI/DKI ! 1759: * ! 1760: *-NAME: ! 1761: * RW_DEALLOC () Deallocate an instance of a read/write lock. ! 1762: * ! 1763: *-SYNOPSIS: ! 1764: * #include <sys/ksynch.h> ! 1765: * ! 1766: * void RW_DEALLOC (rwlock_t * lockp); ! 1767: * ! 1768: *-ARGUMENTS: ! 1769: * lockp Pointer to the read/write lock to be deallocated. ! 1770: * ! 1771: *-DESCRIPTION: ! 1772: * RW_DEALLOC () deallocates the read/write lock specified by "lockp". ! 1773: * ! 1774: *-RETURN VALUE: ! 1775: * None. ! 1776: * ! 1777: *-LEVEL: ! 1778: * Base or Interrupt. ! 1779: * ! 1780: *-NOTES: ! 1781: * Does not sleep. ! 1782: * ! 1783: * Attempting to deallocate a lock that is currently locked or is being ! 1784: * waited for is an error and will result in undefined behavior. ! 1785: * ! 1786: * Driver-defined basic locks, read/write locks (other than the one being ! 1787: * deallocated), and sleep locks may be held across calls to this ! 1788: * function. ! 1789: * ! 1790: *-SEE_ALSO: ! 1791: * RW_ALLOC (), RW_RDLOCK (), RW_TRYRDLOCK (), RW_TRYWRLOCK (), ! 1792: * RW_UNLOCK (), RW_WRLOCK () ! 1793: */ ! 1794: ! 1795: #if __USE_PROTO__ ! 1796: void (RW_DEALLOC) (rwlock_t * lockp) ! 1797: #else ! 1798: void ! 1799: RW_DEALLOC __ARGS ((lockp)) ! 1800: rwlock_t * lockp; ! 1801: #endif ! 1802: { ! 1803: ASSERT (lockp != NULL); ! 1804: ASSERT (ATOMIC_FETCH_UCHAR (lockp->rw_locked) == 0); ! 1805: ASSERT (ATOMIC_FETCH_USHORT (lockp->rw_readers) == 0); ! 1806: ! 1807: ASSERT (ATOMIC_FETCH_USHORT (lockp->rw_next_ticket) == ! 1808: ATOMIC_FETCH_USHORT (lockp->rw_lock_holder)); ! 1809: ! 1810: ! 1811: /* ! 1812: * Remove from the list of all read/write locks and free any ! 1813: * statistics buffer space before freeing the lock itself. ! 1814: */ ! 1815: ! 1816: FREE_LNODE (& lockp->rw_node, & rw_locks); ! 1817: ! 1818: _lock_free (lockp, sizeof (* lockp)); ! 1819: } ! 1820: ! 1821: ! 1822: /* ! 1823: *-STATUS: ! 1824: * DDI/DKI ! 1825: * ! 1826: *-NAME: ! 1827: * RW_RDLOCK () Acquire a read/write lock in read mode. ! 1828: * ! 1829: *-SYNOPSIS: ! 1830: * #include <sys/types.h> ! 1831: * #include <sys/ksynch.h> ! 1832: * ! 1833: * pl_t RW_RDLOCK (rwlock_t * lockp, pl_t pl); ! 1834: * ! 1835: *-ARGUMENTS: ! 1836: * lockp Pointer to the read/write lock to be acquired. ! 1837: * ! 1838: * pl The interrupt priority level to be set while the lock ! 1839: * is held by the caller. Because some implementations ! 1840: * require that interrupts that might attempt to acquire ! 1841: * the lock be blocked on which the lock is held, ! 1842: * portable drivers must specify a "pl" value that is ! 1843: * sufficient to block out any interrupt handler that ! 1844: * might attempt to acquire this lock. See the ! 1845: * description of the "min_pl" argument to RW_ALLOC () ! 1846: * for additional discussion. Implementations that do ! 1847: * not require that the interrupt priority level be ! 1848: * raised during lock acquisition may choose to ignore ! 1849: * this argument. ! 1850: * ! 1851: *-DESCRIPTION: ! 1852: * RW_RDLOCK () sets the interrupt priority level in accordance with the ! 1853: * value specified by "pl" (if required by the implementation) and ! 1854: * acquires the lock specified by "lockp". If the lock is not currently ! 1855: * available, the caller will wait until the lock is available in read ! 1856: * mode. A read/write lock is available in read mode when the lock is ! 1857: * not held by any context or when the lock is held by one or more ! 1858: * readers and there are no waiting writers. It is implementation-defined ! 1859: * whether the caller will block during the wait. Some implementations ! 1860: * may cause the caller to spin for the duration of the wait, while on ! 1861: * others the caller may block at some point. ! 1862: * ! 1863: *-RETURN VALUE: ! 1864: * Upon acquiring the lock, RW_RDLOCK () returns the previous interrupt ! 1865: * priority level. ! 1866: * ! 1867: *-LEVEL: ! 1868: * Base or Interrupt. ! 1869: * ! 1870: *-NOTES: ! 1871: * Read/write locks are not recursive. A call to RW_RDLOCK () attempting ! 1872: * to acquire a lock that is already held by the calling context may ! 1873: * result in deadlock. ! 1874: * ! 1875: * Calls to RD_RDLOCK () should honor the ordering defined by the lock ! 1876: * hierarchy [see RW_ALLOC ()] in order to avoid deadlock. ! 1877: * ! 1878: * Driver-defined sleep locks may be held across calls to this function. ! 1879: * ! 1880: * Driver-defined basic locks and read/write locks may be held across ! 1881: * calls to this function subject to the hierarchy and recursion ! 1882: * restrictions described above. ! 1883: * ! 1884: * When called from interrupt level, the "pl" argument must not specify ! 1885: * a priority below the level at which the interrupt handler is running. ! 1886: * ! 1887: *-SEE_ALSO: ! 1888: * RW_ALLOC (), RW_DEALLOC (), RW_TRYRDLOCK (), RW_TRYWRLOCK (), ! 1889: * RW_UNLOCK (), RW_WRLOCK () ! 1890: */ ! 1891: ! 1892: #if __USE_PROTO__ ! 1893: pl_t (RW_RDLOCK) (rwlock_t * lockp, pl_t pl) ! 1894: #else ! 1895: pl_t ! 1896: RW_RDLOCK __ARGS ((lockp, pl)) ! 1897: rwlock_t * lockp; ! 1898: pl_t pl; ! 1899: #endif ! 1900: { ! 1901: pl_t prev_pl; ! 1902: ushort_t ticket_no; ! 1903: ! 1904: ASSERT (lockp != NULL); ! 1905: ASSERT (splcmp (pl, plhi) <= 0); ! 1906: ! 1907: ! 1908: /* ! 1909: * Enforce minimum-priority assertion, pl >= lockp->rw_min_pl. Note ! 1910: * that splcmp () abstracts subtraction-for-comparison of priority ! 1911: * levels, which explains the form of the assertion. ! 1912: */ ! 1913: ! 1914: ASSERT (splcmp (pl, lockp->rw_min_pl) >= 0); ! 1915: ! 1916: ! 1917: /* ! 1918: * On a uniprocessor, encountering a read/write lock that is already ! 1919: * locked is *always* an error, even on machines with many different ! 1920: * interrupt priority levels. The hierarchy-assertion mechanism ! 1921: * cannot always deal with this, since RW_TRYRDLOCK () can acquire ! 1922: * locks in a different order. ! 1923: * ! 1924: * On a multiprocessor, we can just spin here. ! 1925: */ ! 1926: ! 1927: for (;;) { ! 1928: ! 1929: ! 1930: /* ! 1931: * We block out merely the necessary interrupts at this point; ! 1932: * we don't need a blanket interrupt blockage since the lock ! 1933: * system works as atomically as possible. ! 1934: */ ! 1935: ! 1936: prev_pl = splx (pl); ! 1937: ! 1938: if (ATOMIC_TEST_AND_SET_UCHAR (lockp->rw_locked) == 0) { ! 1939: /* ! 1940: * Now we have acquired the test-and-set part, see ! 1941: * if there are any waiting writers preventing us ! 1942: * from acquiring a shared read lock. ! 1943: * ! 1944: * We read the "lock holder" and "next ticket" entries ! 1945: * in that sequence to be pessimistic, since we can ! 1946: * assume that other CPUs might be writing to this... ! 1947: */ ! 1948: ! 1949: ticket_no = ATOMIC_FETCH_USHORT (lockp->rw_lock_holder); ! 1950: ! 1951: if (ticket_no == ATOMIC_FETCH_USHORT (lockp->rw_next_ticket)) ! 1952: break; ! 1953: ! 1954: /* ! 1955: * The read/write lock is held exclusively, release ! 1956: * the test-and-set sub-lock. ! 1957: */ ! 1958: ! 1959: ATOMIC_CLEAR_UCHAR (lockp->rw_locked); ! 1960: } ! 1961: ! 1962: /* ! 1963: * We can return the interrupt priority to whatever it was ! 1964: * upon entry to this routine since we can't acquire the ! 1965: * initial test-and-set lock. ! 1966: */ ! 1967: ! 1968: (void) splx (prev_pl); ! 1969: ! 1970: #ifdef __UNIPROCESSOR__ ! 1971: cmn_err (CE_PANIC, "RW_RDLOCK : deadlock!"); ! 1972: #elif defined (__TEST_AND_TEST__) ! 1973: /* ! 1974: * In order to reduce contention on the test-and-set part of ! 1975: * the read/write lock, we defer attempting to acquire that ! 1976: * lock until there appear to be no waiting writers, and until ! 1977: * the test-and-set lock is free before attempting to re- ! 1978: * acquire it. Of course, more sophisticated backoff schemes ! 1979: * might also help this approach. ! 1980: */ ! 1981: ! 1982: do { ! 1983: /* ! 1984: * We use this idiom to ensure that the lock holder ! 1985: * item is read before the next ticket item for ! 1986: * maximum pessimism. ! 1987: */ ! 1988: ! 1989: ticket_no = ATOMIC_FETCH_USHORT (lockp->rw_lock_holder); ! 1990: ! 1991: } while (ticket_no != ! 1992: ATOMIC_FETCH_USHORT (lockp->rw_next_ticket) || ! 1993: ATOMIC_FETCH_UCHAR (lockp->rw_locked) != 0); ! 1994: #endif ! 1995: } ! 1996: ! 1997: ! 1998: /* ! 1999: * There are no waiting writers yet, so acquire the lock in read mode ! 2000: * by incrementing the count of readers. ! 2001: */ ! 2002: ! 2003: ticket_no = ATOMIC_FETCH_USHORT (lockp->rw_readers); ! 2004: ! 2005: if (ATOMIC_FETCH_AND_STORE_USHORT (lockp->rw_readers, ticket_no + 1) ! 2006: != ticket_no) { ! 2007: /* ! 2008: * If we didn't read the same number twice, then the ! 2009: * test-and-set lock protection isn't doing its job. ! 2010: */ ! 2011: ! 2012: cmn_err (CE_PANIC, "RW_RDLOCK : lock increment failure"); ! 2013: } ! 2014: ! 2015: ! 2016: #if 0 ! 2017: #ifdef __UNIPROCESSOR__ ! 2018: if (ticket_no != 0) ! 2019: cmn_err (CE_WARN, "RW_RDLOCK : Recursive read-lock attempt"); ! 2020: #endif ! 2021: #endif ! 2022: ! 2023: /* ! 2024: * And allow other CPUs to try and acquire tickets or increment the ! 2025: * reader count by releasing the test-and-set lock. ! 2026: */ ! 2027: ! 2028: ATOMIC_CLEAR_UCHAR (lockp->rw_locked); ! 2029: ! 2030: ! 2031: /* ! 2032: * Here we check and maintain the hierarchy assertions, and record ! 2033: * any required debugging/statistics information about the lock. ! 2034: */ ! 2035: ! 2036: LOCK_COUNT_HIERARCHY (lockp->rw_hierarchy); ! 2037: ! 2038: TRACE_RW_LOCK (lockp); ! 2039: ! 2040: ! 2041: /* ! 2042: * Now would be an appropriate time to check that the requested level ! 2043: * is >= the current level on entry. Strictly speaking, this does not ! 2044: * have to be true if the processor is currently at base level, but ! 2045: * for now we'll discourage that behaviour. ! 2046: */ ! 2047: ! 2048: ASSERT (splcmp (pl, prev_pl) >= 0); ! 2049: ! 2050: return prev_pl; ! 2051: } ! 2052: ! 2053: ! 2054: /* ! 2055: *-STATUS: ! 2056: * DDI/DKI ! 2057: * ! 2058: *-NAME: ! 2059: * RW_TRYRDLOCK () Try to acquire a read/write lock in read mode. ! 2060: * ! 2061: *-SYNOPSIS: ! 2062: * #include <sys/types.h> ! 2063: * #include <sys/ksynch.h> ! 2064: * ! 2065: * pl_t RW_TRYRDLOCK (rwlock_t * lockp, pl_t pl); ! 2066: * ! 2067: *-ARGUMENTS: ! 2068: * lockp Pointer to the read/write lock to be acquired. ! 2069: * ! 2070: * pl The interrupt priority level to be set while the lock ! 2071: * is held by the caller. Because some implementations ! 2072: * require that interrupts that might attempt to acquire ! 2073: * the lock be blocked on the processor on which the ! 2074: * lock is held, portable drivers must specify a "pl" ! 2075: * value that is sufficient to block out and interrupt ! 2076: * handler that might attempt to acquire this lock. See ! 2077: * the description of RW_ALLOC () for additional ! 2078: * discussion and a list of the valid values for "pl". ! 2079: * Implementations that do not require that the interrupt ! 2080: * priority level be raised during lock acquisition may ! 2081: * choose to ignore this argument. ! 2082: * ! 2083: *-DESCRIPTION: ! 2084: * If the lock specified by "lockp" is immediately available in read mode ! 2085: * (there is not a writer holding the lock and there are no waiting ! 2086: * writers) RW_TRYRDLOCK () sets the interrupt priority level in ! 2087: * accordance with the value specified by "pl" (if required by the ! 2088: * implementation) and acquires the lock in read mode. If the lock is not ! 2089: * immediately available in read mode, the function returns without ! 2090: * acquiring the lock. ! 2091: * ! 2092: *-RETURN VALUE: ! 2093: * If the lock is acquired, RW_TRYRDLOCK () returns the previous ! 2094: * interrupt priority level ("plbase" - "plhi"). If the lock is not ! 2095: * acquired the value "invpl" is returned. ! 2096: * ! 2097: *-LEVEL: ! 2098: * Base or Interrupt. ! 2099: * ! 2100: *-NOTES: ! 2101: * Does not sleep. ! 2102: * ! 2103: * RW_TRYRDLOCK () may be used to acquire a lock in a different order ! 2104: * from the order defined by the lock hierarchy. ! 2105: * ! 2106: * Driver-defined basic locks, read/write locks, and sleep locks may be ! 2107: * held across calls to this function. ! 2108: * ! 2109: * When called from interrupt level, the "pl" argument must not specify ! 2110: * a priority level below the level at which the interrupt handler is ! 2111: * running. ! 2112: * ! 2113: *-SEE_ALSO: ! 2114: * RW_ALLOC (), RW_DEALLOC (), RW_RDLOCK (), RW_TRYWRLOCK (), ! 2115: * RW_UNLOCK (), RW_WRLOCK () ! 2116: */ ! 2117: ! 2118: #if __USE_PROTO__ ! 2119: pl_t (RW_TRYRDLOCK) (rwlock_t * lockp, pl_t pl) ! 2120: #else ! 2121: pl_t ! 2122: RW_TRYRDLOCK __ARGS ((lockp, pl)) ! 2123: rwlock_t * lockp; ! 2124: pl_t pl; ! 2125: #endif ! 2126: { ! 2127: pl_t prev_pl; ! 2128: ushort_t ticket_no; ! 2129: ! 2130: ASSERT (lockp != NULL); ! 2131: ASSERT (splcmp (pl, plhi) <= 0); ! 2132: ! 2133: ! 2134: /* ! 2135: * Enforce minimum-priority assertion, pl >= lockp->bl_min_pl. Note ! 2136: * that splcmp () abstracts subtraction-for-comparison of priority ! 2137: * levels, which explains the form of the assertion. ! 2138: */ ! 2139: ! 2140: ASSERT (splcmp (pl, lockp->rw_min_pl) >= 0); ! 2141: ! 2142: ! 2143: #ifdef __TEST_AND_TEST__ ! 2144: /* ! 2145: * We test the ticket numbers to see whether there is any reason to ! 2146: * even try acquiring the test-and-set lock that forms the ticket ! 2147: * gate. ! 2148: * ! 2149: * We read the "lock holder" and "next ticket" entries in that ! 2150: * sequence to be pessimistic, since we can assume that other CPUs ! 2151: * might be looking at this... ! 2152: */ ! 2153: ! 2154: ticket_no = ATOMIC_FETCH_USHORT (lockp->rw_lock_holder); ! 2155: ! 2156: if (ticket_no != ATOMIC_FETCH_USHORT (lockp->rw_next_ticket)) ! 2157: return invpl; ! 2158: #endif ! 2159: ! 2160: ! 2161: /* ! 2162: * We block out interrupts at this point to allow the following ! 2163: * operations room to do their stuff in, since interrupts at an ! 2164: * inappropriate moment can cause deadlock... of course, if the ! 2165: * definition of the lock is such that interrupts can proceed, then ! 2166: * that's OK, since the lock acquisition is atomic. ! 2167: */ ! 2168: ! 2169: prev_pl = splx (pl); ! 2170: ! 2171: ! 2172: /* ! 2173: * Now would be an appropriate time to check that the requested level ! 2174: * is >= the current level on entry. ! 2175: */ ! 2176: ! 2177: ASSERT (splcmp (pl, prev_pl) >= 0); ! 2178: ! 2179: ! 2180: /* ! 2181: * Test to see whether the lock in question is already taken, and if ! 2182: * not, we take it. We don't spin if this is a ticket lock, since if ! 2183: * the basic lock is taken there is *no way* that the lock will be ! 2184: * free for us immediately. ! 2185: */ ! 2186: ! 2187: if (ATOMIC_TEST_AND_SET_UCHAR (lockp->rw_locked) == 0) { ! 2188: /* ! 2189: * Check the ticket numbers to ensure that the lock *really* ! 2190: * is free. ! 2191: */ ! 2192: ! 2193: ticket_no = ATOMIC_FETCH_USHORT (lockp->rw_next_ticket); ! 2194: ! 2195: if (ticket_no != ATOMIC_FETCH_USHORT (lockp->rw_lock_holder)) { ! 2196: ! 2197: ATOMIC_CLEAR_UCHAR (lockp->rw_locked); ! 2198: goto try_failed; ! 2199: } ! 2200: ! 2201: /* ! 2202: * Now acquire the lock in read mode by incrementing the ! 2203: * count of readers. ! 2204: */ ! 2205: ! 2206: ticket_no = ATOMIC_FETCH_USHORT (lockp->rw_readers); ! 2207: ! 2208: if (ATOMIC_FETCH_AND_STORE_USHORT (lockp->rw_readers, ! 2209: ticket_no + 1) != ticket_no) { ! 2210: /* ! 2211: * If we didn't read the same number twice, then the ! 2212: * test-and-set lock protection isn't doing its job. ! 2213: */ ! 2214: ! 2215: cmn_err (CE_PANIC, "RW_TRYRDLOCK : Increment failure"); ! 2216: } ! 2217: ! 2218: ! 2219: /* ! 2220: * Now we have the lock, we can release the test-and-set lock. ! 2221: */ ! 2222: ! 2223: ATOMIC_CLEAR_UCHAR (lockp->rw_locked); ! 2224: ! 2225: ! 2226: /* ! 2227: * RW_TRYRDLOCK () bypasses the hierarchy-assertion tests, ! 2228: * but we record the maximum acquired hierarchy level for ! 2229: * maximum strictness checking in inner lock attempts. ! 2230: * ! 2231: * We also record debugging and statistics information here ! 2232: * with TRACE_RW_LOCK (). ! 2233: */ ! 2234: ! 2235: LOCK_COUNT_HIERARCHY (lockp->rw_hierarchy); ! 2236: ! 2237: TRACE_RW_LOCK (lockp); ! 2238: ! 2239: ! 2240: /* ! 2241: * All done! Return successfully. ! 2242: */ ! 2243: ! 2244: return prev_pl; ! 2245: } ! 2246: ! 2247: try_failed: ! 2248: /* ! 2249: * We cannot acquire the lock, so reset the priority and exit with ! 2250: * the flag value. ! 2251: */ ! 2252: ! 2253: (void) splx (prev_pl); ! 2254: ! 2255: return invpl; ! 2256: } ! 2257: ! 2258: ! 2259: /* ! 2260: *-STATUS: ! 2261: * DDI/DKI ! 2262: * ! 2263: *-NAME: ! 2264: * RW_TRYWRLOCK () Try to acquire a read/write lock in write mode. ! 2265: * ! 2266: *-SYNOPSIS: ! 2267: * #include <sys/types.h> ! 2268: * #include <sys/ksynch.h> ! 2269: * ! 2270: * pl_t RW_TRYWRLOCK (rwlock_t * lockp, pl_t pl); ! 2271: * ! 2272: *-ARGUMENTS: ! 2273: * lockp Pointer to the read/write lock to be acquired. ! 2274: * ! 2275: * pl The interrupt priority level to be set while the lock ! 2276: * is held by the caller. Because some implementations ! 2277: * require that interrupts that might attempt to acquire ! 2278: * the lock be blocked on the processor on which the ! 2279: * lock is held, portable drivers must specify a "pl" ! 2280: * value that is sufficient to block out and interrupt ! 2281: * handler that might attempt to acquire this lock. See ! 2282: * the description of RW_ALLOC () for additional ! 2283: * discussion and a list of the valid values for "pl". ! 2284: * Implementations that do not require that the interrupt ! 2285: * priority level be raised during lock acquisition may ! 2286: * choose to ignore this argument. ! 2287: * ! 2288: *-DESCRIPTION: ! 2289: * If the lock specified by "lockp" is immediately available in write ! 2290: * mode (no context is holding the lock in read mode or write mode), ! 2291: * RW_TRYWRLOCK () sets the interrupt priority level in accordance with ! 2292: * the value specified by "pl" (if required by the implementation) and ! 2293: * acquires the lock in write mode. If the lock is not immediately ! 2294: * available in write mode, the function returns without acquiring the ! 2295: * lock. ! 2296: * ! 2297: *-RETURN VALUE: ! 2298: * If the lock is acquired, RW_TRYWRLOCK () returns the previous ! 2299: * interrupt priority level ("plbase" - "plhi"). If the lock is not ! 2300: * acquired the value "invpl" is returned. ! 2301: * ! 2302: *-LEVEL: ! 2303: * Base or Interrupt. ! 2304: * ! 2305: *-NOTES: ! 2306: * Does not sleep. ! 2307: * ! 2308: * RW_TRYWRLOCK () may be used to acquire a lock in a different order ! 2309: * from the order defined by the lock hierarchy. ! 2310: * ! 2311: * Driver-defined basic locks, read/write locks, and sleep locks may be ! 2312: * held across calls to this function. ! 2313: * ! 2314: * When called from interrupt level, the "pl" argument must not specify ! 2315: * a priority level below the level at which the interrupt handler is ! 2316: * running. ! 2317: * ! 2318: *-SEE_ALSO: ! 2319: * RW_ALLOC (), RW_DEALLOC (), RW_RDLOCK (), RW_TRYRDLOCK (), ! 2320: * RW_UNLOCK (), RW_WRLOCK () ! 2321: */ ! 2322: ! 2323: #if __USE_PROTO__ ! 2324: pl_t (RW_TRYWRLOCK) (rwlock_t * lockp, pl_t pl) ! 2325: #else ! 2326: pl_t ! 2327: RW_TRYWRLOCK __ARGS ((lockp, pl)) ! 2328: rwlock_t * lockp; ! 2329: pl_t pl; ! 2330: #endif ! 2331: { ! 2332: pl_t prev_pl; ! 2333: ushort_t ticket_no; ! 2334: ! 2335: ASSERT (lockp != NULL); ! 2336: ASSERT (splcmp (pl, plhi) <= 0); ! 2337: ! 2338: ! 2339: /* ! 2340: * Enforce minimum-priority assertion, pl >= lockp->bl_min_pl. Note ! 2341: * that splcmp () abstracts subtraction-for-comparison of priority ! 2342: * levels, which explains the form of the assertion. ! 2343: */ ! 2344: ! 2345: ASSERT (splcmp (pl, lockp->rw_min_pl) >= 0); ! 2346: ! 2347: ! 2348: #ifdef __TEST_AND_TEST__ ! 2349: /* ! 2350: * We test the ticket numbers and reader count to see whether there ! 2351: * is any reason to even try acquiring the test-and-set lock that ! 2352: * forms the ticket gate. ! 2353: * ! 2354: * We read the "lock holder" and "next ticket" entries in that ! 2355: * sequence to be pessimistic, since we can assume that other CPUs ! 2356: * might be looking at this... ! 2357: */ ! 2358: ! 2359: ticket_no = ATOMIC_FETCH_USHORT (lockp->rw_lock_holder); ! 2360: ! 2361: if (ticket_no != ATOMIC_FETCH_USHORT (lockp->rw_next_ticket) || ! 2362: ATOMIC_FETCH_USHORT (lockp->rw_readers) != 0) ! 2363: return invpl; ! 2364: #endif ! 2365: ! 2366: ! 2367: /* ! 2368: * We block out interrupts at this point to allow the following ! 2369: * operations room to do their stuff in, since interrupts at an ! 2370: * inappropriate moment can cause deadlock... of course, if the ! 2371: * definition of the lock is such that interrupts can proceed, then ! 2372: * that's OK, since the lock acquisition is atomic. ! 2373: */ ! 2374: ! 2375: prev_pl = splx (pl); ! 2376: ! 2377: ! 2378: /* ! 2379: * Now would be an appropriate time to check that the requested level ! 2380: * is >= the current level on entry. ! 2381: */ ! 2382: ! 2383: ASSERT (splcmp (pl, prev_pl) >= 0); ! 2384: ! 2385: ! 2386: /* ! 2387: * Test to see whether the lock in question is already taken, and if ! 2388: * not, we take it. We don't spin if this is a ticket lock, since if ! 2389: * the basic lock is taken there is *no way* that the lock will be ! 2390: * free for us immediately. ! 2391: */ ! 2392: ! 2393: if (ATOMIC_TEST_AND_SET_UCHAR (lockp->rw_locked) == 0) { ! 2394: /* ! 2395: * Check the ticket numbers and the reader count to ensure ! 2396: * that the lock *really* is free. ! 2397: */ ! 2398: ! 2399: ticket_no = ATOMIC_FETCH_USHORT (lockp->rw_next_ticket); ! 2400: ! 2401: if (ticket_no != ATOMIC_FETCH_USHORT (lockp->rw_lock_holder) || ! 2402: ATOMIC_FETCH_USHORT (lockp->rw_readers) != 0) { ! 2403: ! 2404: ATOMIC_CLEAR_UCHAR (lockp->rw_locked); ! 2405: goto try_failed; ! 2406: } ! 2407: ! 2408: /* ! 2409: * Now acquire the lock in write mode by incrementing the ! 2410: * ticket counter. ! 2411: */ ! 2412: ! 2413: if (ATOMIC_FETCH_AND_STORE_USHORT (lockp->rw_next_ticket, ! 2414: ticket_no + 1) != ticket_no) { ! 2415: /* ! 2416: * If we didn't read the same number twice, then the ! 2417: * test-and-set lock protection isn't doing its job. ! 2418: */ ! 2419: ! 2420: cmn_err (CE_PANIC, "RW_TRYWRLOCK : Increment failure"); ! 2421: } ! 2422: ! 2423: ! 2424: /* ! 2425: * Now we have the lock, we can release the test-and-set lock. ! 2426: */ ! 2427: ! 2428: ATOMIC_CLEAR_UCHAR (lockp->rw_locked); ! 2429: ! 2430: ! 2431: /* ! 2432: * RW_TRYWRLOCK () bypasses the hierarchy-assertion tests, ! 2433: * but we record the maximum acquired hierarchy level for ! 2434: * maximum strictness checking in inner lock attempts. ! 2435: * ! 2436: * We also record and debugging and statistics information ! 2437: * here with TRACE_RW_LOCK (). ! 2438: */ ! 2439: ! 2440: LOCK_COUNT_HIERARCHY (lockp->rw_hierarchy); ! 2441: ! 2442: TRACE_RW_LOCK (lockp); ! 2443: ! 2444: ! 2445: /* ! 2446: * All done! Return success... ! 2447: */ ! 2448: ! 2449: return prev_pl; ! 2450: } ! 2451: ! 2452: try_failed: ! 2453: /* ! 2454: * We cannot acquire the lock, so reset the priority and exit with ! 2455: * the flag value. ! 2456: */ ! 2457: ! 2458: (void) splx (prev_pl); ! 2459: ! 2460: return invpl; ! 2461: } ! 2462: ! 2463: ! 2464: /* ! 2465: *-STATUS: ! 2466: * DDI/DKI ! 2467: * ! 2468: *-NAME: ! 2469: * RW_UNLOCK () Release a read/write lock. ! 2470: * ! 2471: *-SYNOPSIS: ! 2472: * #include <sys/types.h> ! 2473: * #include <sys/ksynch.h> ! 2474: * ! 2475: * pl_t RW_UNLOCK (rwlock_t * lockp, pl_t pl); ! 2476: * ! 2477: *-ARGUMENTS: ! 2478: * lockp Pointer to the read/write lock to be released. ! 2479: * ! 2480: * pl The interrupt priority level to be set after releasing ! 2481: * the lock. See the description of the "min_pl" argument ! 2482: * to RW_ALLOC () for a list of the valid values for ! 2483: * "pl". If lock calls are not being nested or if the ! 2484: * caller is unlocking in the reverse order that locks ! 2485: * were acquired, the "pl" argument will typically be the ! 2486: * value that was returned from the corresponding call to ! 2487: * acquire the lock. The caller may need to specify a ! 2488: * different value for "pl" if nested locks are being ! 2489: * released in some order other that the reverse order of ! 2490: * acquisition, so as to ensure that the interrupt ! 2491: * priority level is kept sufficiently high to block ! 2492: * interrupt code that might attempt to acquire locks ! 2493: * which are still held. Although portable drivers must ! 2494: * always specify an appropriate "pl" argument, ! 2495: * implementations which do not require that the ! 2496: * interrupt priority level be raised during lock ! 2497: * acquisition may choose to ignore this argument. ! 2498: * ! 2499: *-DESCRIPTION: ! 2500: * RW_UNLOCK () releases the basic lock specified by "lockp" and then ! 2501: * sets the interrupt priority level in accordance with the value ! 2502: * specified by "pl" (if required by the implementation). ! 2503: * ! 2504: *-RETURN VALUE: ! 2505: * None. ! 2506: * ! 2507: *-LEVEL: ! 2508: * Base or Interrupt. ! 2509: * ! 2510: *-NOTES: ! 2511: * Does not sleep. ! 2512: * ! 2513: * Driver-defined basic locks, read/write locks, and sleep locks may be ! 2514: * held across calls to this function. ! 2515: * ! 2516: *-SEE_ALSO: ! 2517: * RW_ALLOC (), RW_DEALLOC (), RW_RDLOCK (), RW_TRYRDLOCK (), ! 2518: * RW_TRYWRLOCK (), RW_WRLOCK () ! 2519: */ ! 2520: ! 2521: #if __USE_PROTO__ ! 2522: void (RW_UNLOCK) (rwlock_t * lockp, pl_t pl) ! 2523: #else ! 2524: void ! 2525: RW_UNLOCK __ARGS ((lockp, pl)) ! 2526: rwlock_t * lockp; ! 2527: pl_t pl; ! 2528: #endif ! 2529: { ! 2530: ushort_t ticket_no; ! 2531: ! 2532: ASSERT (lockp != NULL); ! 2533: ASSERT (splcmp (plbase, pl) <= 0 && splcmp (pl, plhi) <= 0); ! 2534: ! 2535: ! 2536: /* ! 2537: * Undo whatever the hierarchy-assertion and debugging/statistics ! 2538: * mechanisms do. ! 2539: */ ! 2540: ! 2541: LOCK_FREE_HIERARCHY (lockp->rw_hierarchy); ! 2542: ! 2543: UNTRACE_RW_LOCK (lockp); ! 2544: ! 2545: ! 2546: /* ! 2547: * Now release the lock... since writers have to wait for all readers ! 2548: * to release the lock, if there any readers then we are releasing ! 2549: * in read mode. ! 2550: */ ! 2551: ! 2552: if (ATOMIC_FETCH_USHORT (lockp->rw_readers) != 0) { ! 2553: /* ! 2554: * In order to safely decrement the reader count, we have to ! 2555: * acquire the test-and-set lock in the absence of an ! 2556: * atomic decrement facility. ! 2557: */ ! 2558: ! 2559: while (ATOMIC_TEST_AND_SET_UCHAR (lockp->rw_locked) != 0) { ! 2560: ! 2561: #ifdef __UNIPROCESSOR__ ! 2562: cmn_err (CE_PANIC, "RW_UNLOCK : deadlock!"); ! 2563: #elif defined (__TEST_AND_TEST__) ! 2564: /* ! 2565: * To reduce contention, we wait until the test-and- ! 2566: * set lock is free before attempting to re-acquire ! 2567: * it. ! 2568: */ ! 2569: ! 2570: while (ATOMIC_FETCH_UCHAR (lockp->rw_locked) != 0) ! 2571: /* DO NOTHING */; ! 2572: #endif ! 2573: } ! 2574: ! 2575: ticket_no = ATOMIC_FETCH_USHORT (lockp->rw_readers); ! 2576: ! 2577: if (ATOMIC_FETCH_AND_STORE_USHORT (lockp->rw_readers, ! 2578: ticket_no - 1) != ticket_no ) { ! 2579: /* ! 2580: * If we didn't read the same number twice, then the ! 2581: * test-and-set lock protection isn't doing its job. ! 2582: */ ! 2583: ! 2584: cmn_err (CE_PANIC, "RW_UNLOCK : Decrement failure"); ! 2585: } ! 2586: ! 2587: /* ! 2588: * Now free the test-and-set lock. ! 2589: */ ! 2590: ! 2591: ATOMIC_CLEAR_UCHAR (lockp->rw_locked); ! 2592: ! 2593: } else if (ticket_no = ATOMIC_FETCH_USHORT (lockp->rw_lock_holder), ! 2594: ticket_no != ATOMIC_FETCH_USHORT (lockp->rw_next_ticket)) { ! 2595: /* ! 2596: * Release the lock to the next ticket holder or the waiting ! 2597: * readers if there are no waiting ticket-holders. ! 2598: */ ! 2599: ! 2600: ! 2601: if (ATOMIC_FETCH_AND_STORE_USHORT (lockp->rw_lock_holder, ! 2602: ticket_no + 1) != ticket_no ) { ! 2603: /* ! 2604: * If we didn't read the same number twice, then the ! 2605: * test-and-set lock protection isn't doing its job. ! 2606: */ ! 2607: ! 2608: cmn_err (CE_PANIC, "RW_UNLOCK : Increment failure"); ! 2609: } ! 2610: ! 2611: } else ! 2612: cmn_err (CE_PANIC, "RW_UNLOCK : not locked"); ! 2613: ! 2614: ! 2615: /* ! 2616: * And lower out priority level to finish up. ! 2617: */ ! 2618: ! 2619: (void) splx (pl); ! 2620: } ! 2621: ! 2622: ! 2623: /* ! 2624: *-STATUS: ! 2625: * DDI/DKI ! 2626: * ! 2627: *-NAME: ! 2628: * RW_WRLOCK () Acquire a read/write lock in write mode. ! 2629: * ! 2630: *-SYNOPSIS: ! 2631: * #include <sys/types.h> ! 2632: * #include <sys/ksynch.h> ! 2633: * ! 2634: * pl_t RW_WRLOCK (rwlock_t * lockp, pl_t pl); ! 2635: * ! 2636: *-ARGUMENTS: ! 2637: * lockp Pointer to the read/write lock to be acquired. ! 2638: * ! 2639: * pl The interrupt priority level to be set while the lock ! 2640: * is held by the caller. Because some implementations ! 2641: * require that interrupts that might attempt to acquire ! 2642: * the lock be blocked on which the lock is held, ! 2643: * portable drivers must specify a "pl" value that is ! 2644: * sufficient to block out any interrupt handler that ! 2645: * might attempt to acquire this lock. See the ! 2646: * description of the "min_pl" argument to RW_ALLOC () ! 2647: * for additional discussion. Implementations that do ! 2648: * not require that the interrupt priority level be ! 2649: * raised during lock acquisition may choose to ignore ! 2650: * this argument. ! 2651: * ! 2652: *-DESCRIPTION: ! 2653: * RW_WRLOCK () sets the interrupt priority level in accordance with the ! 2654: * value specified by "pl" (if required by the implementation) and ! 2655: * acquires the lock specified by "lockp". If the lock is not currently ! 2656: * available, the caller will wait until the lock is available in write ! 2657: * mode. A read/write lock is available in write mode when the lock is ! 2658: * not held by any context. It is implementation-defined whether the ! 2659: * caller will block during the wait. Some implementations may cause the ! 2660: * caller to spin for the duration of the wait, while on others the ! 2661: * caller may block at some point. ! 2662: * ! 2663: *-RETURN VALUE: ! 2664: * Upon acquiring the lock, RW_WRLOCK () returns the previous interrupt ! 2665: * priority level. ! 2666: * ! 2667: *-LEVEL: ! 2668: * Base or Interrupt. ! 2669: * ! 2670: *-NOTES: ! 2671: * Read/write locks are not recursive. A call to RW_WRLOCK () attempting ! 2672: * to acquire a lock that is already held by the calling context may ! 2673: * result in deadlock. ! 2674: * ! 2675: * Calls to RD_WRLOCK () should honor the ordering defined by the lock ! 2676: * hierarchy [see RW_ALLOC ()] in order to avoid deadlock. ! 2677: * ! 2678: * Driver-defined sleep locks may be held across calls to this function. ! 2679: * ! 2680: * Driver-defined basic locks and read/write locks may be held across ! 2681: * calls to this function subject to the hierarchy and recursion ! 2682: * restrictions described above. ! 2683: * ! 2684: * When called from interrupt level, the "pl" argument must not specify ! 2685: * a priority below the level at which the interrupt handler is running. ! 2686: * ! 2687: *-SEE_ALSO: ! 2688: * RW_ALLOC (), RW_DEALLOC (), RW_RDLOCK (), RW_TRYRDLOCK (), ! 2689: * RW_TRYWRLOCK (), RW_UNLOCK () ! 2690: */ ! 2691: ! 2692: #if __USE_PROTO__ ! 2693: pl_t (RW_WRLOCK) (rwlock_t * lockp, pl_t pl) ! 2694: #else ! 2695: pl_t ! 2696: RW_WRLOCK __ARGS ((lockp, pl)) ! 2697: rwlock_t * lockp; ! 2698: pl_t pl; ! 2699: #endif ! 2700: { ! 2701: pl_t prev_pl; ! 2702: ushort_t ticket_no; ! 2703: ! 2704: ASSERT (lockp != NULL); ! 2705: ASSERT (splcmp (pl, plhi) <= 0); ! 2706: ! 2707: ! 2708: /* ! 2709: * Enforce minimum-priority assertion, pl >= lockp->rw_min_pl. Note ! 2710: * that splcmp () abstracts subtraction-for-comparison of priority ! 2711: * levels, which explains the form of the assertion. ! 2712: */ ! 2713: ! 2714: ASSERT (splcmp (pl, lockp->rw_min_pl) >= 0); ! 2715: ! 2716: ! 2717: /* ! 2718: * On a uniprocessor, encountering a read/write lock that is already ! 2719: * locked is *always* an error, even on machines with many different ! 2720: * interrupt priority levels. The hierarchy-assertion mechanism ! 2721: * cannot always deal with this, since RW_TRYRDLOCK () can acquire ! 2722: * locks in a different order. ! 2723: * ! 2724: * On a multiprocessor, we can just spin here. Note that since LOCK () ! 2725: * is defined as requiring values of "pl" greater than the current ! 2726: * interrupt priority level, the use of splraise () by ! 2727: * TEST_AND_SET_LOCK () is legitimate. ! 2728: */ ! 2729: ! 2730: prev_pl = TEST_AND_SET_LOCK (lockp->rw_locked, pl, "RW_WRLOCK"); ! 2731: ! 2732: ! 2733: /* ! 2734: * Take a ticket... ! 2735: */ ! 2736: ! 2737: ticket_no = ATOMIC_FETCH_USHORT (lockp->rw_next_ticket); ! 2738: ! 2739: if (ATOMIC_FETCH_AND_STORE_USHORT (lockp->rw_next_ticket, ! 2740: ticket_no + 1) != ticket_no) { ! 2741: /* ! 2742: * If we didn't read the same number twice, then the ! 2743: * test-and-set lock protection isn't doing its job. ! 2744: */ ! 2745: ! 2746: cmn_err (CE_PANIC, "RW_WRLOCK : ticket increment failure"); ! 2747: } ! 2748: ! 2749: ! 2750: /* ! 2751: * Allow other CPUs access to the lock data structures by releasing ! 2752: * the test-and-set lock. ! 2753: */ ! 2754: ! 2755: ATOMIC_CLEAR_UCHAR (lockp->rw_locked); ! 2756: ! 2757: ! 2758: /* ! 2759: * Now, let's wait for our number to come up and for all the readers ! 2760: * to release their shared locks. ! 2761: */ ! 2762: ! 2763: while (ATOMIC_FETCH_USHORT (lockp->rw_lock_holder) != ticket_no || ! 2764: ATOMIC_FETCH_USHORT (lockp->rw_readers) != 0) { ! 2765: /* ! 2766: * At this point we might want to implement some form of ! 2767: * proportional backoff to reduce memory traffic. Later. ! 2768: * ! 2769: * Another hot contender for this spot is detecting failures ! 2770: * on other CPUs... ! 2771: */ ! 2772: ! 2773: #ifdef __UNIPROCESSOR__ ! 2774: cmn_err (CE_PANIC, "RW_WRLOCK : deadlock on ticket"); ! 2775: #endif ! 2776: } ! 2777: ! 2778: ! 2779: /* ! 2780: * Now would be an appropriate time to check that the requested level ! 2781: * is >= the current level on entry. Strictly speaking, this does not ! 2782: * have to be true if the processor is currently at base level, but ! 2783: * for now we'll discourage that behaviour. ! 2784: */ ! 2785: ! 2786: ASSERT (splcmp (pl, prev_pl) >= 0); ! 2787: ! 2788: ! 2789: /* ! 2790: * Test the lock-acquisition-hierarchy assertions. ! 2791: */ ! 2792: ! 2793: ASSERT (ddi_cpu_data ()->dc_max_hierarchy < lockp->rw_hierarchy); ! 2794: ! 2795: LOCK_COUNT_HIERARCHY (lockp->rw_hierarchy); ! 2796: ! 2797: ! 2798: /* ! 2799: * Since it may be useful for post-mortem debugging to record some ! 2800: * information about the context which acquired the lock, we defer ! 2801: * to some generic recorder function or macro. ! 2802: * ! 2803: * Note that in general it does not appear to be possible to make ! 2804: * detailed assertions about the relation between the contexts in ! 2805: * which a lock is acquired and/or released. In particular, it ! 2806: * might be possible for a basic lock to be tied to some device ! 2807: * hardware-related operation where a lock might be acquired on one ! 2808: * CPU and released on another. ! 2809: * ! 2810: * In addition, any lock statistics are kept by this operation. ! 2811: */ ! 2812: ! 2813: TRACE_RW_LOCK (lockp); ! 2814: ! 2815: return prev_pl; ! 2816: } ! 2817: ! 2818: ! 2819: /* ! 2820: *-STATUS: ! 2821: * DDI/DKI ! 2822: * ! 2823: *-NAME: ! 2824: * SLEEP_ALLOC () Allocate and initialize a sleep lock. ! 2825: * ! 2826: *-SYNOPSIS: ! 2827: * #include <sys/types.h> ! 2828: * #include <sys/kmem.h> ! 2829: * #include <sys/ksynch.h> ! 2830: * ! 2831: * sleep_t * SLEEP_ALLOC (int arg, lkinfo_t * lkinfop, int flag); ! 2832: * ! 2833: *-ARGUMENTS: ! 2834: * arg Placeholder for future use. "arg" must be equal to ! 2835: * zero. ! 2836: * ! 2837: * ! 2838: * lkinfop Pointer to a lkinfo structure. The lk_name member of ! 2839: * the lkinfo structure points to a character string ! 2840: * defining a name that will be associated with the lock ! 2841: * for the purposes of statistics gathering. The name ! 2842: * should begin with the driver prefix and should be ! 2843: * unique to the lock or group of locks for which the ! 2844: * driver wishes to collect a uniquely identifiable set ! 2845: * of statistics (ie, if a given name is shared by a ! 2846: * group of locks, the statistics of the individual locks ! 2847: * within the group will not be uniquely identifiable). ! 2848: * ! 2849: * The only bit flag currently specified within the ! 2850: * "lk_flags" member of the lkinfo structure is the ! 2851: * LK_NOSTATS flag, which specifies that statistics are ! 2852: * not to be collected for this particular lock. ! 2853: * ! 2854: * A given lkinfo structure may be shared among multiple ! 2855: * sleep but a lkinfo structure may not be shared between ! 2856: * a sleep lock and a basic or read/write lock. The ! 2857: * called must ensure that the "lk_pad" member of the ! 2858: * "lkinfo" structure is zeroed out before passing it to ! 2859: * SLEEP_ALLOC (). ! 2860: * ! 2861: * flag Specifies whether the caller is willing to sleep ! 2862: * waiting for memory. If "flag" is set to KM_SLEEP, the ! 2863: * caller will sleep if necessary until sufficient memory ! 2864: * is available. If "flag" is set to KM_NOSLEEP, the ! 2865: * caller will not sleep, but SLEEP_ALLOC () will return ! 2866: * NULL if sufficient memory is not immediately ! 2867: * available. ! 2868: * ! 2869: *-DESCRIPTION: ! 2870: * SLEEP_ALLOC () dynamically allocates and initialises an instance of a ! 2871: * sleep lock. The lock is initialised to the unlocked state. ! 2872: * ! 2873: *-RETURN VALUE: ! 2874: * Upon successful completion, SLEEP_ALLOC () returns a pointer to the ! 2875: * newly allocated lock. If KM_NOSLEEP is specified and sufficient ! 2876: * memory is not immediately available, SLEEP_ALLOC () returns a NULL ! 2877: * pointer. ! 2878: * ! 2879: *-LEVEL: ! 2880: * Base only if "flag" is set to KM_SLEEP. Base or Interrupt if "flag" is ! 2881: * set to KM_NOSLEEP. ! 2882: * ! 2883: *-NOTES: ! 2884: * May sleep if "flag" is set to KM_NOSLEEP. ! 2885: * ! 2886: * Driver-defined basic locks and read/write locks may be held across ! 2887: * calls to this function if "flag" is set to KM_NOSLEEP but may not be ! 2888: * held if "flag" is KM_SLEEP. ! 2889: * ! 2890: * Driver-defined sleep locks may be held across calls to this function ! 2891: * regardless of the value of "flag". ! 2892: * ! 2893: *-SEE_ALSO: ! 2894: * SLEEP_DEALLOC (), SLEEP_LOCK (), SLEEP_LOCK_SIG (), ! 2895: * SLEEP_LOCKAVAIL (), SLEEP_LOCKOWNED (), SLEEP_TRYLOCK (), ! 2896: * SLEEP_UNLOCK (), lkinfo ! 2897: */ ! 2898: ! 2899: #if __USE_PROTO__ ! 2900: sleep_t * (SLEEP_ALLOC) (int arg, lkinfo_t * lkinfop, int flag) ! 2901: #else ! 2902: sleep_t * ! 2903: SLEEP_ALLOC __ARGS ((arg, lkinfop, flag)) ! 2904: int arg; ! 2905: lkinfo_t * lkinfop; ! 2906: int flag; ! 2907: #endif ! 2908: { ! 2909: sleep_t * lockp; ! 2910: ! 2911: ASSERT (arg == 0); ! 2912: ASSERT (flag == KM_SLEEP || flag == KM_NOSLEEP); ! 2913: ASSERT (lkinfop != NULL); ! 2914: ! 2915: ! 2916: /* ! 2917: * Allocate and initialise the data, possibly waiting for enough ! 2918: * memory to become available. ! 2919: */ ! 2920: ! 2921: if ((lockp = (sleep_t *) _lock_malloc (sizeof (* lockp), ! 2922: flag)) != NULL) { ! 2923: INIT_LNODE (& lockp->sl_node, lkinfop, & sleep_locks, flag); ! 2924: PLIST_INIT (lockp->sl_plist); ! 2925: } ! 2926: ! 2927: return lockp; ! 2928: } ! 2929: ! 2930: ! 2931: /* ! 2932: *-STATUS: ! 2933: * DDI/DKI ! 2934: * ! 2935: *-NAME: ! 2936: * SLEEP_DEALLOC () Deallocate an instance of a sleep lock. ! 2937: * ! 2938: *-SYNOPSIS: ! 2939: * #include <sys/ksynch.h> ! 2940: * ! 2941: * void SLEEP_DEALLOC (sleep_t * lockp); ! 2942: * ! 2943: *-ARGUMENTS: ! 2944: * lockp Pointer to the sleep lock to be deallocated. ! 2945: * ! 2946: *-DESCRIPTION: ! 2947: * SLEEP_DEALLOC () deallocates the sleep lock specified by "lockp". ! 2948: * ! 2949: *-RETURN VALUE: ! 2950: * None. ! 2951: * ! 2952: *-LEVEL: ! 2953: * Base or Interrupt. ! 2954: * ! 2955: *-NOTES: ! 2956: * Does not sleep. ! 2957: * ! 2958: * Attempting to deallocate a lock that is currently locked or is being ! 2959: * waited for is an error and will result in undefined behavior. ! 2960: * ! 2961: * Driver-defined basic locks, read/write locks, and sleep locks (other ! 2962: * than the one being deallocated) may be held across calls to this ! 2963: * function. ! 2964: * ! 2965: *-SEE_ALSO: ! 2966: * SLEEP_ALLOC (), SLEEP_LOCK (), SLEEP_LOCK_SIG (), SLEEP_LOCKAVAIL (), ! 2967: * SLEEP_LOCKOWNED (), SLEEP_TRYLOCK (), SLEEP_UNLOCK () ! 2968: */ ! 2969: ! 2970: #if __USE_PROTO__ ! 2971: void (SLEEP_DEALLOC) (sleep_t * lockp) ! 2972: #else ! 2973: void ! 2974: SLEEP_DEALLOC __ARGS ((lockp)) ! 2975: sleep_t * lockp; ! 2976: #endif ! 2977: { ! 2978: ASSERT (lockp != NULL); ! 2979: ! 2980: /* ! 2981: * Remove from the list of all sleep locks and free any statistics ! 2982: * buffer space before freeing the lock itself. ! 2983: */ ! 2984: ! 2985: PLIST_DESTROY (lockp->sl_plist); ! 2986: ! 2987: FREE_LNODE (& lockp->sl_node, & sleep_locks); ! 2988: ! 2989: _lock_free (lockp, sizeof (* lockp)); ! 2990: } ! 2991: ! 2992: ! 2993: /* ! 2994: *-STATUS: ! 2995: * DDI/DKI ! 2996: * ! 2997: *-NAME: ! 2998: * SLEEP_LOCK () Acquire a sleep lock. ! 2999: * ! 3000: *-SYNOPSIS: ! 3001: * #include <sys/ksynch.h> ! 3002: * ! 3003: * void SLEEP_LOCK (sleep_t * lockp, int priority); ! 3004: * ! 3005: *-ARGUMENTS: ! 3006: * lockp Pointer to the sleep lock to be acquired. ! 3007: * ! 3008: * priority A hint to the scheduling policy as to the relative ! 3009: * priority the caller wishes to be assigned while ! 3010: * running in the kernel after waking up. The valid ! 3011: * values for this argument are as follows: ! 3012: * ! 3013: * pridisk Priority appropriate for disk driver ! 3014: * prinet Priority appropriate for network driver. ! 3015: * pritty Priority appropriate for terminal driver. ! 3016: * pritape Priority appropriate for tape driver. ! 3017: * prihi High priority. ! 3018: * primed Medium priority. ! 3019: * prilo Low priority. ! 3020: * ! 3021: * Drivers may use these values to request a priority ! 3022: * appropriate to a given type of device or to request a ! 3023: * priority that is high, medium or low relative to other ! 3024: * activities within the kernel. ! 3025: * ! 3026: * It is also permissible to specify positive or negative ! 3027: * offsets from the values defined above. Positive ! 3028: * offsets result in favourable priority. The maximum ! 3029: * allowable offset in all cases is 3 (eg. pridisk+3 ! 3030: * and pridisk-3 are valid values by pridisk+4 and ! 3031: * pridisk-4 are not valid). Offsets can be useful in ! 3032: * defining the relative importance of different locks or ! 3033: * resources that may be hald by a given driver. In ! 3034: * general, a higher relative priority should be used ! 3035: * when the caller is attempting to acquire a highly- ! 3036: * contended lock or resource, or when the caller is ! 3037: * already holding one or more locks or kernel resources ! 3038: * upon entry to SLEEP_LOCK (). ! 3039: * ! 3040: * The exact semantics of the "priority" argument is ! 3041: * specific to the scheduling class of the caller, and ! 3042: * some scheduling classes may choose to ignore the ! 3043: 8 argument for the purposes of assigning a scheduling ! 3044: * priority. ! 3045: * ! 3046: *-DESCRIPTION: ! 3047: * SLEEP_LOCK () acquires the sleep lock specified by "lockp". If the ! 3048: * lock is not immediately available, the caller is put to sleep (the ! 3049: * caller's execution is suspended and other processes may be scheduled) ! 3050: * until the lock becomes available to the caller, at which point the ! 3051: * caller wakes up and returns with the lock held. ! 3052: * ! 3053: * The caller will not be interrupted by signals while sleeping inside ! 3054: * SLEEP_LOCK (). ! 3055: * ! 3056: *-RETURN VALUE: ! 3057: * None. ! 3058: * ! 3059: *-LEVEL: ! 3060: * Base level only. ! 3061: * ! 3062: *-NOTES: ! 3063: * May sleep. ! 3064: * ! 3065: * Sleep locks are not recursive. A call to SLEEP_LOCK () attempting to ! 3066: * acquire a lock that is currently held by the calling context will ! 3067: * result in deadlock. ! 3068: * ! 3069: * Driver-defined basic locks and read/write locks may not be held across ! 3070: * calls to this function. ! 3071: * ! 3072: * Driver-defined sleep locks may be held across calls to this function ! 3073: * subject to the recursion restrictions described above. ! 3074: * ! 3075: *-SEE_ALSO: ! 3076: * SLEEP_ALLOC (), SLEEP_DEALLOC (), SLEEP_LOCK_SIG (), ! 3077: * SLEEP_LOCKAVAIL (), SLEEP_LOCKOWNED (), SLEEP_TRYLOCK (), ! 3078: * SLEEP_UNLOCK () ! 3079: */ ! 3080: ! 3081: #if __USE_PROTO__ ! 3082: void (SLEEP_LOCK) (sleep_t * lockp, int priority) ! 3083: #else ! 3084: void ! 3085: SLEEP_LOCK __ARGS ((lockp, priority)) ! 3086: sleep_t * lockp; ! 3087: int priority; ! 3088: #endif ! 3089: { ! 3090: pl_t prev_pl; ! 3091: ! 3092: ASSERT (lockp != NULL); ! 3093: ASSERT_BASE_LEVEL (); ! 3094: ! 3095: ! 3096: /* ! 3097: * Take the path of least resistance; if the sleep lock is not ! 3098: * currently held, then just acquire it and get out. ! 3099: */ ! 3100: ! 3101: if (ATOMIC_TEST_AND_SET_UCHAR (lockp->sl_locked) == 0) { ! 3102: /* ! 3103: * We successfully acquired it! Just leave after writing in ! 3104: * the info for SLEEP_LOCKAVAIL (). ! 3105: */ ! 3106: ! 3107: lockp->sl_holder = PROC_HANDLE (); ! 3108: return; ! 3109: } ! 3110: ! 3111: ! 3112: /* ! 3113: * OK, we do it the hard way. ! 3114: * ! 3115: * The sleep lock might have been unlocked while we waited to lock ! 3116: * the process list, so we retest the item. ! 3117: */ ! 3118: ! 3119: prev_pl = PLIST_LOCK (lockp->sl_plist, "SLEEP_LOCK"); ! 3120: ! 3121: if (ATOMIC_TEST_AND_SET_UCHAR (lockp->sl_locked) != 0) { ! 3122: /* ! 3123: * No, we need to wait. Note the cast to void... we assume ! 3124: * that MAKE_SLEEPING () will filter out all improper attempts ! 3125: * to wake us. ! 3126: * ! 3127: * We assert that whoever woke us up will have left the sleep ! 3128: * locked in the "locked" state for us. While *we* won't have ! 3129: * the process-list locked, someone else might so we can't ! 3130: * make an assertion on that. ! 3131: */ ! 3132: ! 3133: (void) MAKE_SLEEPING (lockp->sl_plist, priority, ! 3134: SLEEP_NO_SIGNALS); ! 3135: ! 3136: ASSERT (lockp->sl_holder == PROC_HANDLE ()); ! 3137: ASSERT (ATOMIC_FETCH_UCHAR (lockp->sl_locked) != 0); ! 3138: ! 3139: (void) splx (prev_pl); ! 3140: } else { ! 3141: /* ! 3142: * Write the owner information for SLEEP_LOCKOWNED () and ! 3143: * release the test-and-set lock on the process list. ! 3144: */ ! 3145: ! 3146: lockp->sl_holder = PROC_HANDLE (); ! 3147: ! 3148: PLIST_UNLOCK (lockp->sl_plist, prev_pl); ! 3149: } ! 3150: } ! 3151: ! 3152: ! 3153: /* ! 3154: *-STATUS: ! 3155: * DDI/DKI ! 3156: * ! 3157: *-NAME: ! 3158: * SLEEP_LOCKAVAIL () Query whether a sleep lock is available. ! 3159: * ! 3160: *-SYNOPSIS: ! 3161: * #include <sys/types.h> ! 3162: * #include <sys/ksynch.h> ! 3163: * ! 3164: * bool_t SLEEP_LOCKAVAIL (sleep_t * lockp); ! 3165: * ! 3166: *-ARGUMENTS: ! 3167: * lockp Pointer to the sleep lock to be queried. ! 3168: * ! 3169: *-DESCRIPTION: ! 3170: * SLEEP_LOCKAVAIL () returns an indication of whether the sleep lock ! 3171: * specified by "lockp" is currently available. ! 3172: * ! 3173: * The state of the lock may change and the value returned may no longer ! 3174: * be valid by the time the caller sees it. The caller is expected to ! 3175: * understand that this is "stale data" and is either using it as a ! 3176: * heuristic or has arranged for the data to be meaningful by other ! 3177: * means. ! 3178: * ! 3179: *-RETURN VALUE: ! 3180: * SLEEP_LOCKAVAIL () returns TRUE (a non-zero value) if the lock was ! 3181: * available or FALSE (zero) if the lock was not available. ! 3182: * ! 3183: *-LEVEL: ! 3184: * Base or Interrupt. ! 3185: * ! 3186: *-NOTES: ! 3187: * Does not sleep. ! 3188: * ! 3189: * Driver-defined basic locks, read/write locks and sleep locks may be ! 3190: * held across calls to this function. ! 3191: * ! 3192: *-SEE_ALSO: ! 3193: * SLEEP_ALLOC (), SLEEP_DEALLOC (), SLEEP_LOCK (), SLEEP_LOCK_SIG (), ! 3194: * SLEEP_LOCKOWNED (), SLEEP_TRYLOCK (), SLEEP_UNLOCK () ! 3195: */ ! 3196: ! 3197: #if __USE_PROTO__ ! 3198: bool_t (SLEEP_LOCKAVAIL) (sleep_t * lockp) ! 3199: #else ! 3200: bool_t ! 3201: SLEEP_LOCKAVAIL __ARGS ((lockp)) ! 3202: sleep_t * lockp; ! 3203: #endif ! 3204: { ! 3205: ASSERT (lockp != NULL); ! 3206: ! 3207: return ATOMIC_FETCH_UCHAR (lockp->sl_locked) == 0; ! 3208: } ! 3209: ! 3210: ! 3211: /* ! 3212: *-STATUS: ! 3213: * DDI/DKI ! 3214: * ! 3215: *-NAME: ! 3216: * SLEEP_LOCKOWNED () Query whether a sleep lock is held by the caller. ! 3217: * ! 3218: *-SYNOPSIS: ! 3219: * #include <sys/types.h> ! 3220: * #include <sys/ksynch.h> ! 3221: * ! 3222: * bool_t SLEEP_LOCKOWNED (sleep_t * lockp); ! 3223: * ! 3224: *-ARGUMENTS: ! 3225: * lockp Pointer to the sleep lock to be queried. ! 3226: * ! 3227: *-DESCRIPTION: ! 3228: * SLEEP_LOCKOWNED () returns an indication of whether the sleep lock ! 3229: * specified by "lockp" is held by the calling context. ! 3230: * ! 3231: * SLEEP_LOCKOWNED () is intended for use only within ASSERT () ! 3232: * expressions [see ASSERT ()] and other code that is conditionally ! 3233: * compiled under the DEBUG compilation option. The SLEEP_LOCKOWNED () ! 3234: * function is only defined under the DEBUG compilation option, and ! 3235: * therefore calls to SLEEP_LOCKOWNED () will not compile when DEBUG is ! 3236: * not defined. ! 3237: * ! 3238: *-RETURN VALUE: ! 3239: * SLEEP_LOCKOWNED () returns TRUE (a non-zero value) if the lock is ! 3240: * currently held by the calling context or FALSE (zero) if the lock is ! 3241: * not currently held by the calling context. ! 3242: * ! 3243: *-LEVEL: ! 3244: * Base or Interrupt. ! 3245: * ! 3246: *-NOTES: ! 3247: * Does not sleep. ! 3248: * ! 3249: * Driver-defined basic locks, read/write locks and sleep locks may be ! 3250: * held across calls to this function. ! 3251: * ! 3252: *-SEE_ALSO: ! 3253: * SLEEP_ALLOC (), SLEEP_DEALLOC (), SLEEP_LOCK (), SLEEP_LOCK_SIG (), ! 3254: * SLEEP_LOCKAVAIL (), SLEEP_TRYLOCK (), SLEEP_UNLOCK () ! 3255: */ ! 3256: ! 3257: #if __USE_PROTO__ ! 3258: bool_t (SLEEP_LOCKOWNED) (sleep_t * lockp) ! 3259: #else ! 3260: bool_t ! 3261: SLEEP_LOCKOWNED __ARGS ((lockp)) ! 3262: sleep_t * lockp; ! 3263: #endif ! 3264: { ! 3265: ASSERT (lockp != NULL); ! 3266: ! 3267: return lockp->sl_holder == PROC_HANDLE (); ! 3268: } ! 3269: ! 3270: ! 3271: /* ! 3272: *-STATUS: ! 3273: * DDI/DKI ! 3274: * ! 3275: *-NAME: ! 3276: * SLEEP_LOCK_SIG () Acquire a sleep lock. ! 3277: * ! 3278: *-SYNOPSIS: ! 3279: * #include <sys/types.h> ! 3280: * #include <sys/ksynch.h> ! 3281: * ! 3282: * bool_t SLEEP_LOCK_SIG (sleep_t * lockp, int priority); ! 3283: * ! 3284: *-ARGUMENTS: ! 3285: * lockp Pointer to the sleep lock to be acquired. ! 3286: * ! 3287: * priority A hint to the scheduling policy as to the relative ! 3288: * priority the caller wishes to be assigned while ! 3289: * running in the kernel after waking up. The valid ! 3290: * values for this argument are as follows: ! 3291: * ! 3292: * pridisk Priority appropriate for disk driver ! 3293: * prinet Priority appropriate for network driver. ! 3294: * pritty Priority appropriate for terminal driver. ! 3295: * pritape Priority appropriate for tape driver. ! 3296: * prihi High priority. ! 3297: * primed Medium priority. ! 3298: * prilo Low priority. ! 3299: * ! 3300: * Drivers may use these values to request a priority ! 3301: * appropriate to a given type of device or to request a ! 3302: * priority that is high, medium or low relative to other ! 3303: * activities within the kernel. ! 3304: * ! 3305: * It is also permissible to specify positive or negative ! 3306: * offsets from the values defined above. Positive ! 3307: * offsets result in favourable priority. The maximum ! 3308: * allowable offset in all cases is 3 (eg. pridisk+3 ! 3309: * and pridisk-3 are valid values by pridisk+4 and ! 3310: * pridisk-4 are not valid). Offsets can be useful in ! 3311: * defining the relative importance of different locks or ! 3312: * resources that may be hald by a given driver. In ! 3313: * general, a higher relative priority should be used ! 3314: * when the caller is attempting to acquire a highly- ! 3315: * contended lock or resource, or when the caller is ! 3316: * already holding one or more locks or kernel resources ! 3317: * upon entry to SLEEP_LOCK_SIG (). ! 3318: * ! 3319: * The exact semantics of the "priority" argument to the ! 3320: * scheduling class of the caller, and some scheduling ! 3321: * classes may choose to ignore the argument for the ! 3322: * purposes of assigning a scheduling priority. ! 3323: * ! 3324: *-DESCRIPTION: ! 3325: * SLEEP_LOCK_SIG () acquires the sleep lock specified by "lockp". If the ! 3326: * lock is not immediately available, the caller is put to sleep (the ! 3327: * caller's execution is suspended and other processes may be scheduled) ! 3328: * until the lock becomes available to the caller, at which point the ! 3329: * caller wakes up and returns with the lock held. ! 3330: * ! 3331: * SLEEP_LOCK_SIG () may be interrupted by a signal, in which case it may ! 3332: * return early without acquiring the lock. ! 3333: * ! 3334: * If the function is interrupted by a job control stop signal (eg ! 3335: * SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU) which results in the caller ! 3336: * entering a stopped state, the SLEEP_LOCK_SIG () function will ! 3337: * transparently retry the lock operation upon continuing (the caller ! 3338: * will not return without the lock). ! 3339: * ! 3340: * If the function is interrupted by a signal other than a job control ! 3341: * signal, or by a job control signal that does not result in the caller ! 3342: * stopping (because the signal has a non-default disposition), the ! 3343: * SLEEP_LOCK_SIG () function will return early without acquiring the ! 3344: * lock. ! 3345: * ! 3346: *-RETURN VALUE: ! 3347: * SLEEP_LOCK_SIG () returns TRUE (a non-zero value) if the lock is ! 3348: * successfully acquired or FALSE (zero) if the function returned early ! 3349: * because of a signal. ! 3350: * ! 3351: *-LEVEL: ! 3352: * Base level only. ! 3353: * ! 3354: *-NOTES: ! 3355: * May sleep. ! 3356: * ! 3357: * Sleep locks are not recursive. A call to SLEEP_LOCK_SIG () attempting ! 3358: * to acquire a lock that is currently held by the calling context will ! 3359: * result in deadlock. ! 3360: * ! 3361: * Driver-defined basic locks and read/write locks may not be held across ! 3362: * calls to this function. ! 3363: * ! 3364: * Driver-defined sleep locks may be held across calls to this function ! 3365: * subject to the recursion restrictions described above. ! 3366: * ! 3367: *-SEE_ALSO: ! 3368: * SLEEP_ALLOC (), SLEEP_DEALLOC (), SLEEP_LOCK (), SLEEP_LOCKAVAIL (), ! 3369: * SLEEP_LOCKOWNED (), SLEEP_TRYLOCK (), SLEEP_UNLOCK (), signals ! 3370: */ ! 3371: ! 3372: #if __USE_PROTO__ ! 3373: bool_t (SLEEP_LOCK_SIG) (sleep_t * lockp, int priority) ! 3374: #else ! 3375: bool_t ! 3376: SLEEP_LOCK_SIG __ARGS ((lockp, priority)) ! 3377: sleep_t * lockp; ! 3378: int priority; ! 3379: #endif ! 3380: { ! 3381: pl_t prev_pl; ! 3382: ! 3383: ASSERT (lockp != NULL); ! 3384: ASSERT_BASE_LEVEL (); ! 3385: ! 3386: /* ! 3387: * Take the path of least resistance; if the sleep lock is not ! 3388: * currently held, then just acquire it and get out. ! 3389: */ ! 3390: ! 3391: if (ATOMIC_TEST_AND_SET_UCHAR (lockp->sl_locked) == 0) { ! 3392: /* ! 3393: * We successfully acquired it! Just leave after writing in ! 3394: * the info for SLEEP_LOCKAVAIL (). ! 3395: */ ! 3396: ! 3397: lockp->sl_holder = PROC_HANDLE (); ! 3398: return TRUE; ! 3399: } ! 3400: ! 3401: ! 3402: /* ! 3403: * OK, we do it the hard way. ! 3404: * ! 3405: * The sleep lock might have been unlocked while we waited to lock ! 3406: * the process list, so we retest that member. ! 3407: */ ! 3408: ! 3409: prev_pl = PLIST_LOCK (lockp->sl_plist, "SLEEP_LOCK"); ! 3410: ! 3411: if (ATOMIC_TEST_AND_SET_UCHAR (lockp->sl_locked) != 0) { ! 3412: /* ! 3413: * No, we need to wait. ! 3414: */ ! 3415: ! 3416: if (MAKE_SLEEPING (lockp->sl_plist, priority, ! 3417: SLEEP_INTERRUPTIBLE) == PROCESS_SIGNALLED) { ! 3418: /* ! 3419: * We were signalled. MAKE_SLEEPING () will have ! 3420: * released the test-and-set lock on the process list, ! 3421: * so we just have to reset the interrupt priority ! 3422: * level and return an indication to the caller. ! 3423: */ ! 3424: ! 3425: (void) splx (prev_pl); ! 3426: ! 3427: return FALSE; ! 3428: } ! 3429: ! 3430: /* ! 3431: * We assert that whoever woke us up will have left the sleep ! 3432: * locked in the "locked" state for us. While *we* won't have ! 3433: * the process-list locked, someone else might so we can't ! 3434: * make an assertion on that. ! 3435: */ ! 3436: ! 3437: ASSERT (lockp->sl_holder == PROC_HANDLE ()); ! 3438: ASSERT (ATOMIC_FETCH_UCHAR (lockp->sl_locked) != 0); ! 3439: ! 3440: (void) splx (prev_pl); ! 3441: } else { ! 3442: /* ! 3443: * Write the owner information for SLEEP_LOCKAVAIL () and ! 3444: * release the test-and-set lock on the process list. ! 3445: */ ! 3446: ! 3447: lockp->sl_holder = PROC_HANDLE (); ! 3448: ! 3449: PLIST_UNLOCK (lockp->sl_plist, prev_pl); ! 3450: } ! 3451: ! 3452: return TRUE; ! 3453: } ! 3454: ! 3455: ! 3456: /* ! 3457: *-STATUS: ! 3458: * DDI/DKI ! 3459: * ! 3460: *-NAME: ! 3461: * SLEEP_TRYLOCK () Try to acquire a sleep lock. ! 3462: * ! 3463: *-SYNOPSIS: ! 3464: * #include <sys/types.h> ! 3465: * #include <sys/ksynch.h> ! 3466: * ! 3467: * bool_t SLEEP_TRYLOCK (sleep_t * lockp); ! 3468: * ! 3469: *-ARGUMENTS: ! 3470: * lockp Pointer to the sleep lock to be acquired. ! 3471: * ! 3472: *-DESCRIPTION: ! 3473: * If the lock specified by "lockp" is immediately available (can be ! 3474: * acquired without sleeping) SLEEP_TRYLOCK () acquires the lock. If the ! 3475: * lock is not immediately available, the function returns without ! 3476: * acquiring the lock. ! 3477: * ! 3478: *-RETURN VALUE: ! 3479: * SLEEP_TRYLOCK () returns TRUE (a non-zero value) if the lock is ! 3480: * successfully acquired or FALSE (zero) if the lock is not acquired. ! 3481: * ! 3482: *-LEVEL: ! 3483: * Base or interrupt. ! 3484: * ! 3485: *-NOTES: ! 3486: * Does not sleep. ! 3487: * ! 3488: * Driver-defined basic locks, read/write locks and sleep locks may be ! 3489: * held across calls to this function. ! 3490: * ! 3491: *-SEE_ALSO: ! 3492: * SLEEP_ALLOC (), SLEEP_DEALLOC (), SLEEP_LOCK (), SLEEP_LOCK_SIG (), ! 3493: * SLEEP_LOCKAVAIL (), SLEEP_LOCKOWNED (), SLEEP_UNLOCK () ! 3494: */ ! 3495: ! 3496: #if __USE_PROTO__ ! 3497: bool_t (SLEEP_TRYLOCK) (sleep_t * lockp) ! 3498: #else ! 3499: bool_t ! 3500: SLEEP_TRYLOCK __ARGS ((lockp)) ! 3501: sleep_t * lockp; ! 3502: #endif ! 3503: { ! 3504: ASSERT (lockp != NULL); ! 3505: ! 3506: if (ATOMIC_TEST_AND_SET_UCHAR (lockp->sl_locked) == 0) { ! 3507: lockp->sl_holder = PROC_HANDLE (); ! 3508: return TRUE; ! 3509: } ! 3510: ! 3511: return FALSE; ! 3512: } ! 3513: ! 3514: ! 3515: /* ! 3516: *-STATUS: ! 3517: * DDI/DKI ! 3518: * ! 3519: *-NAME: ! 3520: * SLEEP_UNLOCK () Release a sleep lock. ! 3521: * ! 3522: *-SYNOPSIS: ! 3523: * #include <sys/ksynch.h> ! 3524: * ! 3525: * void SLEEP_UNLOCK (sleep_t * lockp); ! 3526: * ! 3527: *-ARGUMENTS: ! 3528: * lockp Pointer to the sleep lock to be released. ! 3529: * ! 3530: *-DESCRIPTION: ! 3531: * SLEEP_UNLOCK () releases the sleep lock specified by "lockp". If there ! 3532: * are processes waiting for the lock, one of the waiting processes is ! 3533: * awakened. ! 3534: * ! 3535: *-RETURN VALUE: ! 3536: * None. ! 3537: * ! 3538: *-LEVEL: ! 3539: * Base or interrupt. ! 3540: * ! 3541: *-NOTES: ! 3542: * Does not sleep. ! 3543: * ! 3544: * Driver-defined basic locks, read/write locks and sleep locks may be ! 3545: * held across calls to this function. ! 3546: * ! 3547: *-SEE_ALSO: ! 3548: * SLEEP_ALLOC (), SLEEP_DEALLOC (), SLEEP_LOCK (), SLEEP_LOCK_SIG (), ! 3549: * SLEEP_LOCKAVAIL (), SLEEP_LOCKOWNED (), SLEEP_TRYLOCK () ! 3550: */ ! 3551: ! 3552: #if __USE_PROTO__ ! 3553: void (SLEEP_UNLOCK) (sleep_t * lockp) ! 3554: #else ! 3555: void ! 3556: SLEEP_UNLOCK __ARGS ((lockp)) ! 3557: sleep_t * lockp; ! 3558: #endif ! 3559: { ! 3560: pl_t prev_pl; ! 3561: ! 3562: /* ! 3563: * Make some assertions. Note that we *don't* assert that our ! 3564: * PROC_HANDLE () matches "sl_holder", since thanks to ! 3565: * SLEEP_TRYLOCK () allowing interrupt contexts to acquire sleep ! 3566: * locks we can't rely on that. ! 3567: */ ! 3568: ! 3569: ASSERT (lockp != NULL); ! 3570: ASSERT (ATOMIC_FETCH_UCHAR (lockp->sl_locked)); ! 3571: ! 3572: ! 3573: /* ! 3574: * When we are unlocking the sleep lock, we must lock the process ! 3575: * list so we can test whether there are any waiting processes to ! 3576: * be given the lock. ! 3577: */ ! 3578: ! 3579: prev_pl = PLIST_LOCK (lockp->sl_plist, "SLEEP_UNLOCK"); ! 3580: ! 3581: ! 3582: /* ! 3583: * If there are no waiters, we unlock the sleep lock, otherwise we ! 3584: * wake the first waiting process and leave the lock in the locked ! 3585: * state for the process to simply take over from us later. ! 3586: * ! 3587: * In order to cross-check things with the sleep function, we write ! 3588: * the expected processes' identity into the lock. ! 3589: */ ! 3590: ! 3591: if ((lockp->sl_holder = WAKE_ONE (lockp->sl_plist)) == NULL) { ! 3592: /* ! 3593: * No waiting processes, give the lock away. ! 3594: */ ! 3595: ! 3596: ATOMIC_CLEAR_UCHAR (lockp->sl_locked); ! 3597: } ! 3598: ! 3599: PLIST_UNLOCK (lockp->sl_plist, prev_pl); ! 3600: } ! 3601: ! 3602: ! 3603: /* ! 3604: *-STATUS: ! 3605: * DDI/DKI ! 3606: * ! 3607: *-NAME: ! 3608: * SV_ALLOC () Allocate and initialize a synchronization variable. ! 3609: * ! 3610: *-SYNOPSIS: ! 3611: * #include <sys/kmem.h> ! 3612: * #include <sys/ksynch.h> ! 3613: * ! 3614: * sv_t * SV_ALLOC (int flag); ! 3615: * ! 3616: *-ARGUMENTS: ! 3617: * flag Specifies whether the caller is willing to sleep ! 3618: * waiting for memory. If "flag" is set to KM_SLEEP, the ! 3619: * caller will sleep if necessary until sufficient memory ! 3620: * is available. If "flag" is set to KM_NOSLEEP, the ! 3621: * caller will not sleep, but SLEEP_ALLOC () will return ! 3622: * NULL if sufficient memory is not immediately ! 3623: * available. ! 3624: * ! 3625: *-DESCRIPTION: ! 3626: * SV_ALLOC () dynamically allocates and initialises an instance of a ! 3627: * synchronization variable. ! 3628: * ! 3629: *-RETURN VALUE: ! 3630: * Upon successful completion, SV_ALLOC () returns a pointer to the newly ! 3631: * allocated synchronization variable. If KM_NOSLEEP is specified and ! 3632: * sufficient memory is not immediately available, SV_ALLOC () returns a ! 3633: * NULL pointer. ! 3634: * ! 3635: *-LEVEL: ! 3636: * Base only if "flag" is set to KM_SLEEP. Base or Interrupt if "flag" is ! 3637: * set to KM_NOSLEEP. ! 3638: * ! 3639: *-NOTES: ! 3640: * May sleep if "flag" is set to KM_NOSLEEP. ! 3641: * ! 3642: * Driver-defined basic locks and read/write locks may be held across ! 3643: * calls to this function if "flag" is set to KM_NOSLEEP but may not be ! 3644: * held if "flag" is KM_SLEEP. ! 3645: * ! 3646: * Driver-defined sleep locks may be held across calls to this function ! 3647: * regardless of the value of "flag". ! 3648: * ! 3649: *-SEE_ALSO: ! 3650: * SV_BROADCAST (), SV_DEALLOC (), SV_SIGNAL (), SV_WAIT (), ! 3651: * SV_WAIT_SIG () ! 3652: */ ! 3653: ! 3654: #if __USE_PROTO__ ! 3655: sv_t * (SV_ALLOC) (int flag) ! 3656: #else ! 3657: sv_t * ! 3658: SV_ALLOC __ARGS ((flag)) ! 3659: int flag; ! 3660: #endif ! 3661: { ! 3662: sv_t * svp; ! 3663: ! 3664: ASSERT (flag == KM_SLEEP || flag == KM_NOSLEEP); ! 3665: ! 3666: /* ! 3667: * Allocate and initialise the data, possibly waiting for enough ! 3668: * memory to become available. ! 3669: */ ! 3670: ! 3671: if ((svp = (sv_t *) _lock_malloc (sizeof (* svp), ! 3672: flag)) != NULL) { ! 3673: INIT_LNODE (& svp->sv_node, NULL, & synch_vars, flag); ! 3674: PLIST_INIT (svp->sv_plist); ! 3675: } ! 3676: ! 3677: return svp; ! 3678: } ! 3679: ! 3680: ! 3681: /* ! 3682: *-STATUS: ! 3683: * DDI/DKI ! 3684: * ! 3685: *-NAME: ! 3686: * SV_BROADCAST () Wake up all processes sleeping on a synchronization ! 3687: * variable. ! 3688: * ! 3689: *-SYNOPSIS: ! 3690: * #include <sys/ksynch.h> ! 3691: * ! 3692: * void * SV_BROADCAST (sv_t * svp, int flags); ! 3693: * ! 3694: *-ARGUMENTS: ! 3695: * svp Pointer to the synchronization variable to be ! 3696: * broadcast signalled. ! 3697: * ! 3698: * flags Bit field for flags. No flags are defined for use in ! 3699: * drivers and the "flags" argument must be set to zero. ! 3700: * ! 3701: *-DESCRIPTION: ! 3702: * If one or more processes are blocked on the synchronization variable ! 3703: * specified by "svp", SV_BROADCAST () wakes up all of the blocked ! 3704: * processes. Note that synchronization variables are stateless, and ! 3705: * therefore calls to SV_BROADCAST () only affect processes currently ! 3706: * blocked on the synchronization variable and have not effect on ! 3707: * processes that block on the synchronization variable at a later time. ! 3708: * ! 3709: *-RETURN VALUE: ! 3710: * None. ! 3711: * ! 3712: *-LEVEL: ! 3713: * Base or interrupt. ! 3714: * ! 3715: *-NOTES: ! 3716: * Does not sleep. ! 3717: * ! 3718: * Driver-defined basic locks and read/write locks may be held across ! 3719: * calls to this function if "flag" is set to KM_NOSLEEP but may not be ! 3720: * held if "flag" is KM_SLEEP. ! 3721: * ! 3722: * Driver-defined basic locks, read/write locks and sleep locks may be ! 3723: * held across calls to this function. ! 3724: * ! 3725: *-SEE_ALSO: ! 3726: * SV_ALLOC (), SV_DEALLOC (), SV_SIGNAL (), SV_WAIT (), SV_WAIT_SIG () ! 3727: */ ! 3728: ! 3729: #if __USE_PROTO__ ! 3730: void (SV_BROADCAST) (sv_t * svp, int flags) ! 3731: #else ! 3732: void ! 3733: SV_BROADCAST __ARGS ((svp, flags)) ! 3734: sv_t * svp; ! 3735: int flags; ! 3736: #endif ! 3737: { ! 3738: pl_t prev_pl; ! 3739: ! 3740: ASSERT (flags == 0); ! 3741: ASSERT (svp != NULL); ! 3742: ! 3743: /* ! 3744: * Lock the list (required by WAKE_ALL ()), wake the sleepers, and ! 3745: * unlock the list. ! 3746: */ ! 3747: ! 3748: prev_pl = PLIST_LOCK (svp->sv_plist, "SV_BROADCAST"); ! 3749: ! 3750: WAKE_ALL (svp->sv_plist); ! 3751: ! 3752: PLIST_UNLOCK (svp->sv_plist, prev_pl); ! 3753: } ! 3754: ! 3755: ! 3756: /* ! 3757: *-STATUS: ! 3758: * DDI/DKI ! 3759: * ! 3760: *-NAME: ! 3761: * SV_DEALLOC () Deallocate an instance of a synchronization variable. ! 3762: * ! 3763: *-SYNOPSIS: ! 3764: * #include <sys/ksynch.h> ! 3765: * ! 3766: * void SV_DEALLOC (sv_t * svp); ! 3767: * ! 3768: *-ARGUMENTS: ! 3769: * svp Pointer to the synchronization variable to be ! 3770: * deallocated. ! 3771: * ! 3772: *-DESCRIPTION: ! 3773: * SV_DEALLOC () deallocates the synchronization variable specified by ! 3774: * "svp". ! 3775: * ! 3776: *-RETURN VALUE: ! 3777: * None. ! 3778: * ! 3779: *-LEVEL: ! 3780: * Base or Interrupt. ! 3781: * ! 3782: *-NOTES: ! 3783: * Does not sleep. ! 3784: * ! 3785: * Driver-defined basic locks, read/write locks, and sleep locks may be ! 3786: * held across calls to this function. ! 3787: * ! 3788: *-SEE_ALSO: ! 3789: * SV_ALLOC (), SV_BROADCAST (), SV_SIGNAL (), SV_WAIT (), SV_WAIT_SIG () ! 3790: */ ! 3791: ! 3792: #if __USE_PROTO__ ! 3793: void (SV_DEALLOC) (sv_t * svp) ! 3794: #else ! 3795: void ! 3796: SV_DEALLOC __ARGS ((svp)) ! 3797: sv_t * svp; ! 3798: #endif ! 3799: { ! 3800: ASSERT (svp != NULL); ! 3801: ! 3802: /* ! 3803: * Remove from the list of all synchronization variables and free any ! 3804: * statistics buffer space before freeing the lock itself. ! 3805: */ ! 3806: ! 3807: PLIST_DESTROY (svp->sv_plist); ! 3808: ! 3809: FREE_LNODE (& svp->sv_node, & synch_vars); ! 3810: ! 3811: _lock_free (svp, sizeof (* svp)); ! 3812: } ! 3813: ! 3814: ! 3815: /* ! 3816: *-STATUS: ! 3817: * DDI/DKI ! 3818: * ! 3819: *-NAME: ! 3820: * SV_SIGNAL () Wake up one process sleeping on a synchronization ! 3821: * variable. ! 3822: * ! 3823: *-SYNOPSIS: ! 3824: * #include <sys/ksynch.h> ! 3825: * ! 3826: * void SV_SIGNAL (sv_t * svp, int flags); ! 3827: * ! 3828: *-ARGUMENTS: ! 3829: * svp Pointer to the synchronization variable to be ! 3830: * signalled. ! 3831: * ! 3832: * flags Bit field for flags. No flags are defined for use in ! 3833: * drivers and the "flags" argument must be set to zero. ! 3834: * ! 3835: *-DESCRIPTION: ! 3836: * If one or more processes are blocked on the synchronization variable ! 3837: * specified by "svp", SV_SIGNAL () wakes up a single blocked process. ! 3838: * Note that synchronization variables are stateless, and therefore ! 3839: * calls to SV_SIGNAL only affect processes currently blocked on the ! 3840: * synchronization variable and have no effect on processes that block on ! 3841: * the synchronization variable at a later time. ! 3842: * ! 3843: *-RETURN VALUE: ! 3844: * None. ! 3845: * ! 3846: *-LEVEL: ! 3847: * Base or Interrupt. ! 3848: * ! 3849: *-NOTES: ! 3850: * Does not sleep. ! 3851: * ! 3852: * Driver-defined basic locks, read/write locks, and sleep locks may be ! 3853: * held across calls to this function. ! 3854: * ! 3855: *-SEE_ALSO: ! 3856: * SV_ALLOC (), SV_BROADCAST (), SV_DEALLOC (), SV_WAIT (), ! 3857: * SV_WAIT_SIG () ! 3858: */ ! 3859: ! 3860: #if __USE_PROTO__ ! 3861: void (SV_SIGNAL) (sv_t * svp, int flags) ! 3862: #else ! 3863: void ! 3864: SV_SIGNAL __ARGS ((svp, flags)) ! 3865: sv_t * svp; ! 3866: int flags; ! 3867: #endif ! 3868: { ! 3869: pl_t prev_pl; ! 3870: ! 3871: ASSERT (flags == 0); ! 3872: ASSERT (svp != NULL); ! 3873: ! 3874: /* ! 3875: * Lock the list (required by WAKE_ONE ()), wake a sleeper, and ! 3876: * unlock the list. ! 3877: */ ! 3878: ! 3879: prev_pl = PLIST_LOCK (svp->sv_plist, "SV_SIGNAL"); ! 3880: ! 3881: (void) WAKE_ONE (svp->sv_plist); ! 3882: ! 3883: PLIST_UNLOCK (svp->sv_plist, prev_pl); ! 3884: } ! 3885: ! 3886: ! 3887: /* ! 3888: *-STATUS: ! 3889: * DDI/DKI ! 3890: * ! 3891: *-NAME: ! 3892: * SV_WAIT () Sleep on a synchronization variable. ! 3893: * ! 3894: *-SYNOPSIS: ! 3895: * #include <sys/types.h> ! 3896: * #include <sys/ksynch.h> ! 3897: * ! 3898: * void SV_WAIT (sv_t * svp, int priority, lock_t * lkp); ! 3899: * ! 3900: *-ARGUMENTS: ! 3901: * svp Pointer to the synchronization variable on which to ! 3902: * sleep. ! 3903: * ! 3904: * priority A hint to the scheduling policy as to the relative ! 3905: * priority the caller wishes to be assigned while ! 3906: * running in the kernel after waking up. The valid ! 3907: * values for this argument are as follows: ! 3908: * ! 3909: * pridisk Priority appropriate for disk driver ! 3910: * prinet Priority appropriate for network driver. ! 3911: * pritty Priority appropriate for terminal driver. ! 3912: * pritape Priority appropriate for tape driver. ! 3913: * prihi High priority. ! 3914: * primed Medium priority. ! 3915: * prilo Low priority. ! 3916: * ! 3917: * Drivers may use these values to request a priority ! 3918: * appropriate to a given type of device or to request a ! 3919: * priority that is high, medium or low relative to other ! 3920: * activities within the kernel. ! 3921: * ! 3922: * It is also permissible to specify positive or negative ! 3923: * offsets from the values defined above. Positive ! 3924: * offsets result in favourable priority. The maximum ! 3925: * allowable offset in all cases is 3 (eg. pridisk+3 ! 3926: * and pridisk-3 are valid values by pridisk+4 and ! 3927: * pridisk-4 are not valid). Offsets can be useful in ! 3928: * defining the relative importance of different locks or ! 3929: * resources that may be hald by a given driver. In ! 3930: * general, a higher relative priority should be used ! 3931: * when the caller is attempting to acquire a highly- ! 3932: * contended lock or resource, or when the caller is ! 3933: * already holding one or more locks or kernel resources ! 3934: * upon entry to SV_WAIT (). ! 3935: * ! 3936: * The exact semantics of the "priority" argument is ! 3937: * specific to the scheduling class of the caller, and ! 3938: * some scheduling classes may choose to ignore the ! 3939: * argument for the purposes of assigning a scheduling ! 3940: * priority. ! 3941: * ! 3942: * lkp Pointer to a basic lock which must be locked when ! 3943: * SV_WAIT () is called. The basic lock is released when ! 3944: * the calling process goes to sleep, as described below. ! 3945: *-DESCRIPTION: ! 3946: * SV_WAIT () causes the calling process to go to sleep (the caller's ! 3947: * execution is suspended and other processes may be scheduled) waiting ! 3948: * for a call to SV_SIGNAL () or SV_BROADCAST () for the synchronization ! 3949: * variable specified by "svp". ! 3950: * ! 3951: * The basic lock specified by "lkp" must be held by the caller upon ! 3952: * entry. The lock is released and the interrupt priority level is set to ! 3953: * plbase after the process is queued on the synchronization variable but ! 3954: * prior to switching context switching to another process. When the ! 3955: * caller returns from SV_WAIT () the basic lock is not held and the ! 3956: * interrupt priority level is equal to plbase. ! 3957: * ! 3958: * The caller will not be interrupted by signals while sleeping inside ! 3959: * SV_WAIT (). ! 3960: * ! 3961: *-RETURN VALUE: ! 3962: * None. ! 3963: * ! 3964: *-LEVEL: ! 3965: * Base level only. ! 3966: * ! 3967: *-NOTES: ! 3968: * May sleep. ! 3969: * ! 3970: * Driver-defined basic locks and read/write locks may not be held across ! 3971: * calls to this function. ! 3972: * ! 3973: * Driver-defined sleep locks may be held across calls to this function. ! 3974: * ! 3975: *-SEE_ALSO: ! 3976: * SV_ALLOC (), SV_BROADCAST (), SV_DEALLOC (), SV_SIGNAL (), ! 3977: * SV_WAIT_SIG () ! 3978: */ ! 3979: ! 3980: #if __USE_PROTO__ ! 3981: void (SV_WAIT) (sv_t * svp, int priority, lock_t * lkp) ! 3982: #else ! 3983: void ! 3984: SV_WAIT __ARGS ((svp, priority, lkp)) ! 3985: sv_t * svp; ! 3986: int priority; ! 3987: lock_t * lkp; ! 3988: #endif ! 3989: { ! 3990: ASSERT (svp != NULL); ! 3991: ASSERT (lkp != NULL); ! 3992: ASSERT_BASE_LEVEL (); ! 3993: ! 3994: /* ! 3995: * First off, we have to lock the process list. After this is done we ! 3996: * can safely release the client's basic lock, since once we have our ! 3997: * lock the rest of the wait operation will proceed atomically. ! 3998: */ ! 3999: ! 4000: (void) PLIST_LOCK (svp->sv_plist, "SV_WAIT"); ! 4001: ! 4002: UNLOCK (lkp, plhi); ! 4003: ! 4004: (void) MAKE_SLEEPING (svp->sv_plist, priority, SLEEP_NO_SIGNALS); ! 4005: ! 4006: (void) splbase (); /* let's make sure... */ ! 4007: } ! 4008: ! 4009: ! 4010: /* ! 4011: *-STATUS: ! 4012: * DDI/DKI ! 4013: * ! 4014: *-NAME: ! 4015: * SV_WAIT_SIG () Sleep on a synchronization variable. ! 4016: * ! 4017: *-SYNOPSIS: ! 4018: * #include <sys/types.h> ! 4019: * #include <sys/ksynch.h> ! 4020: * ! 4021: * bool_t SV_WAIT_SIG (sv_t * svp, int priority, lock_t * lkp); ! 4022: * ! 4023: *-ARGUMENTS: ! 4024: * svp Pointer to the synchronization variable on which to ! 4025: * sleep. ! 4026: * ! 4027: * priority A hint to the scheduling policy as to the relative ! 4028: * priority the caller wishes to be assigned while ! 4029: * running in the kernel after waking up. The valid ! 4030: * values for this argument are as follows: ! 4031: * ! 4032: * pridisk Priority appropriate for disk driver ! 4033: * prinet Priority appropriate for network driver. ! 4034: * pritty Priority appropriate for terminal driver. ! 4035: * pritape Priority appropriate for tape driver. ! 4036: * prihi High priority. ! 4037: * primed Medium priority. ! 4038: * prilo Low priority. ! 4039: * ! 4040: * Drivers may use these values to request a priority ! 4041: * appropriate to a given type of device or to request a ! 4042: * priority that is high, medium or low relative to other ! 4043: * activities within the kernel. ! 4044: * ! 4045: * It is also permissible to specify positive or negative ! 4046: * offsets from the values defined above. Positive ! 4047: * offsets result in favourable priority. The maximum ! 4048: * allowable offset in all cases is 3 (eg. pridisk+3 ! 4049: * and pridisk-3 are valid values by pridisk+4 and ! 4050: * pridisk-4 are not valid). Offsets can be useful in ! 4051: * defining the relative importance of different locks or ! 4052: * resources that may be hald by a given driver. In ! 4053: * general, a higher relative priority should be used ! 4054: * when the caller is attempting to acquire a highly- ! 4055: * contended lock or resource, or when the caller is ! 4056: * already holding one or more locks or kernel resources ! 4057: * upon entry to SV_WAIT_SIG (). ! 4058: * ! 4059: * The exact semantics of the "priority" argument is ! 4060: * specific to the scheduling class of the caller, and ! 4061: * some scheduling classes may choose to ignore the ! 4062: * argument for the purposes of assigning a scheduling ! 4063: * priority. ! 4064: * ! 4065: * lkp Pointer to a basic lock which must be locked when ! 4066: * SV_WAIT_SIG () is called. The basic lock is released ! 4067: * when the calling process goes to sleep, as described ! 4068: * below. ! 4069: * ! 4070: *-DESCRIPTION: ! 4071: * SV_WAIT_SIG () causes the calling process to go to sleep (the caller's ! 4072: * execution is suspended and other processes may be scheduled) waiting ! 4073: * for a call to SV_SIGNAL () or SV_BROADCAST () for the synchronization ! 4074: * variable specified by "svp". ! 4075: * ! 4076: * The basic lock specified by "lkp" must be held by the caller upon ! 4077: * entry. The lock is released and the interrupt priority level is set to ! 4078: * plbase after the process is queued on the synchronization variable but ! 4079: * prior to switching context switching to another process. When the ! 4080: * caller returns from SV_WAIT_SIG () the basic lock is not held and the ! 4081: * interrupt priority level is equal to plbase. ! 4082: * ! 4083: * SV_WAIT_SIG () may be interrupted by a signal, in which case it will ! 4084: * return early without waiting for a call to SV_SIGNAL () or ! 4085: * SV_BROADCAST (). ! 4086: * ! 4087: * If the function is interrupted by a job control signal (eg SIGSTOP, ! 4088: * SIGTSTP, SIGTTIN, SIGTTOU) which results in the caller entering a ! 4089: * stopped state, when continued the SV_WAIT_SIG () function will return ! 4090: * TRUE as if the process had been awakened by a call to SV_SIGNAL () or ! 4091: * SV_BROADCAST (). ! 4092: * ! 4093: * If the caller is interrupted by a signal other than a job control ! 4094: * signal, or by a job control signal that does not result in the caller ! 4095: * stopping (because the signal has a non-default disposition), the ! 4096: * SV_WAIT_SIG () call will return FALSE. ! 4097: * ! 4098: *-RETURN VALUE: ! 4099: * SV_WAIT_SIG () returns TRUE (a non-zero value) if the caller woke up ! 4100: * because of a call to SV_SIGNAL () or SV_BROADCAST (), or if the caller ! 4101: * was stopped and subsequently continued. SV_WAIT_SIG () returns FALSE ! 4102: * (zero) if the caller woke up and returned early because of a signal ! 4103: * other than a job control stop signal, or by a job control signal that ! 4104: * did not result in the caller stopping because the signal has a non- ! 4105: * default disposition. ! 4106: * ! 4107: *-LEVEL: ! 4108: * Base level only. ! 4109: * ! 4110: *-NOTES: ! 4111: * May sleep. ! 4112: * ! 4113: * Driver-defined basic locks and read/write locks may not be held across ! 4114: * calls to this function. ! 4115: * ! 4116: * Driver-defined sleep locks may be held across calls to this function. ! 4117: * ! 4118: *-SEE_ALSO: ! 4119: * SV_ALLOC (), SV_BROADCAST (), SV_DEALLOC (), SV_SIGNAL (), SV_WAIT () ! 4120: */ ! 4121: ! 4122: #if __USE_PROTO__ ! 4123: bool_t (SV_WAIT_SIG) (sv_t * svp, int priority, lock_t * lkp) ! 4124: #else ! 4125: bool_t ! 4126: SV_WAIT_SIG __ARGS ((svp, priority, lkp)) ! 4127: sv_t * svp; ! 4128: int priority; ! 4129: lock_t * lkp; ! 4130: #endif ! 4131: { ! 4132: bool_t not_signalled; ! 4133: ! 4134: ASSERT (svp != NULL); ! 4135: ASSERT (lkp != NULL); ! 4136: ASSERT_BASE_LEVEL (); ! 4137: ! 4138: /* ! 4139: * First off, we have to lock the process list. After this is done we ! 4140: * can safely release the client's basic lock, since once we have our ! 4141: * lock the rest of the wait operation will proceed atomically. ! 4142: */ ! 4143: ! 4144: (void) PLIST_LOCK (svp->sv_plist, "SV_WAIT_SIG"); ! 4145: ! 4146: UNLOCK (lkp, plhi); ! 4147: ! 4148: not_signalled = MAKE_SLEEPING (svp->sv_plist, priority, ! 4149: SLEEP_INTERRUPTIBLE) != PROCESS_SIGNALLED; ! 4150: ! 4151: (void) splbase (); /* let's make sure */ ! 4152: ! 4153: return not_signalled; ! 4154: } ! 4155: ! 4156: ! 4157: /* ! 4158: * This function exercises some of the most basic facilities of the locking ! 4159: * system. It is difficult to assure that the code presented in this file ! 4160: * will work correctly, because the nature of the service provided is a ! 4161: * temporal guarantee that can't be verified without performing arbitrary ! 4162: * fine-grained interleavings of at least two execution paths through each ! 4163: * combination of interacting functions. ! 4164: * ! 4165: * So, the best we can do here in a portable fashion is to present some tests ! 4166: * of the observable properties of the above code, and to (hopefully) exercise ! 4167: * the ASSERT () statements in the above for some minimal self-checking. ! 4168: * ! 4169: * One problem with the use of ASSERT () tests is that it's difficult to ! 4170: * build negative tests. The argument to this function indicates a negative ! 4171: * tests that should be performed, presumably in conjunction with some test ! 4172: * script capable of verifying an exception report against a previous run. ! 4173: * ! 4174: * Our tests use a lock interrupt level appropriate to the environment; in ! 4175: * user-mode tests under Coherent, disabling interrupts is prohibited ! 4176: */ ! 4177: ! 4178: #if __COHERENT__ ! 4179: # define test_pl plbase ! 4180: #else ! 4181: # define test_pl plhi ! 4182: #endif ! 4183: ! 4184: #if __USE_PROTO__ ! 4185: int (LOCK_TESTS) (int negative) ! 4186: #else ! 4187: int ! 4188: LOCK_TESTS __ARGS ((negative)) ! 4189: int negative; ! 4190: #endif ! 4191: { ! 4192: pl_t prev_pl; ! 4193: ! 4194: lock_t * basic_lock; ! 4195: rwlock_t * rw_lock; ! 4196: sleep_t * sleep_lock; ! 4197: sv_t * synch_var; ! 4198: ! 4199: static lkinfo_t basic_info = { "test basic lock" }; ! 4200: static lkinfo_t rw_info = { "test read/write lock" }; ! 4201: static lkinfo_t sleep_info = { "test sleep lock" }; ! 4202: ! 4203: /* ! 4204: * Allocate some locks that we can play with... ! 4205: */ ! 4206: ! 4207: basic_lock = LOCK_ALLOC (32, test_pl, & basic_info, KM_SLEEP); ! 4208: rw_lock = RW_ALLOC (10, test_pl, & rw_info, KM_SLEEP); ! 4209: sleep_lock = SLEEP_ALLOC (0, & sleep_info, KM_SLEEP); ! 4210: synch_var = SV_ALLOC (KM_SLEEP); ! 4211: ! 4212: ! 4213: /* ! 4214: * Basic locks. ! 4215: */ ! 4216: ! 4217: if ((prev_pl = TRYLOCK (basic_lock, test_pl)) == invpl || ! 4218: TRYLOCK (basic_lock, test_pl) != invpl) ! 4219: return -1; ! 4220: ! 4221: ! 4222: UNLOCK (basic_lock, prev_pl); ! 4223: ! 4224: prev_pl = LOCK (basic_lock, test_pl); ! 4225: ! 4226: if (TRYLOCK (basic_lock, test_pl) != invpl) ! 4227: return -1; ! 4228: ! 4229: UNLOCK (basic_lock, prev_pl); ! 4230: ! 4231: /* ! 4232: * Read/write locks. ! 4233: */ ! 4234: ! 4235: if ((prev_pl = RW_TRYRDLOCK (rw_lock, test_pl)) == invpl || ! 4236: RW_TRYRDLOCK (rw_lock, test_pl) == invpl || ! 4237: RW_TRYWRLOCK (rw_lock, test_pl) != invpl) ! 4238: return -1; ! 4239: ! 4240: (void) RW_RDLOCK (rw_lock, test_pl); ! 4241: ! 4242: if (RW_TRYWRLOCK (rw_lock, test_pl) != invpl) ! 4243: return -1; ! 4244: ! 4245: RW_UNLOCK (rw_lock, prev_pl); ! 4246: RW_UNLOCK (rw_lock, prev_pl); ! 4247: RW_UNLOCK (rw_lock, prev_pl); ! 4248: ! 4249: ! 4250: if ((prev_pl = RW_TRYWRLOCK (rw_lock, test_pl)) == invpl || ! 4251: RW_TRYWRLOCK (rw_lock, test_pl) != invpl || ! 4252: RW_TRYRDLOCK (rw_lock, test_pl) != invpl) ! 4253: return -1; ! 4254: ! 4255: RW_UNLOCK (rw_lock, prev_pl); ! 4256: ! 4257: ! 4258: prev_pl = RW_WRLOCK (rw_lock, test_pl); ! 4259: ! 4260: if (RW_TRYWRLOCK (rw_lock, test_pl) != invpl || ! 4261: RW_TRYRDLOCK (rw_lock, test_pl) != invpl) ! 4262: return -1; ! 4263: ! 4264: RW_UNLOCK (rw_lock, prev_pl); ! 4265: ! 4266: ! 4267: /* ! 4268: * Sleep locks. ! 4269: */ ! 4270: ! 4271: if (SLEEP_LOCKOWNED (sleep_lock) == TRUE || ! 4272: SLEEP_LOCKAVAIL (sleep_lock) == FALSE || ! 4273: SLEEP_TRYLOCK (sleep_lock) == FALSE || ! 4274: SLEEP_LOCKOWNED (sleep_lock) == FALSE || ! 4275: SLEEP_LOCKAVAIL (sleep_lock) == TRUE || ! 4276: SLEEP_TRYLOCK (sleep_lock) == TRUE) ! 4277: return -1; ! 4278: ! 4279: SLEEP_UNLOCK (sleep_lock); ! 4280: ! 4281: if (SLEEP_LOCKOWNED (sleep_lock) == TRUE || ! 4282: SLEEP_LOCKAVAIL (sleep_lock) == FALSE || ! 4283: SLEEP_LOCK_SIG (sleep_lock, prilo) == FALSE || ! 4284: SLEEP_LOCKOWNED (sleep_lock) == FALSE || ! 4285: SLEEP_LOCKAVAIL (sleep_lock) == TRUE || ! 4286: SLEEP_TRYLOCK (sleep_lock) == TRUE) ! 4287: return -1; ! 4288: ! 4289: SLEEP_UNLOCK (sleep_lock); ! 4290: ! 4291: SLEEP_LOCK (sleep_lock, prilo); ! 4292: ! 4293: if (SLEEP_LOCKOWNED (sleep_lock) == FALSE || ! 4294: SLEEP_LOCKAVAIL (sleep_lock) == TRUE || ! 4295: SLEEP_TRYLOCK (sleep_lock) == TRUE) ! 4296: return -1; ! 4297: ! 4298: SLEEP_UNLOCK (sleep_lock); ! 4299: ! 4300: ! 4301: /* ! 4302: * Synchronisation variables: this is impossible to test without ! 4303: * access to timeout functions. Once DDI/DKI timeout functions are ! 4304: * available, we can use those to at least begin to exercise the ! 4305: * notion of synchronization. ! 4306: */ ! 4307: ! 4308: ! 4309: /* ! 4310: * Negative testing... need to add this! ! 4311: */ ! 4312: ! 4313: /* ! 4314: * Clean up and bail out. ! 4315: */ ! 4316: ! 4317: LOCK_DEALLOC (basic_lock); ! 4318: RW_DEALLOC (rw_lock); ! 4319: SLEEP_DEALLOC (sleep_lock); ! 4320: SV_DEALLOC (synch_var); ! 4321: ! 4322: return 0; ! 4323: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.