windows-nt/Source/XPSP1/NT/net/sfm/atalk/sys/atkmem.c

1535 lines
33 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*
Copyright (c) 1992 Microsoft Corporation
Module Name:
atkmem.c
Abstract:
This module contains the routines which allocates and free memory. Only
the non-paged pool is used.
!!! For profiling, we use spinlock acquire/release for CurAllocCount/CurAllocSize
Author:
Nikhil Kamkolkar (NikhilK@microsoft.com)
Jameel Hyder (JameelH@microsoft.com)
Revision History:
25 Apr 1992 Initial Version (JameelH)
--*/
#include <atalk.h>
#pragma hdrstop
#define FILENUM ATKMEM
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, AtalkInitMemorySystem)
#pragma alloc_text(PAGE, AtalkDeInitMemorySystem)
#endif
VOID
AtalkInitMemorySystem(
VOID
)
{
LONG i;
for (i = 0; i < NUM_BLKIDS; i++)
INITIALIZE_SPIN_LOCK(&atalkBPLock[i]);
AtalkTimerInitialize(&atalkBPTimer,
atalkBPAgePool,
BLOCK_POOL_TIMER);
AtalkTimerScheduleEvent(&atalkBPTimer);
}
VOID
AtalkDeInitMemorySystem(
VOID
)
{
LONG i, j, NumBlksPerChunk;
PBLK_CHUNK pChunk, pFree;
for (i = 0; i < NUM_BLKIDS; i++)
{
NumBlksPerChunk = atalkNumBlks[i];
for (pChunk = atalkBPHead[i];
pChunk != NULL;
NOTHING)
{
DBGPRINT(DBG_COMP_RESOURCES, DBG_LEVEL_INFO,
("AtalkDeInitMemorySystem: Freeing %lx\n", pChunk));
if ((pChunk->bc_NumFree != NumBlksPerChunk) ||
(pChunk->bc_NumAlloc != 0))
{
DBGPRINT(DBG_COMP_RESOURCES, DBG_LEVEL_ERR,
("AtalkDeInitMemorySystem: Unfreed blocks from blockid %d, Chunk %lx\n",
i, pChunk));
ASSERT(0);
}
if (pChunk->bc_BlkId >= BLKID_NEED_NDIS_INT)
{
PBLK_HDR pBlkHdr;
// We need to free the Ndis stuff for these guys
for (j = 0, pBlkHdr = pChunk->bc_FreeHead;
j < NumBlksPerChunk;
j++, pBlkHdr = pBlkHdr->bh_Next)
{
PBUFFER_HDR pBufHdr;
pBufHdr = (PBUFFER_HDR)((PBYTE)pBlkHdr + sizeof(BLK_HDR));
ASSERT(pBufHdr->bh_NdisPkt == NULL);
AtalkNdisFreeBuffer(pBufHdr->bh_NdisBuffer);
}
}
pFree = pChunk;
pChunk = pChunk->bc_Next;
AtalkFreeMemory(pFree);
}
atalkBPHead[i] = NULL;
}
}
PVOID FASTCALL
AtalkAllocMem(
#ifdef TRACK_MEMORY_USAGE
IN ULONG Size,
IN ULONG FileLine
#else
IN ULONG Size
#endif // TRACK_MEMORY_USAGE
)
/*++
Routine Description:
Allocate a block of non-paged memory. This is just a wrapper over ExAllocPool.
Allocation failures are error-logged. We always allocate a ULONG more than
the specified size to accomodate the size. This is used by AtalkFreeMemory
to update the statistics.
Arguments:
Return Value:
--*/
{
PBYTE pBuf;
BOOLEAN zeroed;
#ifdef PROFILING
TIME TimeS, TimeE, TimeD;
#endif
// round up the size so that we can put a signature at the end
// that is on a ULONG boundary
zeroed = ((Size & ZEROED_MEMORY_TAG) == ZEROED_MEMORY_TAG);
Size = DWORDSIZEBLOCK(Size & ~ZEROED_MEMORY_TAG) +
#if DBG
sizeof(DWORD) + // For the signature
#endif
sizeof(ULONG_PTR);
#ifdef PROFILING
TimeS = KeQueryPerformanceCounter(NULL);
#endif
// Do the actual memory allocation. Allocate four extra bytes so
// that we can store the size of the allocation for the free routine.
if ((pBuf = ExAllocatePoolWithTag(NonPagedPool, Size, ATALK_TAG)) == NULL)
{
LOG_ERROR(EVENT_ATALK_MEMORYRESOURCES, STATUS_INSUFFICIENT_RESOURCES, NULL, 0);
DBGPRINT(DBG_COMP_RESOURCES, DBG_LEVEL_FATAL,
("AtalkAllocMemory: failed - size %lx\n", Size));
return NULL;
}
#ifdef PROFILING
INTERLOCKED_INCREMENT_LONG(&AtalkStatistics.stat_CurAllocCount,
&AtalkStatsLock.SpinLock);
INTERLOCKED_INCREMENT_LONG(&AtalkStatistics.stat_ExAllocPoolCount,
&AtalkStatsLock.SpinLock);
TimeE = KeQueryPerformanceCounter(NULL);
TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
INTERLOCKED_ADD_LARGE_INTGR(&AtalkStatistics.stat_ExAllocPoolTime,
TimeD,
&AtalkStatsLock.SpinLock);
#endif
INTERLOCKED_ADD_ULONG(&AtalkStatistics.stat_CurAllocSize,
Size,
&AtalkStatsLock.SpinLock);
ASSERTMSG("AtalkAllocMemory: Allocation has exceeded Limit !!!\n",
AtalkStatistics.stat_CurAllocSize < (ULONG)AtalkMemLimit);
// Save the size of this block in the four extra bytes we allocated.
*((PULONG)pBuf) = Size;
#if DBG
*((PULONG)(pBuf+Size-sizeof(ULONG))) = ATALK_MEMORY_SIGNATURE;
DBGPRINT(DBG_COMP_RESOURCES, DBG_LEVEL_INFO,
("AtalkAllocMemory: Allocated %lx bytes @%lx\n", Size, pBuf));
#endif
AtalkTrackMemoryUsage((PVOID)pBuf, Size, TRUE, FileLine);
#if DBG
Size -= sizeof(ULONG);
#endif
pBuf += sizeof(ULONG_PTR);
if (zeroed)
{
RtlZeroMemory(pBuf, Size - sizeof(ULONG_PTR));
}
// Return a pointer to the memory after the size longword.
return (pBuf);
}
VOID FASTCALL
AtalkFreeMemory(
IN PVOID pBuf
)
/*++
Routine Description:
Free the block of memory allocated via AtalkAllocMemory. This is
a wrapper around ExFreePool.
Arguments:
Return Value:
--*/
{
PULONG pRealBuffer;
ULONG Size;
#ifdef PROFILING
TIME TimeS, TimeE, TimeD;
#endif
// Get a pointer to the block allocated by ExAllocatePool.
pRealBuffer = (PULONG)((PCHAR)pBuf - sizeof(ULONG_PTR));
Size = *pRealBuffer;
AtalkTrackMemoryUsage(pRealBuffer, Size, FALSE, 0);
#if DBG
*pRealBuffer = 0;
// Check the signature at the end
if (*(PULONG)((PCHAR)pRealBuffer + Size - sizeof(ULONG))
!= ATALK_MEMORY_SIGNATURE)
{
DBGPRINT(DBG_COMP_RESOURCES, DBG_LEVEL_FATAL,
("AtalkFreeMemory: Memory overrun on block %lx\n", pRealBuffer));
ASSERT(0);
}
*(PULONG)((PCHAR)pRealBuffer + Size - sizeof(ULONG)) = 0;
#endif
INTERLOCKED_ADD_ULONG(&AtalkStatistics.stat_CurAllocSize,
-(LONG)Size,
&AtalkStatsLock.SpinLock);
#ifdef PROFILING
INTERLOCKED_DECREMENT_LONG(&AtalkStatistics.stat_CurAllocCount,
&AtalkStatsLock);
INTERLOCKED_INCREMENT_LONG(&AtalkStatistics.stat_ExFreePoolCount,
&AtalkStatsLock);
TimeS = KeQueryPerformanceCounter(NULL);
#endif
// Free the pool and return.
ExFreePool(pRealBuffer);
#ifdef PROFILING
TimeE = KeQueryPerformanceCounter(NULL);
TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
INTERLOCKED_ADD_LARGE_INTGR(&AtalkStatistics.stat_ExFreePoolTime,
TimeD,
&AtalkStatsLock.SpinLock);
#endif
}
PBUFFER_DESC
AtalkDescribeBufferDesc(
IN PVOID DataPtr,
IN PVOID FreePtr,
IN USHORT Length,
#ifdef TRACK_BUFFDESC_USAGE
IN USHORT Flags,
IN ULONG FileLine
#else
IN USHORT Flags
#endif
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PBUFFER_DESC pBuffDesc;
if ((pBuffDesc = AtalkAllocBufferDesc(DataPtr,
Length,
#ifdef TRACK_BUFFDESC_USAGE
Flags,
FileLine
#else
Flags
#endif
)) != NULL)
{
pBuffDesc->bd_FreeBuffer = FreePtr;
}
return pBuffDesc;
}
PBUFFER_DESC
AtalkAllocBufferDesc(
IN PVOID Ptr OPTIONAL,
IN USHORT Length,
#ifdef TRACK_BUFFDESC_USAGE
IN USHORT Flags,
IN ULONG FileLine
#else
IN USHORT Flags
#endif
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PBUFFER_DESC pBuffDesc = NULL;
DBGPRINT(DBG_COMP_RESOURCES, DBG_LEVEL_INFO,
("AtalkAllocBuffDesc: Ptr %lx, Length %x, Flags %x\n",
Ptr, Length, Flags));
pBuffDesc = AtalkBPAllocBlock(BLKID_BUFFDESC);
if (pBuffDesc != NULL)
{
#if DBG
pBuffDesc->bd_Signature = BD_SIGNATURE;
#endif
pBuffDesc->bd_Length = Length;
pBuffDesc->bd_Flags = Flags;
pBuffDesc->bd_Next = NULL;
// Depending on whether a char buffer or a PAMDL is being
// passed in...
if (Flags & BD_CHAR_BUFFER)
{
if ((Ptr == NULL) &&
((Ptr = AtalkAllocMemory(Length)) == NULL))
{
pBuffDesc->bd_Flags = 0;
pBuffDesc->bd_CharBuffer = NULL;
AtalkFreeBuffDesc(pBuffDesc);
pBuffDesc = NULL;
}
else
{
pBuffDesc->bd_CharBuffer = pBuffDesc->bd_FreeBuffer = Ptr;
AtalkTrackBuffDescUsage(pBuffDesc, TRUE, FileLine);
}
}
else
{
pBuffDesc->bd_OpaqueBuffer = (PAMDL)Ptr;
}
}
return pBuffDesc;
}
VOID FASTCALL
AtalkFreeBuffDesc(
IN PBUFFER_DESC pBuffDesc
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
ASSERT(VALID_BUFFDESC(pBuffDesc));
if ((pBuffDesc->bd_Flags & (BD_FREE_BUFFER | BD_CHAR_BUFFER)) ==
(BD_FREE_BUFFER | BD_CHAR_BUFFER))
AtalkFreeMemory(pBuffDesc->bd_FreeBuffer);
AtalkTrackBuffDescUsage(pBuffDesc, FALSE, 0);
AtalkBPFreeBlock(pBuffDesc);
}
VOID
AtalkCopyBuffDescToBuffer(
IN PBUFFER_DESC pBuffDesc,
IN LONG SrcOff,
IN LONG BytesToCopy,
IN PBYTE DstBuf
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
NTSTATUS Status;
ULONG BytesCopied;
LONG Index = 0;
ASSERT(VALID_BUFFDESC(pBuffDesc));
while ((pBuffDesc != NULL) &&
(SrcOff > (LONG)pBuffDesc->bd_Length))
{
SrcOff -= pBuffDesc->bd_Length;
pBuffDesc = pBuffDesc->bd_Next;
}
do
{
LONG ThisCopy;
if (pBuffDesc == NULL)
break;
ThisCopy = BytesToCopy;
if (ThisCopy > ((LONG)pBuffDesc->bd_Length - SrcOff))
ThisCopy = ((LONG)pBuffDesc->bd_Length - SrcOff);
BytesToCopy -= ThisCopy;
if (pBuffDesc->bd_Flags & BD_CHAR_BUFFER)
{
RtlCopyMemory(DstBuf + Index,
pBuffDesc->bd_CharBuffer + SrcOff,
ThisCopy);
}
else
{
Status = TdiCopyMdlToBuffer(pBuffDesc->bd_OpaqueBuffer,
SrcOff,
DstBuf + Index,
0,
ThisCopy,
&BytesCopied);
ASSERT(NT_SUCCESS(Status) && (BytesCopied == (ULONG)ThisCopy));
}
Index += ThisCopy;
SrcOff -= (pBuffDesc->bd_Length - ThisCopy);
pBuffDesc = pBuffDesc->bd_Next;
} while (BytesToCopy > 0);
}
VOID
AtalkCopyBufferToBuffDesc(
IN PBYTE SrcBuf,
IN LONG BytesToCopy,
IN PBUFFER_DESC pBuffDesc,
IN LONG DstOff
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
NTSTATUS Status;
LONG Index = 0;
ASSERT(VALID_BUFFDESC(pBuffDesc));
while ((DstOff > (LONG)pBuffDesc->bd_Length) &&
(pBuffDesc != NULL))
{
DstOff -= pBuffDesc->bd_Length;
pBuffDesc = pBuffDesc->bd_Next;
}
do
{
LONG ThisCopy;
if (pBuffDesc == NULL)
break;
ThisCopy = BytesToCopy;
if (ThisCopy > ((LONG)pBuffDesc->bd_Length - DstOff))
ThisCopy = ((LONG)pBuffDesc->bd_Length - DstOff);
BytesToCopy -= ThisCopy;
if (pBuffDesc->bd_Flags & BD_CHAR_BUFFER)
{
RtlCopyMemory(pBuffDesc->bd_CharBuffer + DstOff,
SrcBuf + Index,
ThisCopy);
}
else
{
Status = TdiCopyBufferToMdl(SrcBuf,
Index,
ThisCopy,
pBuffDesc->bd_OpaqueBuffer,
DstOff,
(PULONG)&ThisCopy);
ASSERT(NT_SUCCESS(Status) && (ThisCopy == BytesToCopy));
}
Index += ThisCopy;
DstOff -= (pBuffDesc->bd_Length - ThisCopy);
pBuffDesc = pBuffDesc->bd_Next;
} while (BytesToCopy > 0);
}
LONG FASTCALL
AtalkSizeBuffDesc(
IN PBUFFER_DESC pBuffDesc
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
LONG Size;
ASSERT(VALID_BUFFDESC(pBuffDesc));
for (Size = 0; pBuffDesc != NULL; pBuffDesc = pBuffDesc->bd_Next)
Size += (LONG)pBuffDesc->bd_Length;
return(Size);
}
PAMDL
AtalkSubsetAmdl(
IN PAMDL pStartingMdl,
IN ULONG TotalOffset,
IN ULONG DesiredLength
)
/*++
Routine Description:
This routine is called to build an Mdl chain from a source Mdl chain and
offset into it. We assume we don't know the length of the source Mdl chain,
and we must allocate and build the Mdls for the destination chain, which
we do from non-paged pool. Note that this routine, unlike the IO subsystem
routines, sets the SystemVaMapped bit in the generated Mdls to the same
value as that in the source Mdls.
IT WOULD BE BAD TO USE MmMapLockedPages OR MmProbeAndLockPages ON THE
DESTINATION MDLS UNLESS YOU TAKE RESPONSIBILITY FOR UNMAPPING THEM!
The MDLs that are returned are mapped and locked. (Actually, the pages in
them are in the same state as those in the source MDLs.)
If the system runs out of memory while we are building the destination
MDL chain, we completely clean up the built chain and return with
NewCurrentMdl and NewByteOffset set to the current values of CurrentMdl
and TotalOffset. TRUELength is set to 0.
Environment:
Kernel Mode, Source Mdls locked. It is recommended, although not required,
that the source Mdls be mapped and locked prior to calling this routine.
Arguments:
pStartingMdl - the source appletalk mdl ( == nt mdl)
TotalOffset - Offset within this MDL to start the packet at.
DesiredLength - The number of bytes to insert into the packet.
Return Value:
pointer to build mdl, NULL if out of resources, or lengths inconsistent.
--*/
{
PMDL Destination=NULL;
PBYTE BaseVa;
ULONG NewMdlLength;
PMDL NewMdl;
PMDL pMdl;
PMDL CurrentMdl = (PMDL)pStartingMdl;
ULONG CurrentOffset = TotalOffset;
ULONG BytesToDescribe = DesiredLength;
//
// first make sure that we have enough bytes!
//
if (DesiredLength > (ULONG)AtalkSizeMdlChain(pStartingMdl))
{
DBGPRINT(DBG_COMP_UTILS, DBG_LEVEL_ERR,
("AtalkSubsetMdl: req len (%ld) exceeds avl len (%ld) for mdl %lx\n",
DesiredLength, AtalkSizeMdlChain(pStartingMdl),pStartingMdl));
ASSERT(0);
return(NULL);
}
//
// first, get to the right Mdl (in most cases, the same as pStartingMdl)
//
while (CurrentMdl && (CurrentOffset >= MmGetMdlByteCount (CurrentMdl)))
{
CurrentOffset -= MmGetMdlByteCount (CurrentMdl);
CurrentMdl = CurrentMdl->Next;
ASSERT(CurrentMdl != NULL);
}
while (BytesToDescribe)
{
ASSERT(CurrentMdl != NULL);
BaseVa = (PBYTE)MmGetMdlVirtualAddress(CurrentMdl) + CurrentOffset;
NewMdlLength = MmGetMdlByteCount (CurrentMdl) - CurrentOffset;
// if Mdl has more bytes than what's needed, set available to what's needed
if (NewMdlLength > BytesToDescribe)
{
NewMdlLength = BytesToDescribe;
}
pMdl = IoAllocateMdl(BaseVa,
NewMdlLength,
FALSE,
FALSE,
NULL);
// store the first mdl for return
if (!Destination)
{
Destination = pMdl;
// subsequent Mdl's must start with offset 0!!
CurrentOffset = 0;
}
else
{
// link-in to the earlier mdl
NewMdl->Next = pMdl;
}
NewMdl = pMdl;
if (pMdl != NULL)
{
ATALK_DBG_INC_COUNT(AtalkDbgMdlsAlloced);
#ifdef PROFILING
INTERLOCKED_INCREMENT_LONG(
&AtalkStatistics.stat_CurMdlCount,
&AtalkStatsLock.SpinLock);
#endif
IoBuildPartialMdl(CurrentMdl,
pMdl,
BaseVa,
NewMdlLength);
}
else
{
// free up whatever was allocated so far
while (Destination)
{
pMdl = Destination->Next;
IoFreeMdl(Destination);
ATALK_DBG_DEC_COUNT(AtalkDbgMdlsAlloced);
Destination = pMdl;
}
}
BytesToDescribe -= NewMdlLength;
CurrentMdl = CurrentMdl->Next;
}
return(Destination);
}
PAMDL
AtalkAllocAMdl(
IN PBYTE pBuffer OPTIONAL,
IN LONG Size
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PMDL pMdl = NULL;
if (pBuffer == NULL)
{
pBuffer = AtalkAllocMemory(Size);
}
if ((pBuffer == NULL) ||
((pMdl = IoAllocateMdl(pBuffer, Size, FALSE, FALSE, NULL)) == NULL))
{
LOG_ERROR(EVENT_ATALK_RESOURCES, STATUS_INSUFFICIENT_RESOURCES, NULL, 0);
}
#ifdef PROFILING
else
{
INTERLOCKED_INCREMENT_LONG(
&AtalkStatistics.stat_CurMdlCount,
&AtalkStatsLock.SpinLock);
}
#endif
if (pMdl != NULL)
{
ATALK_DBG_INC_COUNT(AtalkDbgMdlsAlloced);
MmBuildMdlForNonPagedPool(pMdl);
}
return(pMdl);
}
LONG FASTCALL
AtalkSizeMdlChain(
IN PAMDL pAMdlChain
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
LONG Size;
for (Size = 0; pAMdlChain != NULL; pAMdlChain = pAMdlChain->Next)
{
Size += MmGetMdlByteCount(pAMdlChain);
}
return(Size);
}
/*** AtalkBPAllocBlock
*
* Alloc a block of memory from the block pool package. This is written to speed up
* operations where a lot of small fixed size allocations/frees happen. Going to
* ExAllocPool() in these cases is expensive.
*
* It is important to keep the list of blocks in such a way that all completely free
* blocks are at the tail end of the list.
*/
PVOID FASTCALL
AtalkBPAllocBlock(
IN BLKID BlockId
)
{
PBLK_HDR pBlk = NULL;
PBLK_CHUNK pChunk, *ppChunkHead;
KIRQL OldIrql;
USHORT BlkSize;
ATALK_SPIN_LOCK * pLock;
NDIS_STATUS ndisStatus;
#ifdef PROFILING
TIME TimeS, TimeE, TimeD;
TimeS = KeQueryPerformanceCounter(NULL);
#endif
ASSERT (BlockId < NUM_BLKIDS);
BlkSize = atalkBlkSize[BlockId];
ppChunkHead = &atalkBPHead[BlockId];
pLock = &atalkBPLock[BlockId];
ACQUIRE_SPIN_LOCK(pLock, &OldIrql);
if ((pChunk = *ppChunkHead) != NULL)
{
ASSERT(VALID_BC(pChunk));
ASSERT(pChunk->bc_BlkId == BlockId);
ASSERT((pChunk->bc_NumFree + pChunk->bc_NumAlloc) == atalkNumBlks[BlockId]);
if (pChunk->bc_NumFree > 0)
{
// This is where we take it off from
DBGPRINT(DBG_COMP_SYSTEM, DBG_LEVEL_INFO,
("AtalkBPAllocBlock: Found space in Chunk %lx\n", pChunk));
#ifdef PROFILING
INTERLOCKED_INCREMENT_LONG( &AtalkStatistics.stat_NumBPHits,
&AtalkStatsLock.SpinLock);
#endif
}
if (pChunk->bc_NumFree == 0)
{
// We do not have space on any of the chunks on this list
ASSERT(pChunk->bc_NumAlloc == atalkNumBlks[BlockId]);
pChunk = NULL;
}
}
if (pChunk == NULL)
{
DBGPRINT(DBG_COMP_SYSTEM, DBG_LEVEL_INFO,
("AtalkBPAllocBlock: Allocating a new chunk for Id %d\n", BlockId));
#ifdef PROFILING
INTERLOCKED_INCREMENT_LONG( &AtalkStatistics.stat_NumBPMisses,
&AtalkStatsLock.SpinLock);
#endif
pChunk = AtalkAllocMemory(atalkChunkSize[BlockId]);
if (pChunk != NULL)
{
LONG i, j;
PBLK_HDR pBlkHdr;
PBUFFER_HDR pBufHdr;
USHORT NumBlksPerChunk;
#if DBG
pChunk->bc_Signature = BC_SIGNATURE;
pChunk->bc_NumAlloc = 0;
#endif
NumBlksPerChunk = atalkNumBlks[BlockId];
ASSERT (NumBlksPerChunk <= 0xFF);
pChunk->bc_NumFree = (BYTE)NumBlksPerChunk;
pChunk->bc_BlkId = BlockId;
pChunk->bc_FreeHead = (PBLK_HDR)((PBYTE)pChunk + sizeof(BLK_CHUNK));
DBGPRINT(DBG_COMP_SYSTEM, DBG_LEVEL_INFO,
("AtalkBPAllocBlock: Initializing chunk %lx, BlkId=%d\n", pChunk,BlockId));
// Initialize the blocks in the chunk
for (i = 0, pBlkHdr = pChunk->bc_FreeHead;
i < NumBlksPerChunk;
i++, pBlkHdr = pBlkHdr->bh_Next)
{
LONG Size;
#if DBG
pBlkHdr->bh_Signature = BH_SIGNATURE;
#endif
pBlkHdr->bh_Next = (i == (NumBlksPerChunk-1)) ?
NULL :
(PBLK_HDR)((PBYTE)pBlkHdr + BlkSize);
if (BlockId >= BLKID_NEED_NDIS_INT)
{
PCHAR pStartOfBuf;
// We need to initialize the Ndis stuff for these guys
pBufHdr = (PBUFFER_HDR)((PBYTE)pBlkHdr + sizeof(BLK_HDR));
pBufHdr->bh_NdisPkt = NULL;
if (BlockId == BLKID_SENDBUF)
{
Size = sizeof(BUFFER_HDR) + sizeof(BUFFER_DESC);
pStartOfBuf = (PCHAR)pBufHdr + Size;
}
else if ((BlockId == BLKID_MNP_SMSENDBUF) ||
(BlockId == BLKID_MNP_LGSENDBUF) )
{
// NOTE: the 1 byte of Buffer[1], combined with aligning
// effect screws up Size (it's 3 more than what we think!)
Size = sizeof(MNPSENDBUF);
pStartOfBuf = &(((PMNPSENDBUF)pBufHdr)->Buffer[0]);
}
else
{
Size = sizeof(BUFFER_HDR);
pStartOfBuf = (PCHAR)pBufHdr + Size;
}
// Make a NDIS buffer descriptor for this data
NdisAllocateBuffer(&ndisStatus,
&pBufHdr->bh_NdisBuffer,
AtalkNdisBufferPoolHandle,
pStartOfBuf,
(UINT)(BlkSize - sizeof(BLK_HDR) - Size));
if (ndisStatus != NDIS_STATUS_SUCCESS)
{
LOG_ERROR(EVENT_ATALK_NDISRESOURCES, ndisStatus, NULL, 0);
DBGPRINT(DBG_COMP_SYSTEM, DBG_LEVEL_ERR,
("NdisAllocateBuffer: Ndis Out-of-Resource condition hit\n"));
ASSERT(0);
break;
}
ATALK_DBG_INC_COUNT(AtalkDbgMdlsAlloced);
// More processing for send buffers
if ((BlockId == BLKID_SENDBUF) ||
(BlockId == BLKID_MNP_SMSENDBUF) ||
(BlockId == BLKID_MNP_LGSENDBUF))
{
PSENDBUF pSendBuf;
PBUFFER_DESC pBuffDesc;
PMNPSENDBUF pMnpSendBuf;
if (BlockId == BLKID_SENDBUF)
{
pSendBuf = (PSENDBUF)pBufHdr;
pBuffDesc = &pSendBuf->sb_BuffDesc;
pBuffDesc->bd_Length = MAX_SENDBUF_LEN;
pBuffDesc->bd_CharBuffer= pSendBuf->sb_Space;
}
else if (BlockId == BLKID_MNP_SMSENDBUF)
{
pMnpSendBuf = (PMNPSENDBUF)pBufHdr;
pMnpSendBuf->DataSize = 0;
pMnpSendBuf->FreeBuffer = &pMnpSendBuf->Buffer[0];
pBuffDesc = &pMnpSendBuf->sb_BuffDesc;
pBuffDesc->bd_Length = MNP_MINSEND_LEN;
pBuffDesc->bd_CharBuffer= &pMnpSendBuf->Buffer[0];
}
else // if (BlockId == BLKID_MNP_LGSENDBUF)
{
pMnpSendBuf = (PMNPSENDBUF)pBufHdr;
pMnpSendBuf->DataSize = 0;
pMnpSendBuf->FreeBuffer = &pMnpSendBuf->Buffer[0];
pBuffDesc = &pMnpSendBuf->sb_BuffDesc;
pBuffDesc->bd_Length = MNP_MAXSEND_LEN;
pBuffDesc->bd_CharBuffer= &pMnpSendBuf->Buffer[0];
}
#if DBG
pBuffDesc->bd_Signature = BD_SIGNATURE;
#endif
pBuffDesc->bd_Flags = BD_CHAR_BUFFER;
pBuffDesc->bd_Next = NULL;
pBuffDesc->bd_FreeBuffer= NULL;
}
}
}
if (i != NumBlksPerChunk)
{
// This has to be a failure from Ndis !!!
// Undo a bunch of stuff
ASSERT (BlockId >= BLKID_NEED_NDIS_INT);
DBGPRINT(DBG_COMP_SYSTEM, DBG_LEVEL_ERR,
("AtalkBPAllocBlock: Freeing new chunk (Ndis failure) Id %d\n",
BlockId));
pBlkHdr = pChunk->bc_FreeHead;
for (j = 0, pBlkHdr = pChunk->bc_FreeHead;
j < i; j++, pBlkHdr = pBlkHdr->bh_Next)
{
PBUFFER_HDR pBufHdr;
pBufHdr = (PBUFFER_HDR)((PBYTE)pBlkHdr + sizeof(BLK_HDR));
AtalkNdisFreeBuffer(pBufHdr->bh_NdisBuffer);
}
AtalkFreeMemory(pChunk);
pChunk = NULL;
}
else
{
// Successfully initialized the chunk, link it in
AtalkLinkDoubleAtHead(*ppChunkHead, pChunk, bc_Next, bc_Prev);
#if DBG
atalkNumChunksForId[BlockId] ++;
#endif
}
}
}
if (pChunk != NULL)
{
PBLK_CHUNK pTmp;
ASSERT(VALID_BC(pChunk));
ASSERT(pChunk->bc_BlkId == BlockId);
DBGPRINT(DBG_COMP_RESOURCES, DBG_LEVEL_INFO,
("AtalkBPAllocBlock: Allocating a block out of chunk %lx(%d,%d) for Id %d\n",
pChunk, pChunk->bc_NumFree, pChunk->bc_NumAlloc, BlockId));
pBlk = pChunk->bc_FreeHead;
ASSERT(VALID_BH(pBlk));
pChunk->bc_FreeHead = pBlk->bh_Next;
pBlk->bh_pChunk = pChunk;
pChunk->bc_Age = 0; // Reset age
pChunk->bc_NumFree --;
#if DBG
pChunk->bc_NumAlloc ++;
#endif
// If the block is now empty, unlink it from here and move it
// to the first empty slot. We know that all blocks 'earlier' than
// this are non-empty.
if ((pChunk->bc_NumFree == 0) &&
((pTmp = pChunk->bc_Next) != NULL) &&
(pTmp->bc_NumFree > 0))
{
ASSERT(pChunk->bc_NumAlloc == atalkNumBlks[BlockId]);
AtalkUnlinkDouble(pChunk, bc_Next, bc_Prev);
for (; pTmp != NULL; pTmp = pTmp->bc_Next)
{
ASSERT(VALID_BC(pTmp));
if (pTmp->bc_NumFree == 0)
{
ASSERT(pTmp->bc_NumAlloc == atalkNumBlks[BlockId]);
// Found a free one. Park it right here.
AtalkInsertDoubleBefore(pChunk, pTmp, bc_Next, bc_Prev);
break;
}
else if (pTmp->bc_Next == NULL) // We reached the end
{
AtalkLinkDoubleAtEnd(pChunk, pTmp, bc_Next, bc_Prev);
break;
}
}
}
}
if (pBlk != NULL)
{
//
// we allocate Ndis Packets for ARAP guys later, when we really need
//
if ((BlockId >= BLKID_NEED_NDIS_INT) &&
(BlockId != BLKID_MNP_SMSENDBUF) &&
(BlockId != BLKID_MNP_LGSENDBUF))
{
PBUFFER_HDR pBufHdr;
// We need to initialize the Ndis stuff for these guys
pBufHdr = (PBUFFER_HDR)((PBYTE)pBlk + sizeof(BLK_HDR));
pBufHdr->bh_NdisPkt = NULL;
// Allocate an NDIS packet descriptor from the global packet pool
NdisDprAllocatePacket(&ndisStatus,
&pBufHdr->bh_NdisPkt,
AtalkNdisPacketPoolHandle);
if (ndisStatus != NDIS_STATUS_SUCCESS)
{
LOG_ERROR(EVENT_ATALK_NDISRESOURCES, ndisStatus, NULL, 0);
DBGPRINT(DBG_COMP_SYSTEM, DBG_LEVEL_ERR,
("NdisDprAllocatePacket: Ndis Out-of-Resource condition hit\n"));
ASSERT(0);
RELEASE_SPIN_LOCK(pLock, OldIrql);
AtalkBPFreeBlock(pBufHdr);
return(NULL);
}
// Link the buffer descriptor into the packet descriptor
RtlZeroMemory(pBufHdr->bh_NdisPkt->ProtocolReserved, sizeof(PROTOCOL_RESD));
NdisChainBufferAtBack(pBufHdr->bh_NdisPkt,
pBufHdr->bh_NdisBuffer);
((PPROTOCOL_RESD)(pBufHdr->bh_NdisPkt->ProtocolReserved))->Receive.pr_BufHdr = pBufHdr;
}
++pBlk;
#if DBG
atalkBlksForId[BlockId] ++;
#endif
}
RELEASE_SPIN_LOCK(pLock, OldIrql);
#ifdef PROFILING
INTERLOCKED_INCREMENT_LONG(&AtalkStatistics.stat_BPAllocCount,
&AtalkStatsLock.SpinLock);
TimeE = KeQueryPerformanceCounter(NULL);
TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
INTERLOCKED_ADD_LARGE_INTGR(&AtalkStatistics.stat_BPAllocTime,
TimeD,
&AtalkStatsLock.SpinLock);
#endif
return pBlk;
}
/*** atalkBPFreeBlock
*
* Return a block to its owning chunk.
*/
VOID FASTCALL
AtalkBPFreeBlock(
IN PVOID pBlock
)
{
PBLK_CHUNK pChunk;
PBLK_HDR pBlkHdr;
BLKID BlockId;
KIRQL OldIrql;
ATALK_SPIN_LOCK * pLock;
#ifdef PROFILING
TIME TimeS, TimeE, TimeD;
TimeS = KeQueryPerformanceCounter(NULL);
#endif
pBlkHdr = (PBLK_HDR)((PCHAR)pBlock - sizeof(BLK_HDR));
ASSERT(VALID_BH(pBlkHdr));
pChunk = pBlkHdr->bh_pChunk;
ASSERT(VALID_BC(pChunk));
BlockId = pChunk->bc_BlkId;
pLock = &atalkBPLock[BlockId];
ACQUIRE_SPIN_LOCK(pLock, &OldIrql);
if (BlockId >= BLKID_NEED_NDIS_INT)
{
PBUFFER_HDR pBufHdr;
// We need to free the ndis packet here - if present
pBufHdr = (PBUFFER_HDR)pBlock;
if (pBufHdr->bh_NdisPkt != NULL)
{
NdisDprFreePacket(pBufHdr->bh_NdisPkt);
pBufHdr->bh_NdisPkt = NULL;
}
}
DBGPRINT(DBG_COMP_SYSTEM, DBG_LEVEL_INFO,
("AtalkBPFreeBlock: Returning Block %lx to chunk %lx for Id %d\n",
pBlkHdr, pChunk, BlockId));
ASSERT (pChunk->bc_NumFree < atalkNumBlks[BlockId]);
#if DBG
atalkBlksForId[BlockId] --;
pChunk->bc_NumAlloc --;
#endif
pChunk->bc_NumFree ++;
ASSERT((pChunk->bc_NumFree + pChunk->bc_NumAlloc) == atalkNumBlks[BlockId]);
pBlkHdr->bh_Next = pChunk->bc_FreeHead;
pChunk->bc_FreeHead = pBlkHdr;
// If this block's status is changing from a 'none available' to 'available'
// move him to the head of the list
if (pChunk->bc_NumFree == 1)
{
AtalkUnlinkDouble(pChunk, bc_Next, bc_Prev);
AtalkLinkDoubleAtHead(atalkBPHead[BlockId],
pChunk,
bc_Next,
bc_Prev);
}
#ifdef PROFILING
INTERLOCKED_INCREMENT_LONG(&AtalkStatistics.stat_BPFreeCount,
&AtalkStatsLock.SpinLock);
TimeE = KeQueryPerformanceCounter(NULL);
TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
INTERLOCKED_ADD_LARGE_INTGR_DPC(&AtalkStatistics.stat_BPFreeTime,
TimeD,
&AtalkStatsLock.SpinLock);
#endif
RELEASE_SPIN_LOCK(pLock, OldIrql);
}
/*** atalkBPAgePool
*
* Age out the block pool of unused blocks
*/
LOCAL LONG FASTCALL
atalkBPAgePool(
IN PTIMERLIST Context,
IN BOOLEAN TimerShuttingDown
)
{
PBLK_CHUNK pChunk;
LONG i, j, NumBlksPerChunk;
if (TimerShuttingDown)
{
return ATALK_TIMER_NO_REQUEUE;
}
for (i = 0; i < NUM_BLKIDS; i++)
{
NumBlksPerChunk = atalkNumBlks[i];
ACQUIRE_SPIN_LOCK_DPC(&atalkBPLock[i]);
for (pChunk = atalkBPHead[i];
pChunk != NULL; )
{
PBLK_CHUNK pFree;
ASSERT(VALID_BC(pChunk));
pFree = pChunk;
pChunk = pChunk->bc_Next;
// Since all blocks which are completely used up are at the tail end of
// the list, if we encounter one, we are done.
if (pFree->bc_NumFree == 0)
break;
if (pFree->bc_NumFree == NumBlksPerChunk)
{
DBGPRINT(DBG_COMP_SYSTEM, DBG_LEVEL_WARN,
("atalkBPAgePool: Aging Chunk %lx, Id %d NumFree %d\n",
pFree, pFree->bc_BlkId,pFree->bc_NumFree));
if (++(pFree->bc_Age) >= MAX_BLOCK_POOL_AGE)
{
DBGPRINT(DBG_COMP_SYSTEM, DBG_LEVEL_WARN,
("atalkBPAgePool: freeing Chunk %lx, Id %d\n",
pFree, pFree->bc_BlkId));
AtalkUnlinkDouble(pFree, bc_Next, bc_Prev);
#ifdef PROFILING
INTERLOCKED_INCREMENT_LONG( &AtalkStatistics.stat_NumBPAge,
&AtalkStatsLock.SpinLock);
#endif
if (pFree->bc_BlkId >= BLKID_NEED_NDIS_INT)
{
PBLK_HDR pBlkHdr;
// We need to free Ndis stuff for these guys
pBlkHdr = pFree->bc_FreeHead;
for (j = 0, pBlkHdr = pFree->bc_FreeHead;
j < NumBlksPerChunk;
j++, pBlkHdr = pBlkHdr->bh_Next)
{
PBUFFER_HDR pBufHdr;
pBufHdr = (PBUFFER_HDR)((PBYTE)pBlkHdr + sizeof(BLK_HDR));
ASSERT(pBufHdr->bh_NdisPkt == NULL);
AtalkNdisFreeBuffer(pBufHdr->bh_NdisBuffer);
}
}
AtalkFreeMemory(pFree);
#if DBG
atalkNumChunksForId[i] --;
#endif
}
}
}
RELEASE_SPIN_LOCK_DPC(&atalkBPLock[i]);
}
return ATALK_TIMER_REQUEUE;
}
#ifdef TRACK_MEMORY_USAGE
#define MAX_PTR_COUNT 4*1024
#define MAX_MEM_USERS 512
LOCAL ATALK_SPIN_LOCK atalkMemTrackLock = {0};
LOCAL struct
{
PVOID mem_Ptr;
ULONG mem_FileLine;
} atalkMemPtrs[MAX_PTR_COUNT] = {0};
LOCAL struct
{
ULONG mem_FL;
ULONG mem_Count;
} atalkMemUsage[MAX_MEM_USERS] = {0};
BOOLEAN NeverBeenFull=TRUE;
VOID
AtalkTrackMemoryUsage(
IN PVOID pMem,
IN ULONG Size,
IN BOOLEAN Alloc,
IN ULONG FileLine
)
/*++
Routine Description:
Keep track of memory usage by storing and clearing away pointers as and
when they are allocated or freed. This helps in keeping track of memory
leaks.
Arguments:
Return Value:
--*/
{
static int i = 0;
int j, k;
KIRQL OldIrql;
ACQUIRE_SPIN_LOCK(&atalkMemTrackLock, &OldIrql);
if (Alloc)
{
for (j = 0; j < MAX_PTR_COUNT; i++, j++)
{
i = i & (MAX_PTR_COUNT-1);
if (atalkMemPtrs[i].mem_Ptr == NULL)
{
atalkMemPtrs[i].mem_Ptr = pMem;
atalkMemPtrs[i++].mem_FileLine = FileLine;
break;
}
}
for (k = 0; k < MAX_MEM_USERS; k++)
{
if (atalkMemUsage[k].mem_FL == FileLine)
{
atalkMemUsage[k].mem_Count += Size;
break;
}
}
if (k == MAX_MEM_USERS)
{
for (k = 0; k < MAX_MEM_USERS; k++)
{
if (atalkMemUsage[k].mem_FL == 0)
{
atalkMemUsage[k].mem_FL = FileLine;
atalkMemUsage[k].mem_Count = Size;
break;
}
}
if (k == MAX_MEM_USERS)
{
DBGPRINT(DBG_COMP_RESOURCES, DBG_LEVEL_ERR,
("AtalkTrackMemoryUsage: Out of space on atalkMemUsage !!!\n"));
}
}
}
else
{
for (j = 0, k = i; j < MAX_PTR_COUNT; j++, k--)
{
k = k & (MAX_PTR_COUNT-1);
if (atalkMemPtrs[k].mem_Ptr == pMem)
{
atalkMemPtrs[k].mem_Ptr = 0;
atalkMemPtrs[k].mem_FileLine = 0;
break;
}
}
for (k = 0; k < MAX_MEM_USERS; k++)
{
if (atalkMemUsage[k].mem_FL == FileLine)
{
if (atalkMemUsage[k].mem_Count >= Size)
{
atalkMemUsage[k].mem_Count -= Size;
}
break;
}
}
}
RELEASE_SPIN_LOCK(&atalkMemTrackLock, OldIrql);
if (j == MAX_PTR_COUNT)
{
if (NeverBeenFull)
{
NeverBeenFull = FALSE;
DBGPRINT(DBG_COMP_RESOURCES, DBG_LEVEL_ERR,
("AtalkTrackMemoryUsage: %s\n", Alloc ? "Table Full" : "Can't find"));
}
}
}
#endif // TRACK_MEMORY_USAGE
#ifdef TRACK_BUFFDESC_USAGE
#define MAX_BD_COUNT 1024*2
LOCAL ATALK_SPIN_LOCK atalkBdTrackLock = {0};
LOCAL struct
{
PVOID bdesc_Ptr;
ULONG bdesc_FileLine;
} atalkBuffDescPtrs[MAX_BD_COUNT] = {0};
VOID
AtalkTrackBuffDescUsage(
IN PVOID pBuffDesc,
IN BOOLEAN Alloc,
IN ULONG FileLine
)
/*++
Routine Description:
Keep track of buffer-desc usage by storing and clearing away pointers as and
when they are allocated or freed. This helps in keeping track of memory
leaks.
Arguments:
Return Value:
--*/
{
static int i = 0;
int j, k;
KIRQL OldIrql;
ACQUIRE_SPIN_LOCK(&atalkBdTrackLock, &OldIrql);
if (Alloc)
{
for (j = 0; j < MAX_BD_COUNT; i++, j++)
{
i = i & (MAX_BD_COUNT-1);
if (atalkBuffDescPtrs[i].bdesc_Ptr == NULL)
{
atalkBuffDescPtrs[i].bdesc_Ptr = pBuffDesc;
atalkBuffDescPtrs[i++].bdesc_FileLine = FileLine;
break;
}
}
}
else
{
for (j = 0, k = i; j < MAX_BD_COUNT; j++, k--)
{
k = k & (MAX_BD_COUNT-1);
if (atalkBuffDescPtrs[k].bdesc_Ptr == pBuffDesc)
{
atalkBuffDescPtrs[k].bdesc_Ptr = 0;
atalkBuffDescPtrs[k].bdesc_FileLine = 0;
break;
}
}
}
RELEASE_SPIN_LOCK(&atalkBdTrackLock, OldIrql);
if (j == MAX_BD_COUNT)
{
DBGPRINT(DBG_COMP_RESOURCES, DBG_LEVEL_INFO,
("AtalkTrackBuffDescUsage: %s\n", Alloc ? "Table Full" : "Can't find"));
ASSERT(0);
}
}
#endif // TRACK_BUFFDESC_USAGE