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.

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