/*++
*******************************************************************************
* Copyright (c) 1990-98 Digi International Inc., All Rights Reserved				*
* 																										*
* This software contains proprietary and confidential information of Digi		*
* International Inc.  By accepting transfer of this copy, Recipient agrees		*
* to retain this software in confidence, to prevent disclosure to others,		*
* and to make no use of this software other than that for which it was			*
* delivered.  This is an unpublished copyrighted work of Digi International	*
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly	*
* prohibited.																						*
* 																										*
* Restricted Rights Legend																		*
*																										*
* Use, duplication, or disclosure by the Government is subject to					*
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in			*
* Technical Data and Computer Software clause at DFARS 252.227-7031 or			*
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -				*
* Restricted Rights at 48 CFR 52.227-19, as applicable.								*
*																										*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343				*
*																										*
*******************************************************************************

Module Name:

   digialloc.c

Abstract:

   Memory alloc routines.

Author:

   Michael Zarns

--*/

#include "digilib.h"

#if DBG || DIGIALLOC_VERIFY

typedef struct DIGI_MEM_DESCRIPTOR
{
   ULONG BeginTag;
   ULONG Length;
   LIST_ENTRY ListEntry;
} DIGI_MEM_DESCRIPTOR, *PDIGI_MEM_DESCRIPTOR;


//
// Global Data
//


STATIC BOOLEAN    gInitialized = FALSE;

STATIC ULONG      gPoolTag = (ULONG)('igiD');

STATIC LIST_ENTRY gPagedMemQueue = {&gPagedMemQueue,&gPagedMemQueue};
STATIC FAST_MUTEX gPagedMemMutex = {0};

STATIC LIST_ENTRY gNonPagedMemQueue = {&gNonPagedMemQueue,&gNonPagedMemQueue};
STATIC KSPIN_LOCK gNonPagedMemLock  = {0};


VOID 
DigiInitMem( IN ULONG Tag )
/*++

Routine Description:

Arguments:

   Tag - Tag to use when allocating memory.

Return Value:

   None.

--*/
{
   gPoolTag = Tag;

	if ( gInitialized != TRUE )
	{
	   InitializeListHead( &gPagedMemQueue );
		ExInitializeFastMutex( &gPagedMemMutex );

	   InitializeListHead( &gNonPagedMemQueue );
	   KeInitializeSpinLock( &gNonPagedMemLock );

		gInitialized = TRUE;
	}
}


// TAG_PAGED, DgPg, is tag used for paged memory allocs
#define TAG_PAGED				( (ULONG)'gPgD' )

// TAG_NONPAGED, DgNp, is tag used for nonpaged memory allocs
#define TAG_NONPAGED			( (ULONG)'pNgD' )

// TAG_END, Digi, is tag used at end of every alloc'd buffer
#define TAG_END				( (ULONG)'igid' )

// TAG_FREE_BEGIN and TAG_FREE_END are written over BeginTag and EndTag, respectively,
// when memory is freed to show that it's memory we've used, but freed.
#define TAG_FREE_BEGIN		( (ULONG)'bFgD' )
#define TAG_FREE_END			( (ULONG)'eFgD' )


STATIC VOID 
DigiCheckMem( VOID )  
{                                                                             
   PLIST_ENTRY _DigiQueue;                                                    
   KIRQL _OldIrql;

   _DigiQueue = &gNonPagedMemQueue;                                      
                                                                              
   KeAcquireSpinLock( &gNonPagedMemLock, &_OldIrql );                        
                                                                              
   while( _DigiQueue->Flink != &gNonPagedMemQueue )                      
   {                                                                          
      PDIGI_MEM_DESCRIPTOR _MemDesc;                                          
      PUCHAR _Buf;                                                            
                                                                              
      _MemDesc = CONTAINING_RECORD( _DigiQueue->Flink,                        
                                    DIGI_MEM_DESCRIPTOR,                      
                                    ListEntry );                              
                                                                              
      _Buf = (PUCHAR)_MemDesc + sizeof(DIGI_MEM_DESCRIPTOR);                  
      if( (_MemDesc->BeginTag != TAG_NONPAGED) ||
          (*(PULONG)(_Buf + _MemDesc->Length) != TAG_END) )             
      {                                                                       
         DigiDump( DEBUG_ERROR, ( "DigiCheckMem NonPagedPool corruption found (0x%x)\n", _MemDesc ) );
         DbgBreakPoint();                                                     
         break;                                                               
      }                                                                       
                                                                              
      _DigiQueue = _DigiQueue->Flink;                                         
   }                                                                          
   KeReleaseSpinLock( &gNonPagedMemLock, _OldIrql );                         
                                                                           
	if ( KeGetCurrentIrql() < DISPATCH_LEVEL )
	{
		ExAcquireFastMutex( &gPagedMemMutex );
	   _DigiQueue = &gPagedMemQueue;                                      
	   while( _DigiQueue->Flink != &gPagedMemQueue)
	   {                                                                       
	      PDIGI_MEM_DESCRIPTOR _MemDesc;                                       
	      PUCHAR _Buf;                                                         
	                                                                           
	      _MemDesc = CONTAINING_RECORD( _DigiQueue->Flink,                     
	                                    DIGI_MEM_DESCRIPTOR,                   
	                                    ListEntry );                           
	                                                                           
	      _Buf = (PUCHAR)_MemDesc + sizeof(DIGI_MEM_DESCRIPTOR);               
	      if( (_MemDesc->BeginTag != TAG_PAGED) ||
	          (*(PULONG)(_Buf + _MemDesc->Length) != TAG_END) )
	      {                                                                    
	         DigiDump( DEBUG_ERROR, ( "DigiCheckMem PagedPool corruption found (0x%x)\n", _MemDesc ) );  
	         DbgBreakPoint();                                                  
	         break;                                                            
	      }                                                                    
	                                                                           
	      _DigiQueue = _DigiQueue->Flink;                                      
	   }                                                                       
		ExReleaseFastMutex( &gPagedMemMutex );
	}
}

PVOID 
DigiAllocMem( 
	IN POOL_TYPE PoolType, 
	IN ULONG Length )
/*++

Routine Description:

Arguments:

Return Value:

   None.

--*/
{
   KIRQL OldIrql;
   PDIGI_MEM_DESCRIPTOR buf;
   ULONG	Len = ((Length + (sizeof(ULONG)-1)) & ~(sizeof(ULONG)-1));

	if ( !gInitialized )
		DigiInitMem( gPoolTag );
	
#if DBG
	// Check memory consistency only for debug drivers.
	// If we run certain WHQL stress tests or on heavy troughput this 
	// check degrades performance significantly
	DigiCheckMem();
#endif
	
   if( (buf = (PVOID)ExAllocatePoolWithTag( PoolType,
                                            Len + sizeof(DIGI_MEM_DESCRIPTOR) + sizeof(ULONG),
                                            gPoolTag)) == NULL )
      return(NULL);

   buf->Length = Len;
   buf->BeginTag = PoolType == PagedPool ? TAG_PAGED : TAG_NONPAGED;

   *(PULONG)((PUCHAR)buf + Len + sizeof(DIGI_MEM_DESCRIPTOR)) = TAG_END;

   //
   // Insert onto tail of global memory queue
   //

   if( PoolType == PagedPool )
   {
   	ExAcquireFastMutex( &gPagedMemMutex );
      InsertTailList( &gPagedMemQueue, &buf->ListEntry );
		ExReleaseFastMutex( &gPagedMemMutex );
   }
   else
		ExInterlockedInsertTailList( &gNonPagedMemQueue, &buf->ListEntry, &gNonPagedMemLock );

   return( (PUCHAR)buf + sizeof (DIGI_MEM_DESCRIPTOR) );
}


VOID 
DigiFreeMem( IN PVOID Buf )
/*++

Routine Description:

   Does consistency check on passed in memory block and free's the memory
   block.

Arguments:

   Buf - pointer to memory block which is to be freed.

Return Value:

   None.

--*/
{
   PDIGI_MEM_DESCRIPTOR RealBuf;
   KIRQL OldIrql;
   ULONG	Length;

	ASSERT( gInitialized );

	if ( !gInitialized )
		return ;  
   
#if DBG
	// Check memory consistency only for debug drivers.
	// If we run certain WHQL stress tests or on heavy troughput this 
	// check degrades performance significantly
	DigiCheckMem();
#endif

   RealBuf = (PDIGI_MEM_DESCRIPTOR)((PUCHAR)Buf - sizeof(DIGI_MEM_DESCRIPTOR));
   Length = RealBuf->Length;

   if( RealBuf->BeginTag != TAG_NONPAGED &&
       RealBuf->BeginTag != TAG_PAGED )
   {
      DigiDump( DEBUG_ERROR, ( "Memory has been corrupted!\n" ) );
      DbgBreakPoint();
   }

   if( *(PULONG)((PUCHAR)Buf + Length) != TAG_END )
   {
      DigiDump( DEBUG_ERROR, ( "Memory Overrun\n" ) );
      DbgBreakPoint();
   }

	if ( RealBuf->BeginTag == TAG_NONPAGED )
	{
	   KeAcquireSpinLock( &gNonPagedMemLock, &OldIrql );
	   RemoveEntryList( &(RealBuf->ListEntry) );
	   KeReleaseSpinLock( &gNonPagedMemLock, OldIrql );
	}
	else
	{
		ExAcquireFastMutex( &gPagedMemMutex );
	   RemoveEntryList( &(RealBuf->ListEntry) );
		ExReleaseFastMutex( &gPagedMemMutex );
	}

   // Clear the tags
   RealBuf->BeginTag = TAG_FREE_BEGIN;
   *(PULONG)((PUCHAR)Buf + Length) = TAG_FREE_END;

   ExFreePool( RealBuf );
}


VOID DigiDisplayMemoryLeaks( VOID )  
{                                                                             
   PLIST_ENTRY _DigiQueue;                                                    
   KIRQL _OldIrql;

	if ( !gInitialized )
	{
		DigiDump( DEBUG_ERROR, ( "ERROR: DisplayMemoryLeaks failed!  Not initialized!\n" ) );
		return ;
	}

   _DigiQueue = &gNonPagedMemQueue;                                      
                                                                              
   KeAcquireSpinLock( &gNonPagedMemLock, &_OldIrql );                        
                                                                              
	if ( IsListEmpty( &gNonPagedMemQueue ) )
	{
		DigiDump( DEBUG_ERROR, ( "Digi Memory: No NonPaged memory leaks\n" ) );
	}
	else
	{
		DigiDump( DEBUG_ERROR, ( "Digi Memory: NonPaged memory leaks:\n" ) );
		DigiDump( DEBUG_ERROR, ( "   Address      Length       First Dword\n" ) );
	   while( _DigiQueue->Flink != &gNonPagedMemQueue )                      
	   {                                                                          
	      PDIGI_MEM_DESCRIPTOR _MemDesc;                                          
	      PUCHAR _Buf;                                                            
	                                                                              
	      _MemDesc = CONTAINING_RECORD( _DigiQueue->Flink,                        
	                                    DIGI_MEM_DESCRIPTOR,                      
	                                    ListEntry );                              
	                                                                              
	      _Buf = (PUCHAR)_MemDesc + sizeof(DIGI_MEM_DESCRIPTOR);                  
			DigiDump( DEBUG_ERROR, ( "   0x%08x   0x%08x   0x%08x\n", _Buf, _MemDesc->Length, *(PULONG)_Buf ) );
	      _DigiQueue = _DigiQueue->Flink;                                         
	   }                                                                          
		DigiDump( DEBUG_ERROR, ( "   End\n" ) );
	}

   KeReleaseSpinLock( &gNonPagedMemLock, _OldIrql );                         

	ASSERT( KeGetCurrentIrql() < DISPATCH_LEVEL );

	if ( IsListEmpty( &gNonPagedMemQueue ) )
	{
		DigiDump( DEBUG_ERROR, ( "Digi Memory: No Paged memory leaks\n" ) );
	}
	else
	{
	   _DigiQueue = &gPagedMemQueue;                                      
		DigiDump( DEBUG_ERROR, ( "Digi Memory: Paged memory leaks:\n" ) );
		DigiDump( DEBUG_ERROR, ( "   Address      Length       First Dword\n" ) );
	   while( _DigiQueue->Flink != &gPagedMemQueue )
	   {                                                                       
	      PDIGI_MEM_DESCRIPTOR _MemDesc;                                       
	      PUCHAR _Buf;                                                         
	                                                                           
	      _MemDesc = CONTAINING_RECORD( _DigiQueue->Flink, DIGI_MEM_DESCRIPTOR, ListEntry );                           
	      _Buf = (PUCHAR)_MemDesc + sizeof(DIGI_MEM_DESCRIPTOR);               
			DigiDump( DEBUG_ERROR, ( "   0x%08x   0x%08x   0x%08x\n", _Buf, _MemDesc->Length, *(PULONG)_Buf ) );
	      _DigiQueue = _DigiQueue->Flink;                                      
	   }                                                                       
		DigiDump( DEBUG_ERROR, ( "   End\n" ) );
	}
}                                                                             

#endif
