/*++
*******************************************************************************
* 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 ) );
         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 ) );
            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) );
}


// Extended memory allocation routine which allows to specify the chunk tag.
// It is very useful for our Digi driver stacks because it is easier to determine 
// which layer consumes what memory
PVOID
DigiAllocMemEx(
   IN POOL_TYPE PoolType,
   IN ULONG Length,
   IN ULONG Tag )
/*++

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),
                                            Tag)) == 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" ) );     
   }

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

   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
