/*++
*******************************************************************************
* 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"

#ifdef STRATUS_MEM

ULONG gPoolTag = 'igiD';

#else

#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
#endif
