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.

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