windows-nt/Source/XPSP1/NT/net/rras/ip/ipinip/ppool.c
2020-09-26 16:20:57 +08:00

600 lines
13 KiB
C

/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
ipinip\ppool.h
Abstract:
Code for managing NDIS_PACKET pools. This is merely a reformatted version
of SteveC's l2tp\ppool.c
Revision History:
--*/
#define __FILE_SIG__ PPOOL_SIG
#include "inc.h"
//-----------------------------------------------------------------------------
// Local prototypes (alphabetically)
//-----------------------------------------------------------------------------
PPACKET_HEAD
AddPacketBlockToPool(
IN PPACKET_POOL pPool
);
VOID
FreeUnusedPacketPoolBlocks(
IN PPACKET_POOL pPool
);
//-----------------------------------------------------------------------------
// Interface routines
//-----------------------------------------------------------------------------
VOID
InitPacketPool(
OUT PPACKET_POOL pPool,
IN ULONG ulProtocolReservedLength,
IN ULONG ulMaxPackets,
IN ULONG ulPacketsPerBlock,
IN ULONG ulFreesPerCollection,
IN ULONG ulTag
)
/*++
Routine Description
Initialize caller's packet pool control block 'pPool'
Locks
Caller's 'pPool' packet must be protected from multiple access during
this call.
Arguments
ulProtocolReservedLength size in bytes of the 'ProtocolReserved'
array of each individual packet.
ulMaxPackets maximum number of packets allowed in the
entire pool, or 0 for unlimited.
ulPacketsPerBlock number of packets to include in each block
of packets.
ulFreesPerCollection number of FreePacketToPool calls until the
next garbage collect scan, or 0 for default.
ulTag pool tag to use when allocating blocks
Return Value
None
--*/
{
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;
}
InitializeListHead(&(pPool->leBlockHead));
InitializeListHead(&(pPool->leFreePktHead));
RtInitializeSpinLock(&(pPool->rlLock));
}
BOOLEAN
FreePacketPool(
IN PPACKET_POOL pPool
)
/*++
Routine Description
Free up all resources allocated in packet pool 'pPool'. This is the
inverse of InitPacketPool.
Locks
Arguments
Return Value
TRUE if successful
FALSE if any of the pool could not be freed due to outstanding packets
--*/
{
BOOLEAN fSuccess;
KIRQL irql;
RtAcquireSpinLock(&(pPool->rlLock),
&irql);
FreeUnusedPacketPoolBlocks(pPool);
fSuccess = (pPool->ulCurPackets is 0);
RtReleaseSpinLock(&(pPool->rlLock),
irql);
return fSuccess;
}
PNDIS_PACKET
GetPacketFromPool(
IN PPACKET_POOL pPool,
OUT PACKET_HEAD **ppHead
)
/*++
Routine Description
Returns the address of the NDIS_PACKET descriptor allocated from the
pool. The pool is expanded, if necessary, but caller should
still check for NULL return since the pool may have been at maximum
size.
Locks
Arguments
pPool Pool to get packet from
ppHead Pointer 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.
Return Value
Pointer to NDIS_PACKET or NULL
--*/
{
PLIST_ENTRY pleNode;
PPACKET_HEAD pHead;
PNDIS_PACKET pPacket;
KIRQL irql;
RtAcquireSpinLock(&(pPool->rlLock),
&irql);
if(IsListEmpty(&pPool->leFreePktHead))
{
pleNode = NULL;
}
else
{
pleNode = RemoveHeadList(&(pPool->leFreePktHead));
pHead = CONTAINING_RECORD(pleNode, PACKET_HEAD, leFreePktLink);
pHead->pBlock->ulFreePackets--;
}
RtReleaseSpinLock(&(pPool->rlLock),
irql);
if(!pleNode)
{
//
// The free list was empty. Try to expand the pool.
//
pHead = AddPacketBlockToPool(pPool);
if(!pHead)
{
return NULL;
}
}
*ppHead = pHead;
return pHead->pNdisPacket;
}
VOID
FreePacketToPool(
IN PPACKET_POOL pPool,
IN PPACKET_HEAD pHead,
IN BOOLEAN fGarbageCollection
)
/*++
Routine Description
Returns a packet to the pool of unused packet. The packet must have
been previously allocate with GetaPacketFromPool.
Locks
Arguments
pPool Pool to which the packet is to be returned
pHead The "cookie" that was given when the packet was allocated
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.
Return Value
NO_ERROR
--*/
{
KIRQL irql;
RtAcquireSpinLock(&(pPool->rlLock),
&irql);
InsertHeadList(&(pPool->leFreePktHead),
&(pHead->leFreePktLink));
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;
}
}
RtReleaseSpinLock(&(pPool->rlLock),
irql);
}
//-----------------------------------------------------------------------------
// Utility routines (alphabetically)
//-----------------------------------------------------------------------------
PPACKET_HEAD
AddPacketBlockToPool(
IN PPACKET_POOL pPool
)
/*++
Routine Description
Allocate a new packet block and add it to the packet pool
Locks
Arguments
Return Value
NO_ERROR
--*/
{
NDIS_STATUS status;
PPACKET_BLOCK pNew;
ULONG ulSize;
ULONG ulCount;
BOOLEAN fOk;
PPACKET_HEAD pReturn;
KIRQL irql;
fOk = FALSE;
pNew = NULL;
RtAcquireSpinLock(&(pPool->rlLock),
&irql);
do
{
if((pPool->ulMaxPackets) and
(pPool->ulCurPackets >= pPool->ulMaxPackets))
{
//
// No can do. The pool was initialized with a max size and that
// has been reached.
//
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))
{
//
// If a max was specified, respect that
//
ulCount = pPool->ulMaxPackets - pPool->ulCurPackets;
}
}
//
// We allocate a PACKET_BLOCK to account for this block of packets
// and one PACKET_HEAD per packet
//
ulSize = sizeof(PACKET_BLOCK) + (ulCount * sizeof(PACKET_HEAD));
//
// Allocate the contiguous memory block for the PACKETBLOCK header
// and the individual PACKET_HEADs.
//
pNew = RtAllocate(NonPagedPool,
ulSize,
pPool->ulTag);
if(!pNew)
{
Trace(UTIL, ERROR,
("AddPacketBlockToPool: Unable to allocate %d bytes\n",
ulSize));
break;
}
//
// Zero only the block header portion.
//
NdisZeroMemory(pNew, sizeof(PACKET_BLOCK));
//
// Allocate a pool of NDIS_PACKET descriptors.
//
NdisAllocatePacketPool(&status,
&pNew->nhNdisPool,
ulCount,
pPool->ulProtocolReservedLength);
if(status isnot NDIS_STATUS_SUCCESS)
{
Trace(UTIL, ERROR,
("AddPacketBlockToPool: Unable to allocate packet pool for %d packets\n",
ulCount));
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->leBlockHead),
&(pNew->leBlockLink));
fOk = TRUE;
}while(FALSE);
RtReleaseSpinLock(&pPool->rlLock,
irql);
if(!fOk)
{
//
// Bailing, undo whatever succeeded.
//
if(pNew)
{
RtFree(pNew);
if(pNew->nhNdisPool)
{
NdisFreePacketPool(pNew->nhNdisPool);
}
}
return NULL;
}
//
// Initialize each individual packet header and add it to the list of free
// packets.
//
{
ULONG i;
PPACKET_HEAD pHead;
pReturn = NULL;
//
// For each PACKET_HEAD of the block...
//
for(i = 0, pHead = (PPACKET_HEAD)(pNew + 1);
i < ulCount;
i++, pHead++)
{
InitializeListHead(&pHead->leFreePktLink);
pHead->pBlock = pNew;
pHead->pNdisPacket = NULL;
//
// Associate an NDIS_PACKET descriptor from the pool we
// allocated above.
//
NdisAllocatePacket(&status,
&pHead->pNdisPacket,
pNew->nhNdisPool);
if(status isnot NDIS_STATUS_SUCCESS)
{
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 PPACKET_POOL pPool
)
/*++
Routine Description
Check if any of the blocks in pool are not in use, and if so, free them.
Locks
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.
Arguments
pPool Pointer to pool
Return Value
None
--*/
{
PLIST_ENTRY pleNode;
//
// For each block in the pool...
//
pleNode = pPool->leBlockHead.Flink;
while(pleNode isnot &(pPool->leBlockHead))
{
PLIST_ENTRY pleNextNode;
PPACKET_BLOCK pBlock;
pleNextNode = pleNode->Flink;
pBlock = CONTAINING_RECORD(pleNode, PACKET_BLOCK, leBlockLink);
if(pBlock->ulFreePackets >= pBlock->ulPackets)
{
ULONG i;
PPACKET_HEAD pHead;
//
// 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 = (PPACKET_HEAD)(pBlock + 1);
i < pBlock->ulPackets;
i++, pHead++)
{
RemoveEntryList(&(pHead->leFreePktLink));
if(pHead->pNdisPacket)
{
NdisFreePacket(pHead->pNdisPacket);
}
}
//
// Remove and release the unused block.
//
RemoveEntryList(pleNode);
pPool->ulCurPackets -= pBlock->ulPackets;
if(pBlock->nhNdisPool)
{
NdisFreePacketPool(pBlock->nhNdisPool);
}
RtFree(pBlock);
}
pleNode = pleNextNode;
}
}