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.

578 lines
17 KiB

  1. // Copyright (c) 1997, Microsoft Corporation, all rights reserved
  2. //
  3. // bpool.c
  4. // RAS L2TP WAN mini-port/call-manager driver
  5. // Buffer 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_ulDoubleBufferFrees = 0;
  12. // Debug count of calls to NdisAllocateBuffer/NdisCopyBuffer/NdisFreeBuffer,
  13. // where the total of Alloc and Copy should equal Free in idle state.
  14. //
  15. ULONG g_ulNdisAllocateBuffers = 0;
  16. ULONG g_ulNdisCopyBuffers = 0;
  17. ULONG g_ulNdisFreeBuffers = 0;
  18. //-----------------------------------------------------------------------------
  19. // Local prototypes (alphabetically)
  20. //-----------------------------------------------------------------------------
  21. CHAR*
  22. AddBufferBlockToPool(
  23. IN BUFFERPOOL* pPool );
  24. VOID
  25. FreeUnusedBufferPoolBlocks(
  26. IN BUFFERPOOL* pPool );
  27. //-----------------------------------------------------------------------------
  28. // Interface routines
  29. //-----------------------------------------------------------------------------
  30. VOID
  31. InitBufferPool(
  32. OUT BUFFERPOOL* pPool,
  33. IN ULONG ulBufferSize,
  34. IN ULONG ulMaxBuffers,
  35. IN ULONG ulBuffersPerBlock,
  36. IN ULONG ulFreesPerCollection,
  37. IN BOOLEAN fAssociateNdisBuffer,
  38. IN ULONG ulTag )
  39. // Initialize caller's buffer pool control block 'pPool'. 'UlBufferSize'
  40. // is the size in bytes of an individual buffer. 'UlMaxBuffers' is the
  41. // maximum number of buffers allowed in the entire pool or 0 for
  42. // unlimited. 'UlBuffersPerBlock' is the number of buffers to include in
  43. // each block of buffers. 'UlFreesPerCollection' is the number of
  44. // FreeBufferToPool calls until the next garbage collect scan, or 0 for
  45. // default. 'FAssociateNdisBuffer' is set if an NDIS_BUFFER should be
  46. // allocated and associated with each individual buffer. 'UlTag' is the
  47. // memory identification tag to use when allocating blocks.
  48. //
  49. // IMPORTANT: Caller's 'pPool' buffer must be protected from multiple
  50. // access during this call.
  51. //
  52. {
  53. // The requested buffer size is padded, if necessary, so it alligns
  54. // properly when buffer blocks are layed out. The alignment rule also
  55. // applies to the BUFFERBLOCKHEAD and BUFFERHEAD structures, which
  56. // currently align perfectly. We will verify once here, rather than code
  57. // around everywhere else.
  58. //
  59. ASSERT( (ALIGN_UP( sizeof(BUFFERBLOCKHEAD), ULONGLONG )
  60. == sizeof(BUFFERBLOCKHEAD)) );
  61. ASSERT( (ALIGN_UP( sizeof(BUFFERHEAD), ULONGLONG )
  62. == sizeof(BUFFERHEAD)) );
  63. pPool->ulBufferSize = ALIGN_UP( ulBufferSize, ULONGLONG );
  64. pPool->ulMaxBuffers = ulMaxBuffers;
  65. pPool->ulBuffersPerBlock = ulBuffersPerBlock;
  66. pPool->ulFreesSinceCollection = 0;
  67. pPool->fAssociateNdisBuffer = fAssociateNdisBuffer;
  68. pPool->ulTag = ulTag;
  69. if (ulFreesPerCollection)
  70. {
  71. pPool->ulFreesPerCollection = ulFreesPerCollection;
  72. }
  73. else
  74. {
  75. // Calculate default garbage collection trigger. Don't want to be too
  76. // aggressive here.
  77. //
  78. pPool->ulFreesPerCollection = 200 * pPool->ulBuffersPerBlock;
  79. }
  80. TRACE( TL_N, TM_Pool, ( "InitBp tag=$%08x buf=%d cnt=%d",
  81. pPool->ulTag, pPool->ulBufferSize, pPool->ulBuffersPerBlock ) );
  82. InitializeListHead( &pPool->listBlocks );
  83. InitializeListHead( &pPool->listFreeBuffers );
  84. NdisAllocateSpinLock( &pPool->lock );
  85. }
  86. BOOLEAN
  87. FreeBufferPool(
  88. IN BUFFERPOOL* pPool )
  89. // Free up all resources allocated in buffer pool 'pPool'. This is the
  90. // inverse of InitBufferPool.
  91. //
  92. // Returns true if successful, false if any of the pool could not be freed
  93. // due to outstanding packets.
  94. //
  95. {
  96. BOOLEAN fSuccess;
  97. TRACE( TL_N, TM_Pool, ( "FreeBp" ) );
  98. NdisAcquireSpinLock( &pPool->lock );
  99. {
  100. FreeUnusedBufferPoolBlocks( pPool );
  101. fSuccess = (pPool->ulCurBuffers == 0);
  102. }
  103. NdisReleaseSpinLock( &pPool->lock );
  104. return fSuccess;
  105. }
  106. CHAR*
  107. GetBufferFromPool(
  108. IN BUFFERPOOL* pPool )
  109. // Returns the address of the useable memory in an individual buffer
  110. // allocated from the pool 'pPool'. The pool is expanded, if necessary,
  111. // but caller should still check for NULL return since the pool may have
  112. // been at maximum size.
  113. //
  114. {
  115. LIST_ENTRY* pLink;
  116. BUFFERHEAD* pHead;
  117. CHAR* pBuffer;
  118. NdisAcquireSpinLock( &pPool->lock );
  119. {
  120. if (IsListEmpty( &pPool->listFreeBuffers ))
  121. {
  122. pLink = NULL;
  123. }
  124. else
  125. {
  126. pLink = RemoveHeadList( &pPool->listFreeBuffers );
  127. InitializeListHead( pLink );
  128. pHead = CONTAINING_RECORD( pLink, BUFFERHEAD, linkFreeBuffers );
  129. --pHead->pBlock->ulFreeBuffers;
  130. }
  131. }
  132. NdisReleaseSpinLock( &pPool->lock );
  133. if (pLink)
  134. {
  135. pBuffer = (CHAR* )(pHead + 1);
  136. }
  137. else
  138. {
  139. // The free list was empty. Try to expand the pool.
  140. //
  141. pBuffer = AddBufferBlockToPool( pPool );
  142. }
  143. DBG_if (pBuffer)
  144. {
  145. pHead = (BUFFERHEAD* )(pBuffer - sizeof(BUFFERHEAD));
  146. TRACE( TL_N, TM_Pool, ( "GetBfp=$%p, %d free",
  147. pBuffer, pHead->pBlock->ulFreeBuffers ) );
  148. }
  149. DBG_else
  150. {
  151. TRACE( TL_A, TM_Pool, ( "GetBfp failed?" ) );
  152. }
  153. return pBuffer;
  154. }
  155. VOID
  156. FreeBufferToPool(
  157. IN BUFFERPOOL* pPool,
  158. IN CHAR* pBuffer,
  159. IN BOOLEAN fGarbageCollection )
  160. // Returns 'pBuffer' to the pool of unused buffers 'pPool'. 'PBuffer'
  161. // must have been previously allocated with GetBufferFromPool.
  162. // 'FGarbageCollection' is set when the free should be considered for
  163. // purposes of garbage collection. This is used by the AddBufferToPool
  164. // routine to avoid counting the initial "add" frees. Normal callers
  165. // should set this flag.
  166. //
  167. {
  168. BUFFERHEAD* pHead;
  169. pHead = ((BUFFERHEAD* )pBuffer) - 1;
  170. DBG_if (fGarbageCollection)
  171. {
  172. TRACE( TL_I, TM_Pool, ( "FreeBtoP($%p) %d free",
  173. pBuffer, pHead->pBlock->ulFreeBuffers + 1 ) );
  174. }
  175. // Requested by Chun Ye to catch IPSEC problem.
  176. //
  177. ASSERT( pHead->pNdisBuffer && !((MDL* )pHead->pNdisBuffer)->Next );
  178. NdisAcquireSpinLock( &pPool->lock );
  179. do
  180. {
  181. if (pHead->linkFreeBuffers.Flink != &pHead->linkFreeBuffers)
  182. {
  183. ASSERT( !"Double free?" );
  184. ++g_ulDoubleBufferFrees;
  185. break;
  186. }
  187. InsertHeadList( &pPool->listFreeBuffers, &pHead->linkFreeBuffers );
  188. ++pHead->pBlock->ulFreeBuffers;
  189. if (fGarbageCollection)
  190. {
  191. ++pPool->ulFreesSinceCollection;
  192. if (pPool->ulFreesSinceCollection >= pPool->ulFreesPerCollection)
  193. {
  194. // Time to collect garbage, i.e. free any blocks in the
  195. // pool not in use.
  196. //
  197. FreeUnusedBufferPoolBlocks( pPool );
  198. pPool->ulFreesSinceCollection = 0;
  199. }
  200. }
  201. }
  202. while (FALSE);
  203. NdisReleaseSpinLock( &pPool->lock );
  204. }
  205. NDIS_BUFFER*
  206. NdisBufferFromBuffer(
  207. IN CHAR* pBuffer )
  208. // Returns the NDIS_BUFFER associated with the buffer 'pBuffer' which was
  209. // obtained previously with GetBufferFromPool.
  210. //
  211. {
  212. BUFFERHEAD* pHead;
  213. pHead = ((BUFFERHEAD* )pBuffer) - 1;
  214. return pHead->pNdisBuffer;
  215. }
  216. ULONG
  217. BufferSizeFromBuffer(
  218. IN CHAR* pBuffer )
  219. // Returns the original size of the buffer 'pBuffer' which was obtained
  220. // previously with GetBufferFromPool. This is useful for undoing
  221. // NdisAdjustBufferLength.
  222. //
  223. {
  224. BUFFERHEAD* pHead;
  225. pHead = ((BUFFERHEAD* )pBuffer) - 1;
  226. return pHead->pBlock->pPool->ulBufferSize;
  227. }
  228. NDIS_BUFFER*
  229. PoolHandleForNdisCopyBufferFromBuffer(
  230. IN CHAR* pBuffer )
  231. // Returns the handle of the pool from which the NDIS_BUFFER associated
  232. // with the buffer 'pBuffer' was obtained. Caller may use the handle to
  233. // pass to NdisCopyBuffer, one such use per buffer at a time.
  234. //
  235. {
  236. BUFFERHEAD* pHead;
  237. pHead = ((BUFFERHEAD* )pBuffer) - 1;
  238. return pHead->pBlock->hNdisPool;
  239. }
  240. VOID
  241. CollectBufferPoolGarbage(
  242. BUFFERPOOL* pPool )
  243. // Force a garbage collection event on the pool 'pPool'.
  244. //
  245. {
  246. NdisAcquireSpinLock( &pPool->lock );
  247. {
  248. FreeUnusedBufferPoolBlocks( pPool );
  249. pPool->ulFreesSinceCollection = 0;
  250. }
  251. NdisReleaseSpinLock( &pPool->lock );
  252. }
  253. //-----------------------------------------------------------------------------
  254. // Local utility routines (alphabetically)
  255. //-----------------------------------------------------------------------------
  256. CHAR*
  257. AddBufferBlockToPool(
  258. IN BUFFERPOOL* pPool )
  259. // Allocate a new buffer block and add it to the buffer pool 'pPool'.
  260. //
  261. // Returns the address of the usable memory of an individual buffer
  262. // allocated from the pool or NULL if none.
  263. //
  264. {
  265. NDIS_STATUS status;
  266. BUFFERBLOCKHEAD* pNew;
  267. ULONG ulSize;
  268. ULONG ulCount;
  269. BOOLEAN fOk;
  270. BOOLEAN fAssociateNdisBuffer;
  271. CHAR* pReturn;
  272. TRACE( TL_A, TM_Pool, ( "AddBpBlock(%d+%d)",
  273. pPool->ulCurBuffers, pPool->ulBuffersPerBlock ) );
  274. fOk = FALSE;
  275. pNew = NULL;
  276. NdisAcquireSpinLock( &pPool->lock );
  277. {
  278. // Save this for reference after the lock is released.
  279. //
  280. fAssociateNdisBuffer = pPool->fAssociateNdisBuffer;
  281. do
  282. {
  283. if (pPool->ulMaxBuffers
  284. && pPool->ulCurBuffers >= pPool->ulMaxBuffers)
  285. {
  286. // No can do. The pool's already at maximum size.
  287. //
  288. TRACE( TL_A, TM_Pool, ( "Bp maxed?" ) );
  289. break;
  290. }
  291. // Calculate the contiguous block's size and the number of buffers
  292. // it will hold.
  293. //
  294. ulCount = pPool->ulBuffersPerBlock;
  295. if (pPool->ulMaxBuffers)
  296. {
  297. if (ulCount > pPool->ulMaxBuffers - pPool->ulCurBuffers)
  298. {
  299. ulCount = pPool->ulMaxBuffers - pPool->ulCurBuffers;
  300. }
  301. }
  302. ulSize = sizeof(BUFFERBLOCKHEAD) +
  303. (ulCount * (sizeof(BUFFERHEAD) + pPool->ulBufferSize));
  304. // Allocate the contiguous memory block for the BUFFERBLOCK header
  305. // and the individual buffers.
  306. //
  307. pNew = ALLOC_NONPAGED( ulSize, pPool->ulTag );
  308. if (!pNew)
  309. {
  310. TRACE( TL_A, TM_Pool, ( "Alloc BB?" ) );
  311. break;
  312. }
  313. // Zero only the block header portion.
  314. //
  315. NdisZeroMemory( pNew, sizeof(BUFFERBLOCKHEAD) );
  316. if (fAssociateNdisBuffer)
  317. {
  318. // Allocate a pool of NDIS_BUFFER descriptors.
  319. //
  320. // Twice as many descriptors are allocated as buffers so
  321. // caller can use the PoolHandleForNdisCopyBufferFromBuffer
  322. // routine to obtain a pool handle to pass to the
  323. // NdisCopyBuffer used to trim the L2TP header from received
  324. // packets. In the current NDIS implmentation on NT this does
  325. // nothing but return a NULL handle and STATUS_SUCCESS,
  326. // because NDIS_BUFFER's are just MDL's,
  327. // NdisAllocateBufferPool is basically a no-op, and for that
  328. // matter, NdisCopyBuffer doesn't really use the pool handle
  329. // it's passed. It's cheap to stay strictly compliant here,
  330. // though, so we do that.
  331. //
  332. NdisAllocateBufferPool(
  333. &status, &pNew->hNdisPool, ulCount * 2 );
  334. if (status != NDIS_STATUS_SUCCESS)
  335. {
  336. TRACE( TL_A, TM_Pool, ( "AllocBp=$%x?", status ) );
  337. break;
  338. }
  339. }
  340. // Fill in the back pointer to the pool.
  341. //
  342. pNew->pPool = pPool;
  343. // Link the new block. At this point, all the buffers are
  344. // effectively "in use". They are made available in the loop
  345. // below.
  346. //
  347. pNew->ulBuffers = ulCount;
  348. pPool->ulCurBuffers += ulCount;
  349. InsertHeadList( &pPool->listBlocks, &pNew->linkBlocks );
  350. fOk = TRUE;
  351. }
  352. while (FALSE);
  353. }
  354. NdisReleaseSpinLock( &pPool->lock );
  355. if (!fOk)
  356. {
  357. // Bailing, undo whatever succeeded.
  358. //
  359. if (pNew)
  360. {
  361. if (pNew->hNdisPool)
  362. {
  363. NdisFreeBufferPool( pNew->hNdisPool );
  364. }
  365. FREE_NONPAGED( pNew );
  366. }
  367. return NULL;
  368. }
  369. // Initialize each individual buffer slice and add it to the list of free
  370. // buffers.
  371. //
  372. {
  373. ULONG i;
  374. CHAR* pBuffer;
  375. BUFFERHEAD* pHead;
  376. pReturn = NULL;
  377. // For each slice of the block, where a slice consists of a BUFFERHEAD
  378. // and the buffer memory that immediately follows it...
  379. //
  380. for (i = 0, pHead = (BUFFERHEAD* )(pNew + 1);
  381. i < ulCount;
  382. ++i, pHead = (BUFFERHEAD* )
  383. ((CHAR* )(pHead + 1) + pPool->ulBufferSize))
  384. {
  385. pBuffer = (CHAR* )(pHead + 1);
  386. InitializeListHead( &pHead->linkFreeBuffers );
  387. pHead->pBlock = pNew;
  388. pHead->pNdisBuffer = NULL;
  389. if (fAssociateNdisBuffer)
  390. {
  391. // Associate an NDIS_BUFFER descriptor from the pool we
  392. // allocated above.
  393. //
  394. NdisAllocateBuffer(
  395. &status, &pHead->pNdisBuffer, pNew->hNdisPool,
  396. pBuffer, pPool->ulBufferSize );
  397. if (status != NDIS_STATUS_SUCCESS)
  398. {
  399. TRACE( TL_A, TM_Pool, ( "AllocB=$%x?", status ) );
  400. ASSERT( FALSE );
  401. pHead->pNdisBuffer = NULL;
  402. continue;
  403. }
  404. else
  405. {
  406. NdisInterlockedIncrement( &g_ulNdisAllocateBuffers );
  407. }
  408. }
  409. if (pReturn)
  410. {
  411. // Add the constructed buffer to the list of free buffers.
  412. // The 'FALSE' tells the garbage collection algorithm the
  413. // operation is an "add" rather than a "release" and should be
  414. // ignored.
  415. //
  416. FreeBufferToPool( pPool, pBuffer, FALSE );
  417. }
  418. else
  419. {
  420. // The first successfully constructed buffer is returned by
  421. // this routine.
  422. //
  423. pReturn = pBuffer;
  424. }
  425. }
  426. }
  427. return pReturn;
  428. }
  429. VOID
  430. FreeUnusedBufferPoolBlocks(
  431. IN BUFFERPOOL* pPool )
  432. // Check if any of the blocks in pool 'pPool' are not in use, and if so,
  433. // free them.
  434. //
  435. // IMPORTANT: Caller must hold the pool lock.
  436. //
  437. {
  438. LIST_ENTRY* pLink;
  439. TRACE( TL_A, TM_Pool, ( "FreeUnusedBpBlocks" ) );
  440. // For each block in the pool...
  441. //
  442. pLink = pPool->listBlocks.Flink;
  443. while (pLink != &pPool->listBlocks)
  444. {
  445. LIST_ENTRY* pLinkNext;
  446. BUFFERBLOCKHEAD* pBlock;
  447. pLinkNext = pLink->Flink;
  448. pBlock = CONTAINING_RECORD( pLink, BUFFERBLOCKHEAD, linkBlocks );
  449. if (pBlock->ulFreeBuffers >= pBlock->ulBuffers)
  450. {
  451. ULONG i;
  452. BUFFERHEAD* pHead;
  453. TRACE( TL_A, TM_Pool, ( "FreeBpBlock(%d-%d)",
  454. pPool->ulCurBuffers, pPool->ulBuffersPerBlock ) );
  455. // Found a block with no buffers in use. Walk the buffer block
  456. // removing each buffer from the pool's free list and freeing any
  457. // associated NDIS_BUFFER descriptor.
  458. //
  459. for (i = 0, pHead = (BUFFERHEAD* )(pBlock + 1);
  460. i < pBlock->ulBuffers;
  461. ++i, pHead = (BUFFERHEAD* )
  462. (((CHAR* )(pHead + 1)) + pPool->ulBufferSize))
  463. {
  464. RemoveEntryList( &pHead->linkFreeBuffers );
  465. InitializeListHead( &pHead->linkFreeBuffers );
  466. if (pHead->pNdisBuffer)
  467. {
  468. NdisFreeBuffer( pHead->pNdisBuffer );
  469. NdisInterlockedIncrement( &g_ulNdisFreeBuffers );
  470. }
  471. }
  472. // Remove and release the unused block.
  473. //
  474. RemoveEntryList( pLink );
  475. InitializeListHead( pLink );
  476. pPool->ulCurBuffers -= pBlock->ulBuffers;
  477. if (pBlock->hNdisPool)
  478. {
  479. NdisFreeBufferPool( pBlock->hNdisPool );
  480. }
  481. FREE_NONPAGED( pBlock );
  482. }
  483. pLink = pLinkNext;
  484. }
  485. }