/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    lock.h

Abstract:

    This module defines types and functions for the LAN Manager server
    FSP's lock package.  This package began as a modification and
    streamlining of the executive resource package -- it allowed
    recursive acquisition, but didn't provide shared locks.  Later,
    debugging support in the form of level checking was added.

    Coming full circle, the package now serves as a wrapper around the
    real resource package.  It simply provides debugging support.  The
    reasons for reverting to using resources include:

    1) The resource package now supports recursive acquisition.

    2) There are a couple of places in the server where shared access
       is desirable.

    3) The resource package has a "no-wait" option that disables waiting
       for a lock when someone else owns it.  This feature is useful to
       the server FSD.

Author:

    Chuck Lenzmeier (chuckl) 29-Nov-1989
        A modification of Gary Kimura's resource package.  See lock.c.
    David Treadwell (davidtr)

    Chuck Lenzmeier (chuckl)  5-Apr-1991
        Revert to using resource package.

Environment:

    Kernel mode only, LAN Manager server FSP and FSD.

Revision History:

--*/

#ifndef _LOCK_
#define _LOCK_

//#include <ntos.h>

//
// Structure containing global spin locks.  Used to isolate each spin
// lock into its own cache line.
//

typedef struct _SRV_GLOBAL_SPIN_LOCKS {
    ULONG Reserved1[7];
    KSPIN_LOCK Fsd;
    ULONG Reserved2[7];
    struct {
        KSPIN_LOCK Lock;
        ULONG Reserved3[7];
    } Endpoint[ENDPOINT_LOCK_COUNT];
    KSPIN_LOCK Statistics;
    ULONG Reserved4[7];
    KSPIN_LOCK Timer;
    ULONG Reserved5[7];
#if SRVDBG || SRVDBG_HANDLES
    KSPIN_LOCK Debug;
    ULONG Reserved6[7];
#endif
} SRV_GLOBAL_SPIN_LOCKS, *PSRV_GLOBAL_SPIN_LOCKS;

//
// Macros for accessing spin locks.
//

#define ACQUIRE_SPIN_LOCK(lock,irql) {          \
    PAGED_CODE_CHECK();                         \
    ExAcquireSpinLock( (lock), (irql) );        \
    }
#define RELEASE_SPIN_LOCK(lock,irql) {          \
    PAGED_CODE_CHECK();                         \
    ExReleaseSpinLock( (lock), (irql) );        \
    }
#define ACQUIRE_DPC_SPIN_LOCK(lock) {           \
    PAGED_CODE_CHECK();                         \
    ExAcquireSpinLockAtDpcLevel( (lock) );      \
    }
#define RELEASE_DPC_SPIN_LOCK(lock) {           \
    PAGED_CODE_CHECK();                         \
    ExReleaseSpinLockFromDpcLevel( (lock) );    \
    }

#define INITIALIZE_SPIN_LOCK(lock) KeInitializeSpinLock( lock );

#define GLOBAL_SPIN_LOCK(lock) SrvGlobalSpinLocks.lock
#define ENDPOINT_SPIN_LOCK(index) SrvGlobalSpinLocks.Endpoint[index].Lock

#define INITIALIZE_GLOBAL_SPIN_LOCK(lock) INITIALIZE_SPIN_LOCK( &GLOBAL_SPIN_LOCK(lock) )

#define ACQUIRE_GLOBAL_SPIN_LOCK(lock,irql) ACQUIRE_SPIN_LOCK( &GLOBAL_SPIN_LOCK(lock), (irql) )
#define RELEASE_GLOBAL_SPIN_LOCK(lock,irql) RELEASE_SPIN_LOCK( &GLOBAL_SPIN_LOCK(lock), (irql) )
#define ACQUIRE_DPC_GLOBAL_SPIN_LOCK(lock)  ACQUIRE_DPC_SPIN_LOCK( &GLOBAL_SPIN_LOCK(lock) )
#define RELEASE_DPC_GLOBAL_SPIN_LOCK(lock)  RELEASE_DPC_SPIN_LOCK( &GLOBAL_SPIN_LOCK(lock) )

//
// Macros for initializing, deleting, acquiring, and releasing locks.
//

#if !SRVDBG_LOCK

//
// When debugging is disabled, the lock macros simply equate to calls to
// the corresponding resource package functions.
//

#define INITIALIZE_LOCK( lock, level, name ) ExInitializeResourceLite( (lock) )
#define DELETE_LOCK( lock ) ExDeleteResourceLite( (lock) )

#define ACQUIRE_LOCK( lock ) \
                    ExAcquireResourceExclusiveLite( (lock), TRUE )
#define ACQUIRE_LOCK_NO_WAIT( lock ) \
                    ExAcquireResourceExclusiveLite( (lock), FALSE )

#define ACQUIRE_LOCK_SHARED( lock ) \
                    ExAcquireResourceSharedLite( (lock), TRUE )
#define ACQUIRE_LOCK_SHARED_NO_WAIT( lock ) \
                    ExAcquireResourceSharedLite( (lock), FALSE )

#define RELEASE_LOCK(lock) ExReleaseResourceLite( (lock) )

#define LOCK_NUMBER_OF_ACTIVE( lock ) ((lock)->ActiveCount)

#else // !SRVDBG_LOCK

//
// When debugging is enabled, the lock macros equate to calls to
// functions in the server.  These functions are implemented in lock.c.
//

#define INITIALIZE_LOCK( lock, level, name ) \
                    SrvInitializeLock( (lock), (level), (name) )
#define DELETE_LOCK( lock ) SrvDeleteLock( (lock) )

#define ACQUIRE_LOCK( lock ) \
                    SrvAcquireLock( (lock), TRUE, TRUE )
#define ACQUIRE_LOCK_NO_WAIT( lock ) \
                    SrvAcquireLock( (lock), FALSE, TRUE )

#define ACQUIRE_LOCK_SHARED( lock ) \
                    SrvAcquireLock( (lock), TRUE, FALSE )
#define ACQUIRE_LOCK_SHARED_NO_WAIT( lock ) \
                    SrvAcquireLock( (lock), FALSE, FALSE )

#define RELEASE_LOCK( lock ) SrvReleaseLock( (lock) )

#define LOCK_NUMBER_OF_ACTIVE( lock ) ((lock)->Resource.ActiveCount)

#define LOCK_NAME( lock ) ((lock)->Header.LockName)
#define LOCK_LEVEL( lock ) ((lock)->Header.LockLevel)
#define LOCK_THREAD_LIST( lock ) (&((lock)->Header.ThreadListEntry))

#endif // else !SRVDBG_LOCK


#if !SRVDBG_LOCK

//
// When debugging is disabled, a server lock is identical to an
// executive resource.
//

typedef ERESOURCE SRV_LOCK, *PSRV_LOCK;

#define RESOURCE_OF(_l_) (_l_)

#else // !SRVDBG_LOCK

//
// SRV_LOCK_HEADER is a structure that contains debugging information
// used by the server lock package.  Server locks contain a
// SRV_LOCK_HEADER.
//

typedef struct _SRV_LOCK_HEADER {

    //
    // To prevent deadlocks, locks are assigned level numbers.  If a
    // thread holds a lock with level N, it may only acquire new locks
    // with a level greater then N.  Level numbers are assigned during
    // lock initialization.
    //
    // *** Due to the problems involved in retaining the information
    //     necessary to do level checking for shared locks, the lock
    //     package only does level checking for exclusive locks.
    //

    ULONG LockLevel;

    //
    // A doubly-linked list of all the locks owned by a thread is stored
    // in a thread's TEB.  The list is in order of lock level (from
    // highest to lowest), which is also, by definition of lock levels,
    // the order in which the thread acquired the locks.  This allows
    // the thread to release the locks in any order while maintaining
    // easy access to the highest-level lock that the thread owns,
    // thereby providing a mechanism for ensuring that locks are
    // acquired in increasing order.
    //

    LIST_ENTRY ThreadListEntry;

    //
    // The symbolic name of the lock is used in DbgPrint calls.
    //

    PSZ LockName;

} SRV_LOCK_HEADER, *PSRV_LOCK_HEADER;

//
// When debugging is enabled, a server lock is a wrapper around an
// executive resource.
//

typedef struct _SRV_LOCK {

    //
    // The SRV_LOCK_HEADER must appear first!
    //

    SRV_LOCK_HEADER Header;

    //
    // The actual "lock" is maintained by the resource package.
    //

    ERESOURCE Resource;

} SRV_LOCK, *PSRV_LOCK;

#define RESOURCE_OF(_sl_) (_sl_).Resource

//
// Lock functions used when debugging.
//

VOID
SrvInitializeLock(
    IN PSRV_LOCK Lock,
    IN ULONG LockLevel,
    IN PSZ LockName
    );

VOID
SrvDeleteLock (
    IN PSRV_LOCK Lock
    );

BOOLEAN
SrvAcquireLock(
    IN PSRV_LOCK Lock,
    IN BOOLEAN Wait,
    IN BOOLEAN Exclusive
    );

VOID
SrvReleaseLock(
    IN PSRV_LOCK Lock
    );

//
// Macros that define locations in the UserReserved field of the TEB
// where lock level information is stored.
//

#define SRV_TEB_LOCK_LIST 0
#define SRV_TEB_LOCK_INIT 2
#define SRV_TEB_USER_SIZE (3 * sizeof(ULONG))

//
// Max value for lock levels is 0x7FFFFFFF.
//
// Levels for locks used in the server.  The following must be true:
//
// EndpointLock must be lower than ConnectionLock (really
// connection->Lock) because SrvCloseConnectionsFromClient holds
// EndpointLock when it acquires ConnectionLock to check a connection's
// client name, and because a number of callers hold EndpointLock when
// they call SrvCloseConnection, which acquires ConnectionLock.  Note
// also that SrvDeleteServedNet and TerminateServer hold EndpointLock
// when they call SrvCloseEndpoint; any attempt to change
// SrvCloseEndpoint must take this into account.
// ExamineAndProcessConnections also depends on this ordering.
//
// EndpointLock must be lower than MfcbListLock and MfcbLock because
// EndpointLock is held while stopping the server and closing
// connections, thereby closing files on the connections.
//
// ShareLock must be lower than ConnectionLock because
// SrvCloseTreeConnectsOnShare holds ShareLock when it calls
// SrvCloseTreeConnect.  Note that SrvSmbTreeConnect and
// SrvSmbTreeConnectAndX depend on this ordering, because they take out
// both locks concurrently.
//
// Similarly, ShareLock must be lower than MfcbListLock and MfcbLock
// because SrvCloseTreeConnectsOnShare holds ShareLock when it calls
// SrvCloseRfcbsOnTree.
//
// MfcbListLock must be lower than MfcbLock (really mfcb->Lock) because
// SrvMoveFile and DoDelete hold MfcbListLock to find an MFCB, then take
// MfcbLock before releasing MfcbListLock.
//
// MfcbLock must be lower than OrderedListLock and ConnectionLock
// because CompleteOpen acquires these locks while holding the MfcbLock.
//
// OrderedListLock must be lower than ConnectionLock because
// SrvFindEntryInOrderedList holds the ordered list lock when in calls
// the check-and-reference routine for sessions and tree connects.  For
// other ordered lists, this is not a problem, because the other lists
// are either protected the same locks that the check-and-reference
// routine uses (endpoints, connections, and shares), or the
// check-and-reference routine uses a spin lock (files).  Note also that
// OrderedListLock and ConnectionLock are acquired concurrently and in
// the proper order in SrvSmbSessionSetupAndX, SrvSmbTreeConnect, and
// CompleteOpen.
//
// *** WARNING:  If the ordered RFCB list (SrvRfcbList) or the ordered
//     session list (SrvSessionList) are changed to use a lock other
//     than SrvOrderedListLock, the above requirement may change, and
//     the routines listed above may need to change.  Changing other
//     ordered lists that currently use some other global lock also may
//     change the requirements.
//
// DebugLock must be higher than MfcbLock because CompleteOpen holds
// the MfcbLock when it allocates the LFCB and RFCB.  The DebugLock
// is held for all memory allocations.  Note that because of the
// way DebugLock is currently used, it is impossible to acquire other
// locks while holding DebugLock.
//
// ConnectionLock must be higher than SearchLock because the scavenger
// thread holds the search lock while walking the search list looking
// for search blocks to time out, and if a block to time out is found it
// closes the search which acquires the the connection lock in order to
// dereference the session in by the search block.
//
// EndpointLock must be higher than SearchLock because in the above
// scenario, SrvDereferenceSession may call SrvDereferenceConnection
// which acquires the endpoint lock.
//
// SearchLock must be at a higher level than ShareLock because
// SrvCloseShare gets the ShareLock, but SrvCloseSessionsOnTreeConnect
// aquires the SearchLock.
//
// CommDeviceLock must be higher than MfcbLock because DoCommDeviceOpen
// acquires the CommDeviceLock while holding MfcbLock.
//
// OplockListLock needs to be lower than ShareLock as the server may
// need to call SrvDereferenceRfcb while holding the OplockListLock.
// This routine may call SrvDereferenceLfcb which may call
// SrvDereferenceShare which acquires the ShareLock.
//
// This is a summary of the above (the top lock is acquired first and
// therefore must have a lower level):
//
//  endp  endp   endp  share  share  share  mfcbl  mfcb  mfcb  order  mfcb
//  conn  mfcbl  mfcb  conn   mfcbl  mfcb   mfcb   order conn  conn   debug
//
//  search  search  share    oplock  oplock
//  conn    endp    search   mfcb    share
//
// Merging this, we find the following "threads" of requirements:
//
//    share   mfcb   mfcb   oplock
//    search  debug  comm   share
//    endp
//    mfcbl
//    mfcb
//    order
//    conn
//
// The following locks are not affected by the above requirements:
//
//    configuration
//    smbbufferlist
//    Connection->LicenseLock
//
// The levels of all of these locks are made equal and high in an
// attempt to find level requirements not listed above.
//

#define OPLOCK_LIST_LOCK_LEVEL                  (ULONG)0x00000800
#define SHARE_LOCK_LEVEL                        (ULONG)0x00000900
#define SEARCH_LOCK_LEVEL                       (ULONG)0x00001000
#define ENDPOINT_LOCK_LEVEL                     (ULONG)0x00002000
#define MFCB_LIST_LOCK_LEVEL                    (ULONG)0x00003000
#define MFCB_LOCK_LEVEL                         (ULONG)0x00004000
#define COMM_DEVICE_LOCK_LEVEL                  (ULONG)0x00005000
#define ORDERED_LIST_LOCK_LEVEL                 (ULONG)0x00006000
#define CONNECTION_LOCK_LEVEL                   (ULONG)0x00007000
#define CONFIGURATION_LOCK_LEVEL                (ULONG)0x00010000
#define UNLOCKABLE_CODE_LOCK_LEVEL              (ULONG)0x00010000
#define STARTUPSHUTDOWN_LOCK_LEVEL              (ULONG)0x00020000
#define DEBUG_LOCK_LEVEL                        (ULONG)0x00050000
#define LICENSE_LOCK_LEVEL                      (ULONG)0x00100000
#define FCBLIST_LOCK_LEVEL                      (ULONG)0x00200000

#endif // else !SRVDBG_LOCK

#endif // ndef _LOCK_