Source code of Windows XP (NT5)
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.

477 lines
14 KiB

  1. // Copyright (c) 1997, Microsoft Corporation, all rights reserved
  2. //
  3. // ppool.c
  4. // RAS L2TP WAN mini-port/call-manager driver
  5. // Packet pool management routines
  6. //
  7. // 01/07/97 Steve Cobb, adapted from Gurdeep's WANARP code.
  8. #include "ptiwan.h"
  9. //-----------------------------------------------------------------------------
  10. // Local prototypes (alphabetically)
  11. //-----------------------------------------------------------------------------
  12. PACKETHEAD*
  13. AddPacketBlockToPool(
  14. IN PACKETPOOL* pPool );
  15. VOID
  16. FreeUnusedPacketPoolBlocks(
  17. IN PACKETPOOL* pPool );
  18. //-----------------------------------------------------------------------------
  19. // Interface routines
  20. //-----------------------------------------------------------------------------
  21. VOID
  22. InitPacketPool(
  23. OUT PACKETPOOL* pPool,
  24. IN ULONG ulProtocolReservedLength,
  25. IN ULONG ulMaxPackets,
  26. IN ULONG ulPacketsPerBlock,
  27. IN ULONG ulFreesPerCollection,
  28. IN ULONG ulTag )
  29. // Initialize caller's packet pool control block 'pPool'.
  30. // 'UlProtocolReservedLength' is the size in bytes of the
  31. // 'ProtocolReserved' array of each individual packet. 'UlMaxPackets' is
  32. // the maximum number of packets allowed in the entire pool, or 0 for
  33. // unlimited. 'UlPacketsPerBlock' is the number of packets to include in
  34. // each block of packets. 'UlFreesPerCollection' is the number of
  35. // FreePacketToPool calls until the next garbage collect scan, or 0 for
  36. // default. 'UlTag' is the memory identification tag to use when
  37. // allocating blocks.
  38. //
  39. // IMPORTANT: Caller's 'pPool' packet must be protected from multiple
  40. // access during this call.
  41. //
  42. {
  43. pPool->ulProtocolReservedLength = ulProtocolReservedLength;
  44. pPool->ulPacketsPerBlock = ulPacketsPerBlock;
  45. pPool->ulMaxPackets = ulMaxPackets;
  46. pPool->ulFreesSinceCollection = 0;
  47. pPool->ulTag = ulTag;
  48. if (ulFreesPerCollection)
  49. {
  50. pPool->ulFreesPerCollection = ulFreesPerCollection;
  51. }
  52. else
  53. {
  54. // Calculate default garbage collection trigger. Don't want to be too
  55. // aggressive here.
  56. //
  57. pPool->ulFreesPerCollection = 50 * pPool->ulPacketsPerBlock;
  58. }
  59. TRACE( TL_N, TM_Pool, ( "InitPp tag=$%08x pr=%d cnt=%d",
  60. pPool->ulTag,
  61. pPool->ulProtocolReservedLength,
  62. pPool->ulPacketsPerBlock ) );
  63. InitializeListHead( &pPool->listBlocks );
  64. InitializeListHead( &pPool->listFreePackets );
  65. NdisAllocateSpinLock( &pPool->lock );
  66. }
  67. BOOLEAN
  68. FreePacketPool(
  69. IN PACKETPOOL* pPool )
  70. // Free up all resources allocated in packet pool 'pPool'. This is the
  71. // inverse of InitPacketPool.
  72. //
  73. // Returns true if successful, false if any of the pool could not be freed
  74. // due to outstanding packets.
  75. //
  76. {
  77. BOOLEAN fSuccess;
  78. TRACE( TL_N, TM_Pool, ( "FreePp" ) );
  79. NdisAcquireSpinLock( &pPool->lock );
  80. {
  81. FreeUnusedPacketPoolBlocks( pPool );
  82. fSuccess = (pPool->ulCurPackets == 0);
  83. }
  84. NdisReleaseSpinLock( &pPool->lock );
  85. return fSuccess;
  86. }
  87. NDIS_PACKET*
  88. GetPacketFromPool(
  89. IN PACKETPOOL* pPool,
  90. OUT PACKETHEAD** ppHead )
  91. // Returns the address of the NDIS_PACKET descriptor allocated from the
  92. // pool 'pPool'. The pool is expanded, if necessary, but caller should
  93. // still check for NULL return since the pool may have been at maximum
  94. // size. 'PpHead' is the "cookie" that is used to return the packet to
  95. // the pool (see FreePacketToPool). Caller would normally stash this
  96. // value in the appropriate 'reserved' areas of the packet for retrieval
  97. // later.
  98. //
  99. {
  100. LIST_ENTRY* pLink;
  101. PACKETHEAD* pHead;
  102. NDIS_PACKET* pPacket;
  103. NdisAcquireSpinLock( &pPool->lock );
  104. {
  105. if (IsListEmpty( &pPool->listFreePackets ))
  106. {
  107. pLink = NULL;
  108. }
  109. else
  110. {
  111. pLink = RemoveHeadList( &pPool->listFreePackets );
  112. pHead = CONTAINING_RECORD( pLink, PACKETHEAD, linkFreePackets );
  113. --pHead->pBlock->ulFreePackets;
  114. }
  115. }
  116. NdisReleaseSpinLock( &pPool->lock );
  117. if (!pLink)
  118. {
  119. // The free list was empty. Try to expand the pool.
  120. //
  121. pHead = AddPacketBlockToPool( pPool );
  122. if (!pHead)
  123. {
  124. TRACE( TL_A, TM_Pool, ( "GetPfP failed?" ) );
  125. return NULL;
  126. }
  127. }
  128. TRACE( TL_N, TM_Pool,
  129. ( "GetPfP=$%p/h=$%p, %d free",
  130. pHead->pNdisPacket, pHead, pHead->pBlock->ulFreePackets ) );
  131. *ppHead = pHead;
  132. return pHead->pNdisPacket;
  133. }
  134. VOID
  135. FreePacketToPool(
  136. IN PACKETPOOL* pPool,
  137. IN PACKETHEAD* pHead,
  138. IN BOOLEAN fGarbageCollection )
  139. // Returns 'pPacket' to the pool of unused packets 'pPool'. 'PPacket'
  140. // must have been previously allocated with GetPacketFromPool.
  141. // 'FGarbageCollection' is set when the free should be considered for
  142. // purposes of garbage collection. This is used by the AddPacketToPool
  143. // routine to avoid counting the initial "add" frees. Normal callers
  144. // should set this flag.
  145. //
  146. {
  147. DBG_if (fGarbageCollection)
  148. {
  149. TRACE( TL_N, TM_Pool,
  150. ( "FreePtoP($%p,h=$%p) %d free",
  151. pHead->pNdisPacket, pHead, pHead->pBlock->ulFreePackets ) );
  152. }
  153. NdisAcquireSpinLock( &pPool->lock );
  154. {
  155. InsertHeadList( &pPool->listFreePackets, &pHead->linkFreePackets );
  156. ++pHead->pBlock->ulFreePackets;
  157. if (fGarbageCollection)
  158. {
  159. ++pPool->ulFreesSinceCollection;
  160. if (pPool->ulFreesSinceCollection >= pPool->ulFreesPerCollection)
  161. {
  162. // Time to collect garbage, i.e. free any blocks in the pool
  163. // not in use.
  164. //
  165. FreeUnusedPacketPoolBlocks( pPool );
  166. pPool->ulFreesSinceCollection = 0;
  167. }
  168. }
  169. }
  170. NdisReleaseSpinLock( &pPool->lock );
  171. }
  172. //-----------------------------------------------------------------------------
  173. // Utility routines (alphabetically)
  174. //-----------------------------------------------------------------------------
  175. PACKETHEAD*
  176. AddPacketBlockToPool(
  177. IN PACKETPOOL* pPool )
  178. // Allocate a new packet block and add it to the packet pool 'pPool'.
  179. //
  180. // Returns the PACKETHEAD allocated from the pool or NULL if none.
  181. //
  182. {
  183. NDIS_STATUS status;
  184. PACKETBLOCKHEAD* pNew;
  185. ULONG ulSize;
  186. ULONG ulCount;
  187. BOOLEAN fOk;
  188. PACKETHEAD* pReturn;
  189. TRACE( TL_A, TM_Pool, ( "AddPpBlock(%d+%d)",
  190. pPool->ulCurPackets, pPool->ulPacketsPerBlock ) );
  191. fOk = FALSE;
  192. pNew = NULL;
  193. NdisAcquireSpinLock( &pPool->lock );
  194. {
  195. do
  196. {
  197. if (pPool->ulMaxPackets
  198. && pPool->ulCurPackets >= pPool->ulMaxPackets)
  199. {
  200. // No can do. The pool's already at maximum size.
  201. //
  202. TRACE( TL_A, TM_Pool, ( "Pp maxed?" ) );
  203. break;
  204. }
  205. // Calculate the contiguous block's size and the number of packets
  206. // it will hold.
  207. //
  208. ulCount = pPool->ulPacketsPerBlock;
  209. if (pPool->ulMaxPackets)
  210. {
  211. if (ulCount > pPool->ulMaxPackets - pPool->ulCurPackets)
  212. ulCount = pPool->ulMaxPackets - pPool->ulCurPackets;
  213. }
  214. ulSize = sizeof(PACKETBLOCKHEAD) + (ulCount * sizeof(PACKETHEAD));
  215. // Allocate the contiguous memory block for the PACKETBLOCK header
  216. // and the individual PACKETHEADs.
  217. //
  218. pNew = ALLOC_NONPAGED( ulSize, pPool->ulTag );
  219. if (!pNew)
  220. {
  221. TRACE( TL_A, TM_Pool, ( "Alloc PB?") );
  222. break;
  223. }
  224. /* Zero only the block header portion.
  225. */
  226. NdisZeroMemory( pNew, sizeof(PACKETBLOCKHEAD) );
  227. // Allocate a pool of NDIS_PACKET descriptors.
  228. //
  229. NdisAllocatePacketPool(
  230. &status,
  231. &pNew->hNdisPool,
  232. ulCount,
  233. pPool->ulProtocolReservedLength );
  234. if (status != NDIS_STATUS_SUCCESS)
  235. {
  236. TRACE( TL_A, TM_Pool, ( "AllocPp=$%p?", status ) );
  237. break;
  238. }
  239. // Fill in the back pointer to the pool.
  240. //
  241. pNew->pPool = pPool;
  242. // Link the new block. At this point, all the packets are
  243. // effectively "in use". They are made available in the loop
  244. // below.
  245. //
  246. pNew->ulPackets = ulCount;
  247. pPool->ulCurPackets += ulCount;
  248. InsertHeadList( &pPool->listBlocks, &pNew->linkBlocks );
  249. fOk = TRUE;
  250. }
  251. while (FALSE);
  252. }
  253. NdisReleaseSpinLock( &pPool->lock );
  254. if (!fOk)
  255. {
  256. // Bailing, undo whatever succeeded.
  257. //
  258. if (pNew)
  259. {
  260. FREE_NONPAGED( pNew );
  261. if (pNew->hNdisPool)
  262. {
  263. NdisFreePacketPool( pNew->hNdisPool );
  264. }
  265. }
  266. return NULL;
  267. }
  268. // Initialize each individual packet header and add it to the list of free
  269. // packets.
  270. //
  271. {
  272. ULONG i;
  273. PACKETHEAD* pHead;
  274. pReturn = NULL;
  275. // For each PACKETHEAD of the block...
  276. //
  277. for (i = 0, pHead = (PACKETHEAD* )(pNew + 1);
  278. i < ulCount;
  279. ++i, ++pHead)
  280. {
  281. InitializeListHead( &pHead->linkFreePackets );
  282. pHead->pBlock = pNew;
  283. pHead->pNdisPacket = NULL;
  284. // Associate an NDIS_PACKET descriptor from the pool we
  285. // allocated above.
  286. //
  287. NdisAllocatePacket( &status, &pHead->pNdisPacket, pNew->hNdisPool );
  288. if (status != NDIS_STATUS_SUCCESS)
  289. {
  290. TRACE( TL_A, TM_Pool, ( "AllocP=$%p?", status ) );
  291. continue;
  292. }
  293. if (pReturn)
  294. {
  295. // Add the constructed packet to the list of free packets.
  296. // The 'FALSE' tells the garbage collection algorithm the
  297. // operation is an "add" rather than a "release" and should be
  298. // ignored.
  299. //
  300. FreePacketToPool( pPool, pHead, FALSE );
  301. }
  302. else
  303. {
  304. // The first successfully constructed packet is returned by
  305. // this routine.
  306. //
  307. pReturn = pHead;
  308. }
  309. }
  310. }
  311. return pReturn;
  312. }
  313. VOID
  314. FreeUnusedPacketPoolBlocks(
  315. IN PACKETPOOL* pPool )
  316. // Check if any of the blocks in pool 'pPool' are not in use, and if so,
  317. // free them.
  318. //
  319. // IMPORTANT: Caller must hold the pool lock.
  320. //
  321. // NOTE: The MSDN doc says that no locks may be held while calling
  322. // NdisFreePacketXxx, but according to JameelH that is incorrect.
  323. //
  324. {
  325. LIST_ENTRY* pLink;
  326. TRACE( TL_A, TM_Pool, ( "FreeUnusedPpBlocks" ) );
  327. // For each block in the pool...
  328. //
  329. pLink = pPool->listBlocks.Flink;
  330. while (pLink != &pPool->listBlocks)
  331. {
  332. LIST_ENTRY* pLinkNext;
  333. PACKETBLOCKHEAD* pBlock;
  334. pLinkNext = pLink->Flink;
  335. pBlock = CONTAINING_RECORD( pLink, PACKETBLOCKHEAD, linkBlocks );
  336. if (pBlock->ulFreePackets >= pBlock->ulPackets)
  337. {
  338. #if 1 // Assume all buffers are free at time of call.
  339. ULONG i;
  340. PACKETHEAD* pHead;
  341. TRACE( TL_A, TM_Pool, ( "FreePpBlock(%d-%d)",
  342. pPool->ulCurPackets, pPool->ulPacketsPerBlock ) );
  343. // Found a block with no packets in use. Walk the packet block
  344. // removing each packet from the pool's free list and freeing any
  345. // associated NDIS_PACKET descriptor.
  346. //
  347. for (i = 0, pHead = (PACKETHEAD* )(pBlock + 1);
  348. i < pBlock->ulPackets;
  349. ++i, ++pHead)
  350. {
  351. RemoveEntryList( &pHead->linkFreePackets );
  352. if (pHead->pNdisPacket)
  353. {
  354. NdisFreePacket( pHead->pNdisPacket );
  355. }
  356. }
  357. #else // Assume some buffers may not be free at time of call.
  358. LIST_ENTRY* pLink2;
  359. // Found a block with no packets in use. Walk the pool's free
  360. // list looking for buffers from this block.
  361. //
  362. TRACE( TL_A, TM_Pool, ( "FreePpBlock(%d-%d)",
  363. pPool->ulCurPackets, pPool->ulPacketsPerBlock ) );
  364. pLink2 = pPool->listFreePackets.Flink;
  365. while (pLink2 != &pPool->listFreePackets)
  366. {
  367. LIST_ENTRY* pLink2Next;
  368. PACKETHEAD* pHead;
  369. pLink2Next = pLink2->Flink;
  370. pHead = CONTAINING_RECORD( pLink2, PACKETHEAD, linkFreePackets );
  371. if (pHead->pBlock == pBlock)
  372. {
  373. // Found a packet from the unused block. Remove it.
  374. //
  375. RemoveEntryList( pLink2 );
  376. --pBlock->ulFreePackets;
  377. if (pHead->pNdisPacket)
  378. {
  379. NdisFreePacket( pHead->pNdisPacket );
  380. }
  381. }
  382. }
  383. ASSERT( pBlock->ulFreePackets == 0 );
  384. #endif
  385. // Remove and release the unused block.
  386. //
  387. RemoveEntryList( pLink );
  388. pPool->ulCurPackets -= pBlock->ulPackets;
  389. if (pBlock->hNdisPool)
  390. {
  391. NdisFreePacketPool( pBlock->hNdisPool );
  392. }
  393. FREE_NONPAGED( pBlock );
  394. }
  395. pLink = pLinkNext;
  396. }
  397. }