You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1534 lines
34 KiB
1534 lines
34 KiB
/*
|
|
|
|
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 ([email protected])
|
|
Jameel Hyder ([email protected])
|
|
|
|
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
|
|
|