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.

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