/*++ Copyright(c) 1999-2000 Microsoft Corporation Module Name: brdgwait.c Abstract: Ethernet MAC level bridge. WAIT_REFCOUNT implementation Author: Mark Aiken Environment: Kernel mode driver Revision History: Feb 2000 - Original version --*/ #define NDIS_MINIPORT_DRIVER #define NDIS50_MINIPORT 1 #define NDIS_WDM 1 #pragma warning( push, 3 ) #include #include #pragma warning( pop ) #include "bridge.h" // =========================================================================== // // PUBLIC FUNCTIONS // // =========================================================================== VOID BrdgInitializeWaitRef( IN PWAIT_REFCOUNT pRefcount, IN BOOLEAN bResettable ) /*++ Routine Description: Initializes a wait-refcount Arguments: pRefcount The wait-refcount to initialize Return Value: none --*/ { NdisInitializeEvent(&pRefcount->Event); pRefcount->Refcount = 0L; pRefcount->state = WaitRefEnabled; pRefcount->bResettable = bResettable; NdisAllocateSpinLock( &pRefcount->lock ); // The event starts life signaled since the refcount starts at zero NdisSetEvent(&pRefcount->Event); } BOOLEAN BrdgIncrementWaitRef( IN PWAIT_REFCOUNT pRefcount ) /*++ Routine Description: Increments (acquires) a wait-refcount Arguments: pRefcount The wait-refcount to acquire Return Value: TRUE if the wait-refcount was successfully acquired, FALSE otherwise (this can happen if the wait-refcount has been shut down) --*/ { BOOLEAN bSuccess; LONG Scratch = 0L; SAFEASSERT( pRefcount != NULL ); NdisAcquireSpinLock( &pRefcount->lock ); if( pRefcount->state == WaitRefEnabled ) { SAFEASSERT( pRefcount->Refcount >= 0L ); Scratch = ++pRefcount->Refcount; bSuccess = TRUE; } else { // The wait-refcount isn't enabled. SAFEASSERT( (pRefcount->state == WaitRefShutdown) || (pRefcount->state == WaitRefShuttingDown) ); bSuccess = FALSE; } if( bSuccess && (Scratch == 1L) ) { // We incremented from zero. Reset the event. NdisResetEvent( &pRefcount->Event ); } NdisReleaseSpinLock( &pRefcount->lock ); return bSuccess; } VOID BrdgReincrementWaitRef( IN PWAIT_REFCOUNT pRefcount ) /*++ Routine Description: Re-increments a wait-refcount. This is guaranteed to succeed. It is only legal to use this if the caller has already acquired the wait-refcount (i.e., it is guaranteed that the refcount is > 0). CALLING THIS WITHOUT HAVING FIRST ACQUIRED THE WAIT-REFCOUNT WITH BrdgIncrementWaitRef IS A GREAT WAY TO SCREW UP YOUR CODE! Arguments: pRefcount The wait-refcount to re-acquire Return Value: none --*/ { LONG Scratch; SAFEASSERT( pRefcount != NULL ); NdisAcquireSpinLock( &pRefcount->lock ); SAFEASSERT( (pRefcount->state == WaitRefEnabled) || (pRefcount->state == WaitRefShuttingDown) ); SAFEASSERT( pRefcount->Refcount >= 0L ); Scratch = ++pRefcount->Refcount; NdisReleaseSpinLock( &pRefcount->lock ); // Should be impossible for us to have incremented from zero to one SAFEASSERT( Scratch >= 2L ); } VOID BrdgDecrementWaitRef( IN PWAIT_REFCOUNT pRefcount ) /*++ Routine Description: Decrements (releases) a previously incremented (acquired) wait-refcount. Arguments: pRefcount The wait-refcount to decrement Return Value: none --*/ { LONG Scratch; SAFEASSERT( pRefcount != NULL ); NdisAcquireSpinLock( &pRefcount->lock ); SAFEASSERT( (pRefcount->state == WaitRefEnabled) || (pRefcount->state == WaitRefShuttingDown) ); Scratch = --pRefcount->Refcount; SAFEASSERT( Scratch >= 0L ); if( Scratch == 0L ) { // Signal anyone waiting for the refount to go to zero NdisSetEvent( &pRefcount->Event ); } NdisReleaseSpinLock( &pRefcount->lock ); } VOID BrdgBlockWaitRef( IN PWAIT_REFCOUNT pRefcount ) /*++ Routine Description: Puts the wait-refcount in the shutting-down state, making it impossible for the refcount to be incremented anymore. This can be used to block further acquires of the wait-refcount in advance of the shutdown process. Because shutting down the wait-refcount involves waiting for it to hit zero, this can be called at high IRQL to prevent further acquires of the wait-refcount in advance of a low-IRQL call to BrdgShutdownWaitRef(). Arguments: pRefcount The wait-refcount to block Return Value: none --*/ { SAFEASSERT( pRefcount != NULL ); NdisAcquireSpinLock( &pRefcount->lock ); if( pRefcount->state == WaitRefEnabled ) { pRefcount->state = WaitRefShuttingDown; } else { // Do nothing; the wait-refcount is already // shutting down or is already shut down. SAFEASSERT( (pRefcount->state == WaitRefShutdown) || (pRefcount->state == WaitRefShuttingDown) ); } NdisReleaseSpinLock( &pRefcount->lock ); } BOOLEAN BrdgShutdownWaitRefInternal( IN PWAIT_REFCOUNT pRefcount, IN BOOLEAN bRequireBlockedState ) /*++ Routine Description: Blocks new acquisitions of the wait-refcount and waits for the number of consumers to go to zero. If TRUE is returned, the caller can free any resources protected by the wait-refcount Arguments: pRefcount The wait-refcount to shut down bRequireBlockedState TRUE means the shutdown attempt will fail if the wait-refcount isn't in the shutting-down state Return Value: TRUE if the wait-refcount was shut down FALSE indicates that either the wait-refcount was reset or that another thread of execution had already shut down the wait-refcount. In both cases, the shared resources protected by the wait-refcount should NOT be freed. --*/ { BOOLEAN bSuccess; SAFEASSERT(CURRENT_IRQL == PASSIVE_LEVEL); SAFEASSERT( pRefcount != NULL ); NdisAcquireSpinLock( &pRefcount->lock ); if( pRefcount->state == WaitRefEnabled ) { if( bRequireBlockedState ) { // Caller was expecting the refcount to be shutting // down. It must have been reset. That had better // be OK! SAFEASSERT( pRefcount->bResettable ); bSuccess = FALSE; } else { // Caller doesn't require that the refcount be // shutting down. Transition to the shutting-down // state. pRefcount->state = WaitRefShuttingDown; bSuccess = TRUE; } } else if( pRefcount->state == WaitRefShutdown ) { // Someone else already shut down the waitref. // This always means failure. SAFEASSERT( pRefcount->Refcount == 0L ); bSuccess = FALSE; } else { // The refcount is already shutting down. // This is always goodness. SAFEASSERT( pRefcount->state == WaitRefShuttingDown ); bSuccess = TRUE; } NdisReleaseSpinLock( &pRefcount->lock ); if( bSuccess ) { // Wait for all consumers to be done NdisWaitEvent( &pRefcount->Event, 0/*Wait forever*/ ); NdisAcquireSpinLock( &pRefcount->lock ); if( pRefcount->state == WaitRefEnabled ) { // Someone reactivated us while we were sleeping. SAFEASSERT( pRefcount->bResettable ); bSuccess = FALSE; } else if( pRefcount->state == WaitRefShutdown ) { // Someone else shut us down while we were sleeping. SAFEASSERT( pRefcount->Refcount == 0L ); bSuccess = FALSE; } else { if( pRefcount->Refcount == 0L ) { // We completed the shutdown. pRefcount->state = WaitRefShutdown; bSuccess = TRUE; } else { // The waitref must have been reactivated and // shut down again while we were asleep! SAFEASSERT( pRefcount->bResettable ); bSuccess = FALSE; } } NdisReleaseSpinLock( &pRefcount->lock ); } return bSuccess; } BOOLEAN BrdgShutdownWaitRef( IN PWAIT_REFCOUNT pRefcount ) { return BrdgShutdownWaitRefInternal( pRefcount, FALSE ); } BOOLEAN BrdgShutdownBlockedWaitRef( IN PWAIT_REFCOUNT pRefcount ) { return BrdgShutdownWaitRefInternal( pRefcount, TRUE ); } VOID BrdgResetWaitRef( IN PWAIT_REFCOUNT pRefcount ) /*++ Routine Description: Re-enables a wait-refcount. Safe to call in any refcount state; if the refcount is shut down, this will re-enable it. If the refcount is in the middle of shutting down, this will flag it to be re-enabled if the code shutting down the waitref is using BrdgShutdownOrResetWaitRef(). Arguments: pRefcount The wait-refcount Return Value: none --*/ { SAFEASSERT( pRefcount != NULL ); NdisAcquireSpinLock( &pRefcount->lock ); if( pRefcount->state == WaitRefShutdown ) { // The wait-refcount is completely shut down. We // can reactivate it. SAFEASSERT( pRefcount->Refcount == 0L ); pRefcount->state = WaitRefEnabled; } else if( pRefcount->state == WaitRefShuttingDown ) { if( pRefcount->bResettable ) { // Re-enable. The call to BrdgShutdownWaitRef() // or BrdgShutdownBlockedWaitRef() will return // FALSE. pRefcount->state = WaitRefEnabled; } else { // Not allowed to reset this refcount when // in the middle of shutting down SAFEASSERT( FALSE ); } } else { // The wait-refcount is already enabled SAFEASSERT( pRefcount->state == WaitRefEnabled ); } NdisReleaseSpinLock( &pRefcount->lock ); }