Annotation of coherent/f/etc/conf/streams/src/ksynch.c, revision 1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.