Leaked source code of windows server 2003
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.

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