478 lines
14 KiB
C
478 lines
14 KiB
C
|
// Copyright (c) 1997, Microsoft Corporation, all rights reserved
|
||
|
//
|
||
|
// ppool.c
|
||
|
// RAS L2TP WAN mini-port/call-manager driver
|
||
|
// Packet pool management routines
|
||
|
//
|
||
|
// 01/07/97 Steve Cobb, adapted from Gurdeep's WANARP code.
|
||
|
|
||
|
|
||
|
#include "ptiwan.h"
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Local prototypes (alphabetically)
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
PACKETHEAD*
|
||
|
AddPacketBlockToPool(
|
||
|
IN PACKETPOOL* pPool );
|
||
|
|
||
|
VOID
|
||
|
FreeUnusedPacketPoolBlocks(
|
||
|
IN PACKETPOOL* pPool );
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Interface routines
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
VOID
|
||
|
InitPacketPool(
|
||
|
OUT PACKETPOOL* pPool,
|
||
|
IN ULONG ulProtocolReservedLength,
|
||
|
IN ULONG ulMaxPackets,
|
||
|
IN ULONG ulPacketsPerBlock,
|
||
|
IN ULONG ulFreesPerCollection,
|
||
|
IN ULONG ulTag )
|
||
|
|
||
|
// Initialize caller's packet pool control block 'pPool'.
|
||
|
// 'UlProtocolReservedLength' is the size in bytes of the
|
||
|
// 'ProtocolReserved' array of each individual packet. 'UlMaxPackets' is
|
||
|
// the maximum number of packets allowed in the entire pool, or 0 for
|
||
|
// unlimited. 'UlPacketsPerBlock' is the number of packets to include in
|
||
|
// each block of packets. 'UlFreesPerCollection' is the number of
|
||
|
// FreePacketToPool calls until the next garbage collect scan, or 0 for
|
||
|
// default. 'UlTag' is the memory identification tag to use when
|
||
|
// allocating blocks.
|
||
|
//
|
||
|
// IMPORTANT: Caller's 'pPool' packet must be protected from multiple
|
||
|
// access during this call.
|
||
|
//
|
||
|
{
|
||
|
pPool->ulProtocolReservedLength = ulProtocolReservedLength;
|
||
|
pPool->ulPacketsPerBlock = ulPacketsPerBlock;
|
||
|
pPool->ulMaxPackets = ulMaxPackets;
|
||
|
pPool->ulFreesSinceCollection = 0;
|
||
|
pPool->ulTag = ulTag;
|
||
|
|
||
|
if (ulFreesPerCollection)
|
||
|
{
|
||
|
pPool->ulFreesPerCollection = ulFreesPerCollection;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Calculate default garbage collection trigger. Don't want to be too
|
||
|
// aggressive here.
|
||
|
//
|
||
|
pPool->ulFreesPerCollection = 50 * pPool->ulPacketsPerBlock;
|
||
|
}
|
||
|
|
||
|
TRACE( TL_N, TM_Pool, ( "InitPp tag=$%08x pr=%d cnt=%d",
|
||
|
pPool->ulTag,
|
||
|
pPool->ulProtocolReservedLength,
|
||
|
pPool->ulPacketsPerBlock ) );
|
||
|
|
||
|
InitializeListHead( &pPool->listBlocks );
|
||
|
InitializeListHead( &pPool->listFreePackets );
|
||
|
NdisAllocateSpinLock( &pPool->lock );
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOLEAN
|
||
|
FreePacketPool(
|
||
|
IN PACKETPOOL* pPool )
|
||
|
|
||
|
// Free up all resources allocated in packet pool 'pPool'. This is the
|
||
|
// inverse of InitPacketPool.
|
||
|
//
|
||
|
// Returns true if successful, false if any of the pool could not be freed
|
||
|
// due to outstanding packets.
|
||
|
//
|
||
|
{
|
||
|
BOOLEAN fSuccess;
|
||
|
|
||
|
TRACE( TL_N, TM_Pool, ( "FreePp" ) );
|
||
|
|
||
|
NdisAcquireSpinLock( &pPool->lock );
|
||
|
{
|
||
|
FreeUnusedPacketPoolBlocks( pPool );
|
||
|
fSuccess = (pPool->ulCurPackets == 0);
|
||
|
}
|
||
|
NdisReleaseSpinLock( &pPool->lock );
|
||
|
|
||
|
return fSuccess;
|
||
|
}
|
||
|
|
||
|
|
||
|
NDIS_PACKET*
|
||
|
GetPacketFromPool(
|
||
|
IN PACKETPOOL* pPool,
|
||
|
OUT PACKETHEAD** ppHead )
|
||
|
|
||
|
// Returns the address of the NDIS_PACKET descriptor allocated from the
|
||
|
// pool 'pPool'. The pool is expanded, if necessary, but caller should
|
||
|
// still check for NULL return since the pool may have been at maximum
|
||
|
// size. 'PpHead' is the "cookie" that is used to return the packet to
|
||
|
// the pool (see FreePacketToPool). Caller would normally stash this
|
||
|
// value in the appropriate 'reserved' areas of the packet for retrieval
|
||
|
// later.
|
||
|
//
|
||
|
{
|
||
|
LIST_ENTRY* pLink;
|
||
|
PACKETHEAD* pHead;
|
||
|
NDIS_PACKET* pPacket;
|
||
|
|
||
|
NdisAcquireSpinLock( &pPool->lock );
|
||
|
{
|
||
|
if (IsListEmpty( &pPool->listFreePackets ))
|
||
|
{
|
||
|
pLink = NULL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pLink = RemoveHeadList( &pPool->listFreePackets );
|
||
|
pHead = CONTAINING_RECORD( pLink, PACKETHEAD, linkFreePackets );
|
||
|
--pHead->pBlock->ulFreePackets;
|
||
|
}
|
||
|
}
|
||
|
NdisReleaseSpinLock( &pPool->lock );
|
||
|
|
||
|
if (!pLink)
|
||
|
{
|
||
|
// The free list was empty. Try to expand the pool.
|
||
|
//
|
||
|
pHead = AddPacketBlockToPool( pPool );
|
||
|
if (!pHead)
|
||
|
{
|
||
|
TRACE( TL_A, TM_Pool, ( "GetPfP failed?" ) );
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TRACE( TL_N, TM_Pool,
|
||
|
( "GetPfP=$%p/h=$%p, %d free",
|
||
|
pHead->pNdisPacket, pHead, pHead->pBlock->ulFreePackets ) );
|
||
|
|
||
|
*ppHead = pHead;
|
||
|
return pHead->pNdisPacket;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
FreePacketToPool(
|
||
|
IN PACKETPOOL* pPool,
|
||
|
IN PACKETHEAD* pHead,
|
||
|
IN BOOLEAN fGarbageCollection )
|
||
|
|
||
|
// Returns 'pPacket' to the pool of unused packets 'pPool'. 'PPacket'
|
||
|
// must have been previously allocated with GetPacketFromPool.
|
||
|
// 'FGarbageCollection' is set when the free should be considered for
|
||
|
// purposes of garbage collection. This is used by the AddPacketToPool
|
||
|
// routine to avoid counting the initial "add" frees. Normal callers
|
||
|
// should set this flag.
|
||
|
//
|
||
|
{
|
||
|
DBG_if (fGarbageCollection)
|
||
|
{
|
||
|
TRACE( TL_N, TM_Pool,
|
||
|
( "FreePtoP($%p,h=$%p) %d free",
|
||
|
pHead->pNdisPacket, pHead, pHead->pBlock->ulFreePackets ) );
|
||
|
}
|
||
|
|
||
|
NdisAcquireSpinLock( &pPool->lock );
|
||
|
{
|
||
|
InsertHeadList( &pPool->listFreePackets, &pHead->linkFreePackets );
|
||
|
++pHead->pBlock->ulFreePackets;
|
||
|
|
||
|
if (fGarbageCollection)
|
||
|
{
|
||
|
++pPool->ulFreesSinceCollection;
|
||
|
|
||
|
if (pPool->ulFreesSinceCollection >= pPool->ulFreesPerCollection)
|
||
|
{
|
||
|
// Time to collect garbage, i.e. free any blocks in the pool
|
||
|
// not in use.
|
||
|
//
|
||
|
FreeUnusedPacketPoolBlocks( pPool );
|
||
|
pPool->ulFreesSinceCollection = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
NdisReleaseSpinLock( &pPool->lock );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Utility routines (alphabetically)
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
PACKETHEAD*
|
||
|
AddPacketBlockToPool(
|
||
|
IN PACKETPOOL* pPool )
|
||
|
|
||
|
// Allocate a new packet block and add it to the packet pool 'pPool'.
|
||
|
//
|
||
|
// Returns the PACKETHEAD allocated from the pool or NULL if none.
|
||
|
//
|
||
|
{
|
||
|
NDIS_STATUS status;
|
||
|
PACKETBLOCKHEAD* pNew;
|
||
|
ULONG ulSize;
|
||
|
ULONG ulCount;
|
||
|
BOOLEAN fOk;
|
||
|
PACKETHEAD* pReturn;
|
||
|
|
||
|
TRACE( TL_A, TM_Pool, ( "AddPpBlock(%d+%d)",
|
||
|
pPool->ulCurPackets, pPool->ulPacketsPerBlock ) );
|
||
|
|
||
|
fOk = FALSE;
|
||
|
pNew = NULL;
|
||
|
|
||
|
NdisAcquireSpinLock( &pPool->lock );
|
||
|
{
|
||
|
do
|
||
|
{
|
||
|
if (pPool->ulMaxPackets
|
||
|
&& pPool->ulCurPackets >= pPool->ulMaxPackets)
|
||
|
{
|
||
|
// No can do. The pool's already at maximum size.
|
||
|
//
|
||
|
TRACE( TL_A, TM_Pool, ( "Pp maxed?" ) );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Calculate the contiguous block's size and the number of packets
|
||
|
// it will hold.
|
||
|
//
|
||
|
ulCount = pPool->ulPacketsPerBlock;
|
||
|
if (pPool->ulMaxPackets)
|
||
|
{
|
||
|
if (ulCount > pPool->ulMaxPackets - pPool->ulCurPackets)
|
||
|
ulCount = pPool->ulMaxPackets - pPool->ulCurPackets;
|
||
|
}
|
||
|
ulSize = sizeof(PACKETBLOCKHEAD) + (ulCount * sizeof(PACKETHEAD));
|
||
|
|
||
|
// Allocate the contiguous memory block for the PACKETBLOCK header
|
||
|
// and the individual PACKETHEADs.
|
||
|
//
|
||
|
pNew = ALLOC_NONPAGED( ulSize, pPool->ulTag );
|
||
|
if (!pNew)
|
||
|
{
|
||
|
TRACE( TL_A, TM_Pool, ( "Alloc PB?") );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Zero only the block header portion.
|
||
|
*/
|
||
|
NdisZeroMemory( pNew, sizeof(PACKETBLOCKHEAD) );
|
||
|
|
||
|
// Allocate a pool of NDIS_PACKET descriptors.
|
||
|
//
|
||
|
NdisAllocatePacketPool(
|
||
|
&status,
|
||
|
&pNew->hNdisPool,
|
||
|
ulCount,
|
||
|
pPool->ulProtocolReservedLength );
|
||
|
|
||
|
if (status != NDIS_STATUS_SUCCESS)
|
||
|
{
|
||
|
TRACE( TL_A, TM_Pool, ( "AllocPp=$%p?", status ) );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Fill in the back pointer to the pool.
|
||
|
//
|
||
|
pNew->pPool = pPool;
|
||
|
|
||
|
// Link the new block. At this point, all the packets are
|
||
|
// effectively "in use". They are made available in the loop
|
||
|
// below.
|
||
|
//
|
||
|
pNew->ulPackets = ulCount;
|
||
|
pPool->ulCurPackets += ulCount;
|
||
|
InsertHeadList( &pPool->listBlocks, &pNew->linkBlocks );
|
||
|
|
||
|
fOk = TRUE;
|
||
|
}
|
||
|
while (FALSE);
|
||
|
}
|
||
|
NdisReleaseSpinLock( &pPool->lock );
|
||
|
|
||
|
if (!fOk)
|
||
|
{
|
||
|
// Bailing, undo whatever succeeded.
|
||
|
//
|
||
|
if (pNew)
|
||
|
{
|
||
|
FREE_NONPAGED( pNew );
|
||
|
if (pNew->hNdisPool)
|
||
|
{
|
||
|
NdisFreePacketPool( pNew->hNdisPool );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
// Initialize each individual packet header and add it to the list of free
|
||
|
// packets.
|
||
|
//
|
||
|
{
|
||
|
ULONG i;
|
||
|
PACKETHEAD* pHead;
|
||
|
|
||
|
pReturn = NULL;
|
||
|
|
||
|
// For each PACKETHEAD of the block...
|
||
|
//
|
||
|
for (i = 0, pHead = (PACKETHEAD* )(pNew + 1);
|
||
|
i < ulCount;
|
||
|
++i, ++pHead)
|
||
|
{
|
||
|
InitializeListHead( &pHead->linkFreePackets );
|
||
|
pHead->pBlock = pNew;
|
||
|
pHead->pNdisPacket = NULL;
|
||
|
|
||
|
// Associate an NDIS_PACKET descriptor from the pool we
|
||
|
// allocated above.
|
||
|
//
|
||
|
NdisAllocatePacket( &status, &pHead->pNdisPacket, pNew->hNdisPool );
|
||
|
|
||
|
if (status != NDIS_STATUS_SUCCESS)
|
||
|
{
|
||
|
TRACE( TL_A, TM_Pool, ( "AllocP=$%p?", status ) );
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (pReturn)
|
||
|
{
|
||
|
// Add the constructed packet to the list of free packets.
|
||
|
// The 'FALSE' tells the garbage collection algorithm the
|
||
|
// operation is an "add" rather than a "release" and should be
|
||
|
// ignored.
|
||
|
//
|
||
|
FreePacketToPool( pPool, pHead, FALSE );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// The first successfully constructed packet is returned by
|
||
|
// this routine.
|
||
|
//
|
||
|
pReturn = pHead;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return pReturn;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
FreeUnusedPacketPoolBlocks(
|
||
|
IN PACKETPOOL* pPool )
|
||
|
|
||
|
// Check if any of the blocks in pool 'pPool' are not in use, and if so,
|
||
|
// free them.
|
||
|
//
|
||
|
// IMPORTANT: Caller must hold the pool lock.
|
||
|
//
|
||
|
// NOTE: The MSDN doc says that no locks may be held while calling
|
||
|
// NdisFreePacketXxx, but according to JameelH that is incorrect.
|
||
|
//
|
||
|
{
|
||
|
LIST_ENTRY* pLink;
|
||
|
|
||
|
TRACE( TL_A, TM_Pool, ( "FreeUnusedPpBlocks" ) );
|
||
|
|
||
|
// For each block in the pool...
|
||
|
//
|
||
|
pLink = pPool->listBlocks.Flink;
|
||
|
while (pLink != &pPool->listBlocks)
|
||
|
{
|
||
|
LIST_ENTRY* pLinkNext;
|
||
|
PACKETBLOCKHEAD* pBlock;
|
||
|
|
||
|
pLinkNext = pLink->Flink;
|
||
|
|
||
|
pBlock = CONTAINING_RECORD( pLink, PACKETBLOCKHEAD, linkBlocks );
|
||
|
if (pBlock->ulFreePackets >= pBlock->ulPackets)
|
||
|
{
|
||
|
|
||
|
#if 1 // Assume all buffers are free at time of call.
|
||
|
|
||
|
ULONG i;
|
||
|
PACKETHEAD* pHead;
|
||
|
|
||
|
TRACE( TL_A, TM_Pool, ( "FreePpBlock(%d-%d)",
|
||
|
pPool->ulCurPackets, pPool->ulPacketsPerBlock ) );
|
||
|
|
||
|
// Found a block with no packets in use. Walk the packet block
|
||
|
// removing each packet from the pool's free list and freeing any
|
||
|
// associated NDIS_PACKET descriptor.
|
||
|
//
|
||
|
for (i = 0, pHead = (PACKETHEAD* )(pBlock + 1);
|
||
|
i < pBlock->ulPackets;
|
||
|
++i, ++pHead)
|
||
|
{
|
||
|
RemoveEntryList( &pHead->linkFreePackets );
|
||
|
|
||
|
if (pHead->pNdisPacket)
|
||
|
{
|
||
|
NdisFreePacket( pHead->pNdisPacket );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#else // Assume some buffers may not be free at time of call.
|
||
|
|
||
|
LIST_ENTRY* pLink2;
|
||
|
|
||
|
// Found a block with no packets in use. Walk the pool's free
|
||
|
// list looking for buffers from this block.
|
||
|
//
|
||
|
TRACE( TL_A, TM_Pool, ( "FreePpBlock(%d-%d)",
|
||
|
pPool->ulCurPackets, pPool->ulPacketsPerBlock ) );
|
||
|
|
||
|
pLink2 = pPool->listFreePackets.Flink;
|
||
|
while (pLink2 != &pPool->listFreePackets)
|
||
|
{
|
||
|
LIST_ENTRY* pLink2Next;
|
||
|
PACKETHEAD* pHead;
|
||
|
|
||
|
pLink2Next = pLink2->Flink;
|
||
|
|
||
|
pHead = CONTAINING_RECORD( pLink2, PACKETHEAD, linkFreePackets );
|
||
|
if (pHead->pBlock == pBlock)
|
||
|
{
|
||
|
// Found a packet from the unused block. Remove it.
|
||
|
//
|
||
|
RemoveEntryList( pLink2 );
|
||
|
--pBlock->ulFreePackets;
|
||
|
|
||
|
if (pHead->pNdisPacket)
|
||
|
{
|
||
|
NdisFreePacket( pHead->pNdisPacket );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ASSERT( pBlock->ulFreePackets == 0 );
|
||
|
#endif
|
||
|
|
||
|
// Remove and release the unused block.
|
||
|
//
|
||
|
RemoveEntryList( pLink );
|
||
|
pPool->ulCurPackets -= pBlock->ulPackets;
|
||
|
|
||
|
if (pBlock->hNdisPool)
|
||
|
{
|
||
|
NdisFreePacketPool( pBlock->hNdisPool );
|
||
|
}
|
||
|
|
||
|
FREE_NONPAGED( pBlock );
|
||
|
}
|
||
|
|
||
|
pLink = pLinkNext;
|
||
|
}
|
||
|
}
|