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.

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