/*++ 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; } }