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.

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