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.

552 lines
16 KiB

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