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.

2426 lines
74 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Copyright (c) 1991 Nokia Data Systems AB
  4. Module Name:
  5. dlcbuf.c
  6. Abstract:
  7. This module implements the DLC buffer pool manager and provides routines
  8. to lock and unlock transmit buffers outside of the buffer pool
  9. DLC has a buffering scheme inherited from its progenitors right back to
  10. the very first DOS implementation. We must share a buffer pool with an
  11. application: the application allocates memory using any method it desires
  12. and gives us a pointer to that memory and the length. We page-align the
  13. buffer and carve it into pages. Any non page-aligned buffer at the start
  14. or end of the buffer are discarded.
  15. Once DLC has a buffer pool defined, it allocates buffers in a fashion
  16. similar to binary-buddy, or in a method that I shall call 'binary
  17. spouse'. Blocks are initially all contained in page sized units. As
  18. smaller blocks are required, a larger block is repeatedly split into 2
  19. until a i-block is generated where 2**i >= block size required. Unlike
  20. binary-buddy, the binary-spouse method does not coalesce buddy blocks to
  21. create larger buffers once split. Once divorced from each other, binary
  22. spouse blocks are unlikely to get back together.
  23. BufferPoolAllocate is the function that single-handedly implements the
  24. allocator mechanism. It basically handles 2 types of request in the same
  25. routine: the first request is from BUFFER.GET where a buffer will be
  26. returned to the app as a single buffer if we have a block available that
  27. is large enough to satisfy the request. If the request cannot be satisfied
  28. by a single block, we return a chain of smaller blocks. The second type of
  29. request is from the data receive DPC processing where we have to supply a
  30. single block to contain the data. Luckily, through the magic of MDLs, we
  31. can return several smaller blocks linked together by MDLs which masquerade
  32. as a single buffer. Additionally, we can create buffers larger than a
  33. single page in the same manner. This receive buffer must later be handed
  34. to the app in the same format as the buffer allocated by BUFFER.GET, so we
  35. need to be able to view this kind of buffer in 2 ways. This accounts for
  36. the complexity of the various headers and MDL descriptors which must be
  37. applied to the allocated blocks
  38. Contents:
  39. BufferPoolCreate
  40. BufferPoolExpand
  41. BufferPoolFreeExtraPages
  42. DeallocateBuffer
  43. AllocateBufferHeader
  44. BufferPoolAllocate
  45. BufferPoolDeallocate
  46. BufferPoolDeallocateList
  47. BufferPoolBuildXmitBuffers
  48. BufferPoolFreeXmitBuffers
  49. GetBufferHeader
  50. BufferPoolDereference
  51. BufferPoolReference
  52. ProbeVirtualBuffer
  53. AllocateProbeAndLockMdl
  54. BuildMappedPartialMdl
  55. UnlockAndFreeMdl
  56. Author:
  57. Antti Saarenheimo 12-Jul-1991
  58. Environment:
  59. Kernel mode
  60. Revision History:
  61. --*/
  62. #include <dlc.h>
  63. #include <memory.h>
  64. #include "dlcdebug.h"
  65. //
  66. // LOCK/UNLOCK_BUFFER_POOL - acquires or releases per-buffer pool spin lock.
  67. // Use kernel spin locking calls. Assumes variables are called "pBufferPool" and
  68. // "irql"
  69. //
  70. #define LOCK_BUFFER_POOL() KeAcquireSpinLock(&pBufferPool->SpinLock, &irql)
  71. #define UNLOCK_BUFFER_POOL() KeReleaseSpinLock(&pBufferPool->SpinLock, irql)
  72. //
  73. // data
  74. //
  75. PDLC_BUFFER_POOL pBufferPools = NULL;
  76. #define CHECK_FREE_SEGMENT_COUNT(pBuffer)
  77. /*
  78. Enable this, if the free segment size checking fails:
  79. #define CHECK_FREE_SEGMENT_COUNT(pBuffer) CheckFreeSegmentCount(pBuffer)
  80. VOID
  81. CheckFreeSegmentCount(
  82. PDLC_BUFFER_HEADER pBuffer
  83. );
  84. VOID
  85. CheckFreeSegmentCount(
  86. PDLC_BUFFER_HEADER pBuffer
  87. )
  88. {
  89. PDLC_BUFFER_HEADER pTmp;
  90. UINT FreeSegments = 0;
  91. for (pTmp = (pBuffer)->FreeBuffer.pParent->Header.pNextChild;
  92. pTmp != NULL;
  93. pTmp = pTmp->FreeBuffer.pNextChild) {
  94. if (pTmp->FreeBuffer.BufferState == BUF_READY) {
  95. FreeSegments += pTmp->FreeBuffer.Size;
  96. }
  97. }
  98. if (FreeSegments != (pBuffer)->FreeBuffer.pParent->Header.FreeSegments) {
  99. DbgBreakPoint();
  100. }
  101. }
  102. */
  103. /*++
  104. DLC Buffer Manager
  105. ------------------
  106. The buffer pool consists of virtual memory blocks, that must be allocated
  107. by the application program. The buffer block descriptors are allocated
  108. separately from the non-paged pool, because they must be safe from
  109. any memory corruption done in by an application.
  110. The memory allocation strategy is binary buddy. All segments are
  111. exponents of 2 between 256 and 4096. There is no official connection with
  112. the system page size, but actually all segments are allocated within
  113. a page and thus we minimize MDL sizes needed for them. Contiguous
  114. buffer blocks decrease also the DMA overhead. The initial user buffer
  115. is first split to its maximal binary components. The components
  116. are split further in the run time, when the buffer manager runs
  117. out of the smaller segments (eg. it splits a 1024 segment to one
  118. 512 segment and two 256 segments, if it run out of 256 segments and
  119. there were no free 512 segments either).
  120. The clients of the buffer manager allocates buffer lists. They consists
  121. of minimal number of binary segments. For example, a 1600 bytes buffer
  122. request would return a list of 1024, 512 and 256 segments. The smallest
  123. segment is the first and the biggest is the last. The application
  124. program must deallocate all segments returned to it in the receive.
  125. The validity of all deallocated segments is checked with a reservation list.
  126. Buffer Manager provides api commands:
  127. - to initialize a buffer pool (pool constructor)
  128. - to add locked and mapped virtual memory to the buffer
  129. - to deallocate the buffer pool (destructor)
  130. - to allocate a segment list (allocator)
  131. - to deallocate a segment list (deallocator)
  132. - to set Thresholds for the minimum buffer size
  133. --*/
  134. /*++
  135. MEMORY COMMITMENT
  136. The commitement of buffer pools is a special service expecially for
  137. the local busy state management of the link stations. By default
  138. the uncommitted memory is the same as the free memory in the buffer
  139. pool minus the minimum free Threshold, but when a link enters to a
  140. busy state we know how much buffer space the link will need
  141. to receive at least the next frame. Actually we will commit all
  142. I- packets received in the local busy state. The local 'out of buffers' busy
  143. state will be cleared only when there is enough uncommited space in the
  144. buffer pool to receive all expected packets. We still indicate the local
  145. busy state to user, because the flow control function can expand the buffer
  146. pool, if it is necessary. We will just queue the clear local busy state
  147. command to a command queue (even if we complete it immediately),
  148. we don;t execute the queued command before there is enough uncommited space
  149. to enable the link receive.
  150. The buffer space is committed by the size of all expected packets,
  151. when the local busy state of a link is cleared.
  152. All received packets are subracted from the commited buffer space
  153. as far as the link has any committed memory. This may happen only
  154. after a local busy states.
  155. We will provide three macroes to
  156. BufGetPacketSize(PacketSize) - returns probable size of packet in buffers
  157. BufGetUncommittedSpace(hBufferPool) - gets the current uncommited space
  158. BufCommitBuffers(hBufferPool, BufferSize) - commits the given size
  159. BufUncommitBuffers(hBufferPool, PacketSize) - uncommites a packet
  160. --*/
  161. NTSTATUS
  162. ProbeVirtualBuffer(
  163. IN PUCHAR pBuffer,
  164. IN LONG Length
  165. );
  166. NTSTATUS
  167. BufferPoolCreate(
  168. #if DBG
  169. IN PDLC_FILE_CONTEXT pFileContext,
  170. #endif
  171. IN PVOID pUserBuffer,
  172. IN LONG MaxBufferSize,
  173. IN LONG MinFreeSizeThreshold,
  174. OUT HANDLE *pBufferPoolHandle,
  175. OUT PVOID* AlignedAddress,
  176. OUT PULONG AlignedSize
  177. )
  178. /*++
  179. Routine Description:
  180. This routine performs initialization of the NT DLC API buffer pool.
  181. It allocates the buffer descriptor and the initial header blocks.
  182. Arguments:
  183. pFileContext - pointer to DLC_FILE_CONTEXT structure
  184. pUserBuffer - virtual base address of the buffer
  185. MaxBufferSize - the maximum size of the buffer space
  186. MinFreeSizeThreshold - the minimum free space in the buffer
  187. pBufferPoolHandle - the parameter returns the handle of buffer pool,
  188. the same buffer pool may be shared by several
  189. open contexts of one or more dlc applications.
  190. AlignedAddress - we return the page-aligned buffer pool address
  191. AlignedSize - and the page-aligned buffer pool size
  192. Return Value:
  193. Returns NTSTATUS is a NT system call fails.
  194. --*/
  195. {
  196. NTSTATUS status;
  197. PDLC_BUFFER_POOL pBufferPool;
  198. PVOID pAlignedBuffer;
  199. INT i;
  200. register PPACKET_POOL pHeaderPool;
  201. ASSUME_IRQL(DISPATCH_LEVEL);
  202. //
  203. // page-align the buffer
  204. //
  205. pAlignedBuffer = (PVOID)(((ULONG_PTR)pUserBuffer + PAGE_SIZE - 1) & -(LONG)PAGE_SIZE);
  206. //
  207. // and make the length an integral number of pages
  208. //
  209. MaxBufferSize = (MaxBufferSize - (ULONG)((ULONG_PTR)pAlignedBuffer - (ULONG_PTR)pUserBuffer)) & -(LONG)PAGE_SIZE;
  210. //
  211. // the buffer size must be at least one page (BufferSize will be 0 if
  212. // this is not so)
  213. //
  214. if (MaxBufferSize <= 0) {
  215. return DLC_STATUS_BUFFER_SIZE_EXCEEDED;
  216. }
  217. //
  218. // if MinFreeSizeThreshold < 0, we can have problems since it is negated later
  219. // on leaving buffer pool uninitialized
  220. //
  221. if (MinFreeSizeThreshold < 0) {
  222. return DLC_STATUS_INVALID_BUFFER_LENGTH;
  223. }
  224. //
  225. // if the size of the buffer is less than the minimum lock size then we lock
  226. // the entire buffer
  227. //
  228. if (MaxBufferSize < MinFreeSizeThreshold) {
  229. MinFreeSizeThreshold = MaxBufferSize;
  230. }
  231. //
  232. // allocate the DLC_BUFFER_POOL structure. This is followed by an array
  233. // of pointers to buffer headers describing the pages in the buffer pool
  234. //
  235. pBufferPool = ALLOCATE_ZEROMEMORY_DRIVER(sizeof(DLC_BUFFER_POOL)
  236. + sizeof(PVOID)
  237. * BYTES_TO_PAGES(MaxBufferSize)
  238. );
  239. if (!pBufferPool) {
  240. return DLC_STATUS_NO_MEMORY;
  241. }
  242. //
  243. // pHeaderPool is a pool of DLC_BUFFER_HEADER structures - one of these
  244. // is used per page locked
  245. //
  246. pHeaderPool = CREATE_BUFFER_POOL_FILE(DlcBufferPoolObject,
  247. sizeof(DLC_BUFFER_HEADER),
  248. 8
  249. );
  250. if (!pHeaderPool) {
  251. FREE_MEMORY_DRIVER(pBufferPool);
  252. return DLC_STATUS_NO_MEMORY;
  253. }
  254. //
  255. // initialize the buffer pool structure
  256. //
  257. pBufferPool->hHeaderPool = pHeaderPool;
  258. KeInitializeSpinLock(&pBufferPool->SpinLock);
  259. //
  260. // UncommittedSpace is the space above the minimum free threshold in the
  261. // locked region of the buffer pool. We set it to the negative of the
  262. // minimum free threshold here to cause BufferPoolExpand to lock down
  263. // the number of pages required to commit the minimum free threshold
  264. //
  265. pBufferPool->UncommittedSpace = -MinFreeSizeThreshold;
  266. //
  267. // MaxBufferSize is the size of the buffer pool rounded down to an integral
  268. // number of pages
  269. //
  270. pBufferPool->MaxBufferSize = (ULONG)MaxBufferSize;
  271. //
  272. // BaseOffset is the page-aligned address of the buffer pool
  273. //
  274. pBufferPool->BaseOffset = pAlignedBuffer;
  275. //
  276. // MaxOffset is the last byte + 1 (?) in the buffer pool
  277. //
  278. pBufferPool->MaxOffset = (PUCHAR)pAlignedBuffer + MaxBufferSize;
  279. //
  280. // MaximumIndex is the number of pages that describe the buffer pool. This
  281. // number is irrespective of the locked state of the pages
  282. //
  283. pBufferPool->MaximumIndex = (ULONG)(MaxBufferSize / MAX_DLC_BUFFER_SEGMENT);
  284. //
  285. // Link all unlocked pages to a link list.
  286. // Put the last pages in the buffer to the end of the list.
  287. //
  288. for (i = (INT)pBufferPool->MaximumIndex - 1; i >= 0; i--) {
  289. pBufferPool->BufferHeaders[i] = pBufferPool->pUnlockedEntryList;
  290. pBufferPool->pUnlockedEntryList = (PDLC_BUFFER_HEADER)&pBufferPool->BufferHeaders[i];
  291. }
  292. for (i = 0; i < DLC_BUFFER_SEGMENTS; i++) {
  293. InitializeListHead(&pBufferPool->FreeLists[i]);
  294. }
  295. InitializeListHead(&pBufferPool->PageHeaders);
  296. //
  297. // We can now lock the initial page buffers for the buffer pool.
  298. // The buffer pool allocation has been failed, if the procedure
  299. // returns an error.
  300. //
  301. #if DBG
  302. status = BufferPoolExpand(pFileContext, pBufferPool);
  303. #else
  304. status = BufferPoolExpand(pBufferPool);
  305. #endif
  306. if (status != STATUS_SUCCESS) {
  307. //
  308. // We must use the standard procedure for deallocation,
  309. // because the memory locking may have succeeded partially.
  310. // The derefence free all resources in the buffer pool.
  311. //
  312. BufferPoolDereference(
  313. #if DBG
  314. pFileContext,
  315. #endif
  316. &pBufferPool
  317. );
  318. } else {
  319. KIRQL irql;
  320. //
  321. // Save the buffer pool handle to the link list of
  322. // buffer pools
  323. //
  324. ACQUIRE_DLC_LOCK(irql);
  325. pBufferPool->pNext = pBufferPools;
  326. pBufferPools = pBufferPool;
  327. RELEASE_DLC_LOCK(irql);
  328. *pBufferPoolHandle = pBufferPool;
  329. *AlignedAddress = pAlignedBuffer;
  330. *AlignedSize = MaxBufferSize;
  331. }
  332. return status;
  333. }
  334. NTSTATUS
  335. BufferPoolExpand(
  336. #if DBG
  337. IN PDLC_FILE_CONTEXT pFileContext,
  338. #endif
  339. IN PDLC_BUFFER_POOL pBufferPool
  340. )
  341. /*++
  342. Routine Description:
  343. The function checks the minimum and maximum size Thresholds and
  344. locks new pages or unlocks the extra pages and deallocates their
  345. buffer headers.
  346. The procedure uses the standard memory management functions
  347. to lock, probe and map the pages.
  348. The MDL buffer is split to smaller buffers (256, 512, ... 4096).
  349. The orginal buffer is split in the 4 kB even address (usually
  350. page border or even with any page size) to minimize PFNs associated
  351. with the MDLs (each MDL needs now only one PFN, to make
  352. DMA overhead smaller and to save locked memory).
  353. This procedure does not actually assume anything about the paging,
  354. but it should work quite well with any paging implementation.
  355. This procedure MUST be called only from the synchronous code path and
  356. all spinlocks unlocked, because of the page locking (the async
  357. code in always on the DPC level and you cannot make pagefaults on
  358. that level).
  359. Arguments:
  360. pBufferPool - handle of buffer pool data structure.
  361. Return Value:
  362. NTSTATUS
  363. Success - STATUS_SUCCESS
  364. Failure - DLC_STATUS_NO_MEMORY
  365. --*/
  366. {
  367. NTSTATUS status = STATUS_SUCCESS;
  368. PDLC_BUFFER_HEADER pBuffer;
  369. KIRQL irql;
  370. ASSUME_IRQL(DISPATCH_LEVEL);
  371. LOCK_BUFFER_POOL();
  372. //
  373. // UncommittedSpace < 0 just means that we've encroached past the minimum
  374. // free threshold and therefore we're in need of more buffer space (hence
  375. // this function)
  376. //
  377. if (((pBufferPool->UncommittedSpace < 0) || (pBufferPool->MissingSize > 0))
  378. && (pBufferPool->BufferPoolSize < pBufferPool->MaxBufferSize)) {
  379. UINT FreeSlotIndex;
  380. while ((pBufferPool->UncommittedSpace < 0) || (pBufferPool->MissingSize > 0)) {
  381. pBuffer = NULL;
  382. //
  383. // if there are no more pages to lock or we can't allocate a header
  384. // to describe the buffer then quit
  385. //
  386. if (!pBufferPool->pUnlockedEntryList
  387. || !(pBuffer = ALLOCATE_PACKET_DLC_BUF(pBufferPool->hHeaderPool))) {
  388. status = DLC_STATUS_NO_MEMORY;
  389. break;
  390. }
  391. //
  392. // We use a direct mapping to find the immediately
  393. // the buffer headers. Unallocated pages are in a single entry
  394. // link list in that table. We must remove the locked entry
  395. // from the link list and save the buffer header address to the
  396. // new slot. The offset of the entry defines also the free
  397. // unlocked buffer in the buffer pool.
  398. // I have used this funny structure to minimize header
  399. // information for the unlocked virtual pages (you could have
  400. // a huge virtual buffer pool with a very small overhead in DLC).
  401. //
  402. FreeSlotIndex = (UINT)(((ULONG_PTR)pBufferPool->pUnlockedEntryList - (ULONG_PTR)pBufferPool->BufferHeaders) / sizeof(PVOID));
  403. pBuffer->Header.BufferState = BUF_READY;
  404. pBuffer->Header.pLocalVa = (PVOID)((PCHAR)pBufferPool->BaseOffset + FreeSlotIndex * MAX_DLC_BUFFER_SEGMENT);
  405. pBufferPool->pUnlockedEntryList = pBufferPool->pUnlockedEntryList->pNext;
  406. pBufferPool->BufferHeaders[FreeSlotIndex] = pBuffer;
  407. //
  408. // Lock memory always outside the spin locks on 0 level.
  409. //
  410. UNLOCK_BUFFER_POOL();
  411. RELEASE_DRIVER_LOCK();
  412. pBuffer->Header.pMdl = AllocateProbeAndLockMdl(pBuffer->Header.pLocalVa,
  413. MAX_DLC_BUFFER_SEGMENT
  414. );
  415. ACQUIRE_DRIVER_LOCK();
  416. LOCK_BUFFER_POOL();
  417. if (pBuffer->Header.pMdl) {
  418. pBuffer->Header.pGlobalVa = MmGetSystemAddressForMdl(pBuffer->Header.pMdl);
  419. pBuffer->Header.FreeSegments = MAX_DLC_BUFFER_SEGMENT / MIN_DLC_BUFFER_SEGMENT;
  420. status = AllocateBufferHeader(
  421. #if DBG
  422. pFileContext,
  423. #endif
  424. pBufferPool,
  425. pBuffer,
  426. MAX_DLC_BUFFER_SEGMENT / MIN_DLC_BUFFER_SEGMENT,
  427. 0, // logical index within the page
  428. 0 // page in the free page table
  429. );
  430. } else {
  431. MemoryLockFailed = TRUE;
  432. status = DLC_STATUS_MEMORY_LOCK_FAILED;
  433. #if DBG
  434. DbgPrint("DLC.BufferPoolExpand: AllocateProbeAndLockMdl(a=%x, l=%x) failed\n",
  435. pBuffer->Header.pLocalVa,
  436. MAX_DLC_BUFFER_SEGMENT
  437. );
  438. #endif
  439. }
  440. if (status != STATUS_SUCCESS) {
  441. //
  442. // It failed => free MDL (if non-null) and
  443. // restore the link list of available buffers
  444. //
  445. if (pBuffer->Header.pMdl != NULL) {
  446. UnlockAndFreeMdl(pBuffer->Header.pMdl);
  447. }
  448. pBufferPool->BufferHeaders[FreeSlotIndex] = pBufferPool->pUnlockedEntryList;
  449. pBufferPool->pUnlockedEntryList = (PDLC_BUFFER_HEADER)&(pBufferPool->BufferHeaders[FreeSlotIndex]);
  450. DEALLOCATE_PACKET_DLC_BUF(pBufferPool->hHeaderPool, pBuffer);
  451. break;
  452. }
  453. #if LLC_DBG
  454. CHECK_FREE_SEGMENT_COUNT(pBuffer->Header.pNextChild);
  455. #endif
  456. pBufferPool->FreeSpace += MAX_DLC_BUFFER_SEGMENT;
  457. pBufferPool->UncommittedSpace += MAX_DLC_BUFFER_SEGMENT;
  458. pBufferPool->BufferPoolSize += MAX_DLC_BUFFER_SEGMENT;
  459. pBufferPool->MissingSize -= MAX_DLC_BUFFER_SEGMENT;
  460. LlcInsertTailList(&pBufferPool->PageHeaders, pBuffer);
  461. }
  462. pBufferPool->MissingSize = 0;
  463. //
  464. // We will return success, if at least the minimal amount
  465. // memory was allocated. The initial pool size may be too
  466. // big for the current memory constraints set by the
  467. // operating system and actual available physical memory.
  468. //
  469. if (pBufferPool->UncommittedSpace < 0) {
  470. status = DLC_STATUS_NO_MEMORY;
  471. }
  472. }
  473. UNLOCK_BUFFER_POOL();
  474. return status;
  475. }
  476. VOID
  477. BufferPoolFreeExtraPages(
  478. #if DBG
  479. IN PDLC_FILE_CONTEXT pFileContext,
  480. #endif
  481. IN PDLC_BUFFER_POOL pBufferPool
  482. )
  483. /*++
  484. Routine Description:
  485. The function checks the maximum Thresholds and
  486. unlocks the extra pages and deallocates their buffer headers.
  487. Arguments:
  488. pBufferPool - handle of buffer pool data structure.
  489. Return Value:
  490. None.
  491. --*/
  492. {
  493. PDLC_BUFFER_HEADER pBuffer;
  494. KIRQL irql;
  495. PDLC_BUFFER_HEADER pNextBuffer;
  496. ASSUME_IRQL(DISPATCH_LEVEL);
  497. /*
  498. DbgPrint("MaxBufferSize: %x\n", pBufferPool->MaxBufferSize);
  499. DbgPrint("Uncommitted size: %x\n", pBufferPool->UncommittedSpace);
  500. DbgPrint("BufferPoolSize: %x\n", pBufferPool->BufferPoolSize);
  501. DbgPrint("FreeSpace : %x\n", pBufferPool->FreeSpace);
  502. */
  503. LOCK_BUFFER_POOL();
  504. //
  505. // Free the extra pages until we have enough free buffer space.
  506. //
  507. pBuffer = (PDLC_BUFFER_HEADER)pBufferPool->PageHeaders.Flink;
  508. while ((pBufferPool->UncommittedSpace > MAX_FREE_SIZE_THRESHOLD)
  509. && (pBuffer != (PVOID)&pBufferPool->PageHeaders)) {
  510. //
  511. // We may free (unlock) only those buffers given, that have
  512. // all buffers free.
  513. //
  514. if ((UINT)(pBuffer->Header.FreeSegments == (MAX_DLC_BUFFER_SEGMENT / MIN_DLC_BUFFER_SEGMENT))) {
  515. pNextBuffer = pBuffer->Header.pNextHeader;
  516. #if DBG
  517. DeallocateBuffer(pFileContext, pBufferPool, pBuffer);
  518. #else
  519. DeallocateBuffer(pBufferPool, pBuffer);
  520. #endif
  521. pBufferPool->FreeSpace -= MAX_DLC_BUFFER_SEGMENT;
  522. pBufferPool->UncommittedSpace -= MAX_DLC_BUFFER_SEGMENT;
  523. pBufferPool->BufferPoolSize -= MAX_DLC_BUFFER_SEGMENT;
  524. pBuffer = pNextBuffer;
  525. } else {
  526. pBuffer = pBuffer->Header.pNextHeader;
  527. }
  528. }
  529. UNLOCK_BUFFER_POOL();
  530. }
  531. VOID
  532. DeallocateBuffer(
  533. #if DBG
  534. IN PDLC_FILE_CONTEXT pFileContext,
  535. #endif
  536. IN PDLC_BUFFER_POOL pBufferPool,
  537. IN PDLC_BUFFER_HEADER pBuffer
  538. )
  539. /*++
  540. Routine Description:
  541. The routine unlinks all segments of a page from the free lists and
  542. deallocates the data structures.
  543. Arguments:
  544. pBufferPool - handle of buffer pool data structure.
  545. pBuffer - the deallocated buffer header
  546. Return Value:
  547. None
  548. --*/
  549. {
  550. UINT FreeSlotIndex;
  551. PDLC_BUFFER_HEADER pSegment, pNextSegment;
  552. //
  553. // First we unlink the segments from the free lists and
  554. // then free and unlock the data structs of segment.
  555. //
  556. for (pSegment = pBuffer->Header.pNextChild; pSegment != NULL; pSegment = pNextSegment) {
  557. pNextSegment = pSegment->FreeBuffer.pNextChild;
  558. //
  559. // Remove the buffer from the free lists (if it is there)
  560. //
  561. if (pSegment->FreeBuffer.BufferState == BUF_READY) {
  562. LlcRemoveEntryList(pSegment);
  563. }
  564. #if LLC_DBG
  565. else {
  566. //
  567. // This else can be possible only if we are
  568. // deleting the whole buffer pool (ref count=0)
  569. //
  570. if (pBufferPool->ReferenceCount != 0) {
  571. DbgPrint("Error: Invalid buffer state!");
  572. DbgBreakPoint();
  573. }
  574. pSegment->FreeBuffer.pNext = NULL;
  575. }
  576. #endif
  577. IoFreeMdl(pSegment->FreeBuffer.pMdl);
  578. DBG_INTERLOCKED_DECREMENT(AllocatedMdlCount);
  579. DEALLOCATE_PACKET_DLC_BUF(pBufferPool->hHeaderPool, pSegment);
  580. }
  581. //
  582. // Link the page to the free page list in buffer pool header
  583. //
  584. FreeSlotIndex = (UINT)(((ULONG_PTR)pBuffer->Header.pLocalVa - (ULONG_PTR)pBufferPool->BaseOffset) / MAX_DLC_BUFFER_SEGMENT);
  585. pBufferPool->BufferHeaders[FreeSlotIndex] = pBufferPool->pUnlockedEntryList;
  586. pBufferPool->pUnlockedEntryList = (PDLC_BUFFER_HEADER)&(pBufferPool->BufferHeaders[FreeSlotIndex]);
  587. UnlockAndFreeMdl(pBuffer->Header.pMdl);
  588. LlcRemoveEntryList(pBuffer);
  589. DEALLOCATE_PACKET_DLC_BUF(pBufferPool->hHeaderPool, pBuffer);
  590. }
  591. NTSTATUS
  592. AllocateBufferHeader(
  593. #if DBG
  594. IN PDLC_FILE_CONTEXT pFileContext,
  595. #endif
  596. IN PDLC_BUFFER_POOL pBufferPool,
  597. IN PDLC_BUFFER_HEADER pParent,
  598. IN UCHAR Size,
  599. IN UCHAR Index,
  600. IN UINT FreeListTableIndex
  601. )
  602. /*++
  603. Routine Description:
  604. The routine allocates and initializes a new buffer segment
  605. and links it to the given free segment list.
  606. Arguments:
  607. pBufferPool - handle of buffer pool data structure.
  608. pParent - the parent (page) node of this segemnt
  609. Size - size of this segment in 256 byte units
  610. Index - index of this segment in 256 byte units
  611. FreeListTableIndex - log2(Size), (ie. 256 bytes=>0, etc.)
  612. Return Value:
  613. Returns NTSTATUS
  614. Success - STATUS_SUCCESS
  615. Failure - DLC_STATUS_NO_MEMORY
  616. --*/
  617. {
  618. PDLC_BUFFER_HEADER pBuffer;
  619. ASSUME_IRQL(DISPATCH_LEVEL);
  620. if (!(pBuffer = ALLOCATE_PACKET_DLC_BUF(pBufferPool->hHeaderPool))) {
  621. return DLC_STATUS_NO_MEMORY;
  622. }
  623. pBuffer->FreeBuffer.pMdl = IoAllocateMdl((PUCHAR)pParent->Header.pLocalVa
  624. + (UINT)Index * MIN_DLC_BUFFER_SEGMENT,
  625. (UINT)Size * MIN_DLC_BUFFER_SEGMENT,
  626. FALSE, // not used (no IRP)
  627. FALSE, // we can't take this from user quota
  628. NULL
  629. );
  630. if (pBuffer->FreeBuffer.pMdl == NULL) {
  631. DEALLOCATE_PACKET_DLC_BUF(pBufferPool->hHeaderPool, pBuffer);
  632. return DLC_STATUS_NO_MEMORY;
  633. }
  634. DBG_INTERLOCKED_INCREMENT(AllocatedMdlCount);
  635. pBuffer->FreeBuffer.pNextChild = pParent->Header.pNextChild;
  636. pParent->Header.pNextChild = pBuffer;
  637. pBuffer->FreeBuffer.pParent = pParent;
  638. pBuffer->FreeBuffer.Size = Size;
  639. pBuffer->FreeBuffer.Index = Index;
  640. pBuffer->FreeBuffer.BufferState = BUF_READY;
  641. pBuffer->FreeBuffer.FreeListIndex = (UCHAR)FreeListTableIndex;
  642. //
  643. // Link the full page buffer to the first free list
  644. //
  645. LlcInsertHeadList(&(pBufferPool->FreeLists[FreeListTableIndex]), pBuffer);
  646. return STATUS_SUCCESS;
  647. }
  648. NTSTATUS
  649. BufferPoolAllocate(
  650. #if DBG
  651. IN PDLC_FILE_CONTEXT pFileContext,
  652. #endif
  653. IN PDLC_BUFFER_POOL pBufferPool,
  654. IN UINT BufferSize,
  655. IN UINT FrameHeaderSize,
  656. IN UINT UserDataSize,
  657. IN UINT FrameLength,
  658. IN UINT SegmentSizeIndex,
  659. IN OUT PDLC_BUFFER_HEADER *ppBufferHeader,
  660. OUT PUINT puiBufferSizeLeft
  661. )
  662. /*++
  663. Routine Description:
  664. Function allocates the requested buffer (locked and mapped) from the
  665. buffer pool and returns its MDL and descriptor table of user segments.
  666. The returned buffer is actually the minimal combination of some segments
  667. (256, 512, 1024, 2048, 4096).
  668. There is a header in each buffer segment. The size of the frame
  669. header and the user data added to all frames are defined by the caller.
  670. The allocated segments will be linked in three level:
  671. - Segment headers will be linked in the reserved list to
  672. be checked when the application program releases the buffers
  673. back to pool. The actual segments cannot be used, because
  674. they are located in unsafe user memory.
  675. - Segments are linked (from smaller to bigger) for application program,
  676. this link list is used nowhere in the driver (because it is in ...)
  677. - MDLs of the same buffer list are linked for the driver
  678. The link lists goes from smaller segment to bigger, because
  679. the last segment should be the biggest one in a transmit call
  680. (it actually works very nice with 2 or 4 token ring frames).
  681. DON'T TOUCH the calculation of the segment size (includes operations
  682. with BufferSize, ->Cont.DataLength and FirstHeaderSize), the logic is
  683. very complex. The current code has been tested by hand using some
  684. test values, and it seems to work (BufferSize = 0, 1, 0xF3,
  685. FirstHeader = 0,2).
  686. Arguments:
  687. pBufferPool - handle of buffer pool data structure.
  688. BufferSize - the size of the actual data in the requested buffers.
  689. This must really be the actual data. Nobody can know
  690. the size of all segment headers beforehand. The buffer
  691. size must include the frame header size added to the
  692. first buffer in the list!
  693. FrameHeaderSize - the space reserved for the frame header depends on
  694. buffer format (OS/2 or DOS) and if the data is read
  695. contiguously or not. The buffer manager reserves four
  696. bytes from the beginning of the first segment in frame
  697. to link this frame to next frames.
  698. UserDataSize - buffer area reserved for user data (nobody uses this)
  699. FrameLength - the total frame length (may not be the same as
  700. BufferSize, because the LAN and DLC headers may be
  701. saved into the header.
  702. SegmentSizeIndex - the client may ask a number segments having a fixed
  703. size (256, 512, ... 4096).
  704. ppBufferHeader - parameter returns the arrays of the user buffer
  705. segments. The array is allocated in the end of this
  706. buffer. This may include a pointer to a buffer pool,
  707. that is already allocated. The old buffer list will
  708. be linked behind the new buffers.
  709. puiBufferSizeLeft - returns the size of buffer space, that is not yet
  710. allocated. The client may extend the buffer pool
  711. and then continue the allocation of the buffers.
  712. Otherwise you could not allocate more buffers than
  713. defined by MinFreeSizeThreshold.
  714. Return Value:
  715. NTSTATUS
  716. STATUS_SUCCESS
  717. DLC_STATUS_NO_MEMORY - no available memory in the non paged pool
  718. --*/
  719. {
  720. INT i, j, k; // loop indexes (three level loop)
  721. INT LastIndex; // index of smallest allowed segment size.
  722. INT LastAvailable; // Index of free list having biggest segments
  723. UINT SegmentSize; // current segment size
  724. PDLC_BUFFER_HEADER pPrev;
  725. PMDL pPrevMdl;
  726. PDLC_BUFFER_HEADER pNew;
  727. PFIRST_DLC_SEGMENT pDlcBuffer, pLastDlcBuffer;
  728. NTSTATUS Status = STATUS_SUCCESS;
  729. KIRQL irql;
  730. USHORT SavedDataLength;
  731. static USHORT SegmentSizes[DLC_BUFFER_SEGMENTS] = {
  732. #if defined(ALPHA)
  733. 8192,
  734. #endif
  735. 4096,
  736. 2048,
  737. 1024,
  738. 512,
  739. 256
  740. };
  741. ASSUME_IRQL(DISPATCH_LEVEL);
  742. //
  743. // Link the old buffers behind the new ones.
  744. // This is really sick: BufferGet is calling this second (or more)
  745. // time after it has expanded the buffer pool for the new retry,
  746. // we must search the last buffer header, because the extra
  747. // buffer space is removed from it.
  748. //
  749. pPrev = *ppBufferHeader;
  750. if (pPrev != NULL) {
  751. for (pNew = pPrev;
  752. pNew->FrameBuffer.pNextSegment != NULL;
  753. pNew = pNew->FrameBuffer.pNextSegment) {
  754. ; // NOP
  755. }
  756. pLastDlcBuffer = (PFIRST_DLC_SEGMENT)
  757. (
  758. (PUCHAR)pNew->FreeBuffer.pParent->Header.pGlobalVa
  759. + (UINT)pNew->FreeBuffer.Index * MIN_DLC_BUFFER_SEGMENT
  760. );
  761. }
  762. //
  763. // the first frame size has been added to the total length
  764. // (excluding the default header), but we must
  765. // exclude the default buffer header.
  766. //
  767. if (FrameHeaderSize > sizeof(NEXT_DLC_SEGMENT)) {
  768. FrameHeaderSize -= sizeof(NEXT_DLC_SEGMENT);
  769. } else {
  770. FrameHeaderSize = 0;
  771. }
  772. //
  773. // The frame header must be included in the total buffer space
  774. // just as any other stuff. We must add the maximum extra size
  775. // to get all stuff to fit into buffers.
  776. //
  777. BufferSize += MIN_DLC_BUFFER_SEGMENT - 1 + FrameHeaderSize;
  778. //
  779. // Initialize the index variables for the loop
  780. //
  781. if (SegmentSizeIndex == -1) {
  782. i = 0;
  783. LastIndex = DLC_BUFFER_SEGMENTS - 1;
  784. SegmentSize = MAX_DLC_BUFFER_SEGMENT;
  785. } else {
  786. i = SegmentSizeIndex;
  787. LastIndex = SegmentSizeIndex;
  788. SegmentSize = SegmentSizes[SegmentSizeIndex];
  789. }
  790. LastAvailable = 0;
  791. LOCK_BUFFER_POOL();
  792. //
  793. // Loop until we have found enough buffers for
  794. // the given buffer space (any kind, but as few as possible)
  795. // or for the given number of requested buffers.
  796. // Initialize each new buffer. The frame header is a special case.
  797. // We go from bigger segments to smaller ones. The last (and smallest)
  798. // will be initialized as a frame header (if needed).
  799. //
  800. for (; (i <= LastIndex) && BufferSize; i++) {
  801. while (((SegmentSize - sizeof(NEXT_DLC_SEGMENT) - UserDataSize) < BufferSize) || (i == LastIndex)) {
  802. //
  803. // Check if there are any buffers having the optimal size
  804. //
  805. if (IsListEmpty(&pBufferPool->FreeLists[i])) {
  806. //
  807. // Split a bigger segment to smallers. Link the
  808. // extra segments to the free lists and return
  809. // after that to the current size level.
  810. //
  811. for (j = i; j > LastAvailable; ) {
  812. j--;
  813. if (!IsListEmpty(&pBufferPool->FreeLists[j])) {
  814. //
  815. // Take the first available segment header in
  816. // the free list
  817. //
  818. pNew = LlcRemoveHeadList(&pBufferPool->FreeLists[j]);
  819. //
  820. // Split segments until we reach the desired level.
  821. // We leave every (empty) level between a new segment
  822. // header (including the current level (= i).
  823. //
  824. k = j;
  825. do {
  826. k++;
  827. //
  828. // We must also split the orginal buffer header
  829. // and its MDL.
  830. //
  831. pNew->FreeBuffer.Size /= 2;
  832. pNew->FreeBuffer.FreeListIndex++;
  833. //
  834. // We create the new buffer header for
  835. // the upper half of the old buffer segment.
  836. //
  837. Status = AllocateBufferHeader(
  838. #if DBG
  839. pFileContext,
  840. #endif
  841. pBufferPool,
  842. pNew->FreeBuffer.pParent,
  843. pNew->FreeBuffer.Size,
  844. (UCHAR)(pNew->FreeBuffer.Index +
  845. pNew->FreeBuffer.Size),
  846. (UINT)k
  847. );
  848. //
  849. // We cannot stop on error, but we try to
  850. // allocate several smaller segments before
  851. // we will give up.
  852. //
  853. if (Status != STATUS_SUCCESS) {
  854. //
  855. // We couldn't split the buffer, return
  856. // the current buffer back to its slot.
  857. //
  858. pNew->FreeBuffer.Size *= 2;
  859. pNew->FreeBuffer.FreeListIndex--;
  860. LlcInsertHeadList(&pBufferPool->FreeLists[k-1],
  861. pNew
  862. );
  863. break;
  864. }
  865. } while (k != i);
  866. break;
  867. }
  868. }
  869. //
  870. // Did we succeed to split the bigger segments
  871. // to smaller ones?
  872. //
  873. if (IsListEmpty(&pBufferPool->FreeLists[i])) {
  874. //
  875. // We have run out of bigger segments, let's try to
  876. // use the smaller ones instead. Indicate, that
  877. // there exist no bigger segments than current one.
  878. // THIS BREAK STARTS A NEW LOOP WITH A SMALLER
  879. // SEGMENT SIZE.
  880. //
  881. LastAvailable = i;
  882. break;
  883. }
  884. } else {
  885. pNew = LlcRemoveHeadList(&pBufferPool->FreeLists[i]);
  886. }
  887. pDlcBuffer = (PFIRST_DLC_SEGMENT)
  888. ((PUCHAR)pNew->FreeBuffer.pParent->Header.pGlobalVa
  889. + (UINT)pNew->FreeBuffer.Index * MIN_DLC_BUFFER_SEGMENT);
  890. //
  891. // The buffers must be chained together on three level:
  892. // - using kernel Buffer headers (for driver)
  893. // - by user pointer (for apps)
  894. // - MDLs (for NDIS)
  895. //
  896. if (pPrev == NULL) {
  897. //
  898. // Frame header - initialize the list
  899. // HACK-HACK!!!!
  900. //
  901. pPrevMdl = NULL;
  902. pDlcBuffer->Cont.pNext = NULL;
  903. pLastDlcBuffer = pDlcBuffer;
  904. } else {
  905. pPrevMdl = pPrev->FrameBuffer.pMdl;
  906. pDlcBuffer->Cont.pNext = (PNEXT_DLC_SEGMENT)
  907. ((PUCHAR)pPrev->FrameBuffer.pParent->Header.pLocalVa
  908. + (UINT)pPrev->FrameBuffer.Index * MIN_DLC_BUFFER_SEGMENT);
  909. }
  910. pBufferPool->FreeSpace -= SegmentSize;
  911. pBufferPool->UncommittedSpace -= SegmentSize;
  912. pNew->FrameBuffer.pNextFrame = NULL;
  913. pNew->FrameBuffer.BufferState = BUF_USER;
  914. pNew->FrameBuffer.pNextSegment = pPrev;
  915. pNew->FrameBuffer.pParent->Header.FreeSegments -= pNew->FreeBuffer.Size;
  916. #if LLC_DBG
  917. if ((UINT)(MIN_DLC_BUFFER_SEGMENT * pNew->FreeBuffer.Size) != SegmentSize) {
  918. DbgPrint("Invalid buffer size.\n");
  919. DbgBreakPoint();
  920. }
  921. CHECK_FREE_SEGMENT_COUNT(pNew);
  922. #endif
  923. pPrev = pNew;
  924. pDlcBuffer->Cont.UserOffset = sizeof(NEXT_DLC_SEGMENT);
  925. pDlcBuffer->Cont.UserLength = (USHORT)UserDataSize;
  926. pDlcBuffer->Cont.FrameLength = (USHORT)FrameLength;
  927. // Save this length in a local var since pDlcBuffer->Cont.DataLength can be changed by user
  928. // but this is used later on also.
  929. SavedDataLength = (USHORT)(SegmentSize - sizeof(NEXT_DLC_SEGMENT) - UserDataSize);
  930. pDlcBuffer->Cont.DataLength = SavedDataLength;
  931. //
  932. // Check if we have done it!
  933. // Remember, that the buffer size have been round up/over to
  934. // the next 256 bytes even adderss => we never go negative.
  935. //
  936. // 127041: User can change this value between this and the last instruction
  937. //BufferSize -= pDlcBuffer->Cont.DataLength;
  938. BufferSize -= SavedDataLength;
  939. if (BufferSize < MIN_DLC_BUFFER_SEGMENT) {
  940. pDlcBuffer->Cont.UserOffset += (USHORT)FrameHeaderSize;
  941. pDlcBuffer->Cont.DataLength -= (USHORT)FrameHeaderSize;
  942. SavedDataLength -= (USHORT)FrameHeaderSize;
  943. //
  944. // The data must be read to the beginning of the
  945. // buffer chain (eg. because of NdisTransferData).
  946. // => the first buffer must be full and the last
  947. // one must always be odd. The extra length
  948. // in the partial MDL does not matter.
  949. //
  950. BufferSize -= MIN_DLC_BUFFER_SEGMENT - 1;
  951. pLastDlcBuffer->Cont.DataLength += (USHORT)BufferSize;
  952. BuildMappedPartialMdl(
  953. pNew->FrameBuffer.pParent->Header.pMdl,
  954. pNew->FrameBuffer.pMdl,
  955. pNew->FrameBuffer.pParent->Header.pLocalVa
  956. + pNew->FrameBuffer.Index * MIN_DLC_BUFFER_SEGMENT
  957. + FrameHeaderSize
  958. + UserDataSize
  959. + sizeof(NEXT_DLC_SEGMENT),
  960. SavedDataLength
  961. );
  962. pNew->FrameBuffer.pMdl->Next = pPrevMdl;
  963. //
  964. // The buffer headers must be procted (the flag prevents
  965. // user to free them back buffer pool before we have
  966. // indicated the chained receive frames to him).
  967. // The linkage of frame headers will crash, if
  968. // the header buffer is released before the frame
  969. // was indicated!
  970. //
  971. pNew->FrameBuffer.BufferState = BUF_RCV_PENDING;
  972. BufferSize = 0;
  973. break;
  974. } else {
  975. //
  976. // MDL must exclude the buffer header from the actual data.
  977. //
  978. BuildMappedPartialMdl(
  979. pNew->FrameBuffer.pParent->Header.pMdl,
  980. pNew->FrameBuffer.pMdl,
  981. pNew->FrameBuffer.pParent->Header.pLocalVa
  982. + pNew->FrameBuffer.Index * MIN_DLC_BUFFER_SEGMENT
  983. + UserDataSize
  984. + sizeof(NEXT_DLC_SEGMENT),
  985. pDlcBuffer->Cont.DataLength
  986. );
  987. pNew->FrameBuffer.pMdl->Next = pPrevMdl;
  988. }
  989. }
  990. SegmentSize /= 2;
  991. }
  992. if (BufferSize == 0) {
  993. Status = STATUS_SUCCESS;
  994. } else {
  995. BufferSize -= (MIN_DLC_BUFFER_SEGMENT - 1);
  996. //
  997. // The client, that is not running in DPC level may extend
  998. // the buffer pool, if there is still available space left
  999. // in the buffer pool
  1000. //
  1001. if (pBufferPool->MaxBufferSize > pBufferPool->BufferPoolSize) {
  1002. //
  1003. // We can expand the buffer pool, sometimes we must
  1004. // allocate new bigger segments, if the available
  1005. // smaller segments cannot satisfy the request.
  1006. //
  1007. if ((LONG)BufferSize > pBufferPool->MissingSize) {
  1008. pBufferPool->MissingSize = (LONG)BufferSize;
  1009. }
  1010. Status = DLC_STATUS_EXPAND_BUFFER_POOL;
  1011. } else {
  1012. Status = DLC_STATUS_INADEQUATE_BUFFERS;
  1013. }
  1014. }
  1015. UNLOCK_BUFFER_POOL();
  1016. *ppBufferHeader = pPrev;
  1017. *puiBufferSizeLeft = BufferSize;
  1018. return Status;
  1019. }
  1020. NTSTATUS
  1021. BufferPoolDeallocate(
  1022. IN PDLC_BUFFER_POOL pBufferPool,
  1023. IN UINT BufferCount,
  1024. IN PLLC_TRANSMIT_DESCRIPTOR pBuffers
  1025. )
  1026. /*++
  1027. Routine Description:
  1028. Function deallocates the requested buffers. It first checks
  1029. the user buffer in the page table and then adds its header to
  1030. the free list.
  1031. Arguments:
  1032. pBufferPool - handle of buffer pool data structure.
  1033. BufferCount - number of user buffers to be released
  1034. pBuffers - array of the user buffers
  1035. Return Value:
  1036. NTSTATUS
  1037. --*/
  1038. {
  1039. PDLC_BUFFER_HEADER pBuffer;
  1040. UINT i;
  1041. NTSTATUS status = STATUS_SUCCESS;
  1042. KIRQL irql;
  1043. ASSUME_IRQL(PASSIVE_LEVEL);
  1044. LOCK_BUFFER_POOL();
  1045. //
  1046. // Return all buffers
  1047. //
  1048. for (i = 0; i < BufferCount; i++) {
  1049. pBuffer = GetBufferHeader(pBufferPool, pBuffers[i].pBuffer);
  1050. if (pBuffer && (pBuffer->FreeBuffer.BufferState == BUF_USER)) {
  1051. register ULONG bufsize;
  1052. //
  1053. // Set the buffer state READY and restore the modified
  1054. // size and offset fields in MDL
  1055. //
  1056. pBuffer->FreeBuffer.BufferState = BUF_READY;
  1057. pBuffer->FreeBuffer.pParent->Header.FreeSegments += pBuffer->FreeBuffer.Size;
  1058. #if LLC_DBG
  1059. if (pBuffer->FreeBuffer.pParent->Header.FreeSegments > 16) {
  1060. DbgPrint("Invalid buffer size.\n");
  1061. DbgBreakPoint();
  1062. }
  1063. CHECK_FREE_SEGMENT_COUNT(pBuffer);
  1064. #endif
  1065. //
  1066. // a microscopic performance improvement: the compiler (x86 at
  1067. // least) generates the sequence of instructions to work out
  1068. // the buffer size (number of blocks * block size) TWICE,
  1069. // presumably because it can't assume that the structure hasn't
  1070. // changed between the 2 accesses? Anyhow, Nature abhors a
  1071. // vacuum, which is why my house is such a mess
  1072. //
  1073. bufsize = pBuffer->FreeBuffer.Size * MIN_DLC_BUFFER_SEGMENT;
  1074. pBufferPool->FreeSpace += bufsize;
  1075. pBufferPool->UncommittedSpace += bufsize;
  1076. LlcInsertTailList(&pBufferPool->FreeLists[pBuffer->FreeBuffer.FreeListIndex], pBuffer);
  1077. } else {
  1078. //
  1079. // At least one of the released buffers is invalid,
  1080. // may be already released, or it may not exist in
  1081. // the buffer pool at all
  1082. //
  1083. status = DLC_STATUS_INVALID_BUFFER_ADDRESS;
  1084. }
  1085. }
  1086. UNLOCK_BUFFER_POOL();
  1087. return status;
  1088. }
  1089. VOID
  1090. BufferPoolDeallocateList(
  1091. IN PDLC_BUFFER_POOL pBufferPool,
  1092. IN PDLC_BUFFER_HEADER pBufferList
  1093. )
  1094. /*++
  1095. Routine Description:
  1096. Function deallocates the requested buffer list.
  1097. The buffer list may be circular or null terminated.
  1098. Arguments:
  1099. pBufferPool - handle of buffer pool data structure.
  1100. pBufferList - link list of user
  1101. Return Value:
  1102. None
  1103. --*/
  1104. {
  1105. PDLC_BUFFER_HEADER pBuffer, pNextBuffer, pFrameBuffer, pNextFrameBuffer;
  1106. KIRQL irql;
  1107. if (pBufferList == NULL) {
  1108. return;
  1109. }
  1110. LOCK_BUFFER_POOL();
  1111. //
  1112. // Return all buffers to the free lists.
  1113. // The segments are always linked to a null terminated link list.
  1114. // The frames are linked either circular or null terminated
  1115. // link list!
  1116. //
  1117. // Note: both next segment and frame pointers are overlayed with
  1118. // the pPrev and pNext pointers of the double linked free lists.
  1119. //
  1120. pNextFrameBuffer = pBufferList;
  1121. do {
  1122. pBuffer = pFrameBuffer = pNextFrameBuffer;
  1123. pNextFrameBuffer = pFrameBuffer->FrameBuffer.pNextFrame;
  1124. do {
  1125. pNextBuffer = pBuffer->FrameBuffer.pNextSegment;
  1126. #if LLC_DBG
  1127. if (pBuffer->FreeBuffer.BufferState != BUF_USER
  1128. && pBuffer->FreeBuffer.BufferState != BUF_RCV_PENDING) {
  1129. DbgBreakPoint();
  1130. }
  1131. if (pBuffer->FreeBuffer.pParent->Header.FreeSegments > 16) {
  1132. DbgPrint("Invalid buffer size.\n");
  1133. DbgBreakPoint();
  1134. }
  1135. CHECK_FREE_SEGMENT_COUNT(pBuffer);
  1136. #endif
  1137. //
  1138. // Set the buffer state READY and restore the modified
  1139. // size and offset fields in MDL
  1140. //
  1141. pBuffer->FreeBuffer.BufferState = BUF_READY;
  1142. pBuffer->FreeBuffer.pParent->Header.FreeSegments += pBuffer->FreeBuffer.Size;
  1143. pBufferPool->FreeSpace += pBuffer->FreeBuffer.Size * MIN_DLC_BUFFER_SEGMENT;
  1144. pBufferPool->UncommittedSpace += pBuffer->FreeBuffer.Size * MIN_DLC_BUFFER_SEGMENT;
  1145. LlcInsertTailList(&pBufferPool->FreeLists[pBuffer->FreeBuffer.FreeListIndex],
  1146. pBuffer
  1147. );
  1148. } while ( pBuffer = pNextBuffer );
  1149. } while (pNextFrameBuffer && (pNextFrameBuffer != pBufferList));
  1150. UNLOCK_BUFFER_POOL();
  1151. }
  1152. NTSTATUS
  1153. BufferPoolBuildXmitBuffers(
  1154. IN PDLC_BUFFER_POOL pBufferPool,
  1155. IN UINT BufferCount,
  1156. IN PLLC_TRANSMIT_DESCRIPTOR pBuffers,
  1157. IN OUT PDLC_PACKET pPacket
  1158. )
  1159. /*++
  1160. Routine Description:
  1161. Function build a MDL and buffer header list for a frame defined by
  1162. a scatter/gather array. All buffers outside the buffer pool
  1163. are probed and locked. All MDLs (the locked and ones for buffer pool)
  1164. are chained together. The buffer pool headers are also chained.
  1165. If any errors have been found the buffers are released using the
  1166. reverse function (BufferPoolFreeXmitBuffers).
  1167. THIS FUNCTION HAS A VERY SPECIAL SPIN LOCKING DESIGN:
  1168. First we free the global spin lock (and lower the IRQ level to the lowest),
  1169. Then, if the transmit is made from DLC buffers, we lock the
  1170. spin lock again using NdisSpinLock function, that saves and restores
  1171. the IRQL level, when it acquires and releases the spin lock.
  1172. This all is done to minimize the spin locking overhead when we are
  1173. locking transmit buffers, that are usually DLC buffers or normal
  1174. user memory but not both.
  1175. Arguments:
  1176. pBufferPool - handle of buffer pool data structure, THIS MAY BE NULL!!!!
  1177. BufferCount - number of user buffers in the frame
  1178. pBuffers - array of the user buffers of the frame
  1179. pPacket - generic DLC packet used in transmit
  1180. Return Value:
  1181. NTSTATUS
  1182. --*/
  1183. {
  1184. PDLC_BUFFER_HEADER pBuffer, pPrevBuffer = NULL;
  1185. PMDL pMdl, pPrevMdl = NULL;
  1186. INT i;
  1187. NTSTATUS Status = STATUS_SUCCESS;
  1188. BOOLEAN FirstBuffer = TRUE;
  1189. UINT InfoFieldLength = 0;
  1190. BOOLEAN BufferPoolIsLocked = FALSE; // very neat optimization!
  1191. KIRQL irql;
  1192. ASSUME_IRQL(PASSIVE_LEVEL);
  1193. //
  1194. // The client may allocate buffer headers without any buffers!
  1195. //
  1196. if (BufferCount != 0) {
  1197. //
  1198. // walk the buffers in a reverse order to build the
  1199. // list in a convinient way.
  1200. //
  1201. for (i = BufferCount - 1; i >= 0; i--) {
  1202. if (pBuffers[i].cbBuffer == 0) {
  1203. continue;
  1204. }
  1205. InfoFieldLength += pBuffers[i].cbBuffer;
  1206. //
  1207. // Check first if the given address is in the same area as the
  1208. // buffer pool
  1209. //
  1210. if (pBufferPool != NULL
  1211. && (ULONG_PTR)pBuffers[i].pBuffer >= (ULONG_PTR)pBufferPool->BaseOffset
  1212. && (ULONG_PTR)pBuffers[i].pBuffer < (ULONG_PTR)pBufferPool->MaxOffset) {
  1213. //
  1214. // Usually all transmit buffers are either in the buffer
  1215. // pool or they are elsewhere in the user memory.
  1216. // This boolean flag prevents us to toggle the buffer
  1217. // pool spinlock for each transmit buffer segment.
  1218. // (and nt spinlock is slower than its critical section!!!)
  1219. //
  1220. if (BufferPoolIsLocked == FALSE) {
  1221. LOCK_BUFFER_POOL();
  1222. BufferPoolIsLocked = TRUE;
  1223. }
  1224. }
  1225. //
  1226. // The previous check does not yet garantee, that the given buffer
  1227. // if really a buffer pool segment, but the buffer pool is
  1228. // is now unlocked if it was aboslutely outside of the buffer pool,
  1229. // GetBufferHeader- function requires, that the buffer pool
  1230. // is locked, when it is called!
  1231. //
  1232. if (BufferPoolIsLocked
  1233. && (pBuffer = GetBufferHeader(pBufferPool, pBuffers[i].pBuffer)) != NULL) {
  1234. //
  1235. // The provided buffer must be inside the allocated
  1236. // buffer, otherwise the user has corrupted its buffers.
  1237. // user offset within buffer + user length <= buffer
  1238. // length
  1239. // The buffer must be also be owned by the user
  1240. //
  1241. if (((ULONG_PTR)pBuffers[i].pBuffer & (MIN_DLC_BUFFER_SEGMENT - 1))
  1242. + (ULONG)pBuffers[i].cbBuffer
  1243. > (ULONG)(pBuffer->FreeBuffer.Size * MIN_DLC_BUFFER_SEGMENT)
  1244. || (pBuffer->FrameBuffer.BufferState & BUF_USER) == 0) {
  1245. Status = DLC_STATUS_BUFFER_SIZE_EXCEEDED;
  1246. break;
  1247. }
  1248. //
  1249. // The same DLC buffer may be referenced several times.
  1250. // Create a partial MDL for it and add the reference
  1251. // counter.
  1252. //
  1253. if (pBuffer->FrameBuffer.BufferState & BUF_LOCKED) {
  1254. pMdl = IoAllocateMdl(pBuffers[i].pBuffer,
  1255. pBuffers[i].cbBuffer,
  1256. FALSE, // not used (no IRP)
  1257. FALSE, // can't charge from quota now
  1258. NULL // Do not link it to IRPs
  1259. );
  1260. if (pMdl == NULL) {
  1261. Status = DLC_STATUS_NO_MEMORY;
  1262. break;
  1263. }
  1264. DBG_INTERLOCKED_INCREMENT(AllocatedMdlCount);
  1265. BuildMappedPartialMdl(pBuffer->FrameBuffer.pParent->Header.pMdl,
  1266. pMdl,
  1267. pBuffers[i].pBuffer,
  1268. pBuffers[i].cbBuffer
  1269. );
  1270. pBuffer->FrameBuffer.ReferenceCount++;
  1271. if (pBuffers[i].boolFreeBuffer) {
  1272. pBuffer->FrameBuffer.BufferState |= DEALLOCATE_AFTER_USE;
  1273. }
  1274. } else {
  1275. //
  1276. // Modify the MDL for this request, the length must
  1277. // not be bigger than the buffer length and the
  1278. // offset must be within the first 255 bytes of
  1279. // the buffer. Build also the buffer header list
  1280. // (i don't know why?)
  1281. //
  1282. pMdl = pBuffer->FrameBuffer.pMdl;
  1283. if (
  1284. ((UINT)(ULONG_PTR)pBuffers[i].pBuffer & (MIN_DLC_BUFFER_SEGMENT - 1))
  1285. + pBuffers[i].cbBuffer
  1286. > (UINT)pBuffer->FrameBuffer.Size * MIN_DLC_BUFFER_SEGMENT) {
  1287. Status = DLC_STATUS_INVALID_BUFFER_LENGTH;
  1288. break;
  1289. }
  1290. pBuffer->FrameBuffer.pNextSegment = pPrevBuffer;
  1291. pBuffer->FrameBuffer.BufferState |= BUF_LOCKED;
  1292. pBuffer->FrameBuffer.ReferenceCount = 1;
  1293. if (pBuffers[i].boolFreeBuffer) {
  1294. pBuffer->FrameBuffer.BufferState |= DEALLOCATE_AFTER_USE;
  1295. }
  1296. pPrevBuffer = pBuffer;
  1297. //
  1298. // DLC applications may change the user length or
  1299. // buffer length of the frames given to them =>
  1300. // we must reinitialize global buffer and its length
  1301. //
  1302. BuildMappedPartialMdl(pBuffer->FrameBuffer.pParent->Header.pMdl,
  1303. pMdl,
  1304. pBuffers[i].pBuffer,
  1305. pBuffers[i].cbBuffer
  1306. );
  1307. }
  1308. } else {
  1309. if (BufferPoolIsLocked == TRUE) {
  1310. UNLOCK_BUFFER_POOL();
  1311. BufferPoolIsLocked = FALSE;
  1312. }
  1313. //
  1314. // Setup the exception handler around the memory manager
  1315. // calls and clean up any extra data if this fails.
  1316. //
  1317. pMdl = AllocateProbeAndLockMdl(pBuffers[i].pBuffer, pBuffers[i].cbBuffer);
  1318. if (pMdl == NULL) {
  1319. Status = DLC_STATUS_MEMORY_LOCK_FAILED;
  1320. #if DBG
  1321. DbgPrint("DLC.BufferPoolBuildXmitBuffers: AllocateProbeAndLockMdl(a=%x, l=%x) failed\n",
  1322. pBuffers[i].pBuffer,
  1323. pBuffers[i].cbBuffer
  1324. );
  1325. #endif
  1326. break;
  1327. }
  1328. #if LLC_DBG
  1329. cLockedXmitBuffers++;
  1330. #endif
  1331. }
  1332. //
  1333. // Chain all MDLs together
  1334. //
  1335. pMdl->Next = pPrevMdl;
  1336. pPrevMdl = pMdl;
  1337. }
  1338. }
  1339. if (BufferPoolIsLocked == TRUE) {
  1340. UNLOCK_BUFFER_POOL();
  1341. }
  1342. pPacket->Node.pNextSegment = pPrevBuffer;
  1343. pPacket->Node.pMdl = pPrevMdl;
  1344. pPacket->Node.LlcPacket.InformationLength = (USHORT)InfoFieldLength;
  1345. if (Status != STATUS_SUCCESS) {
  1346. //
  1347. // Free all allocated buffer (but the last one because there
  1348. // was an error with it)
  1349. //
  1350. BufferPoolFreeXmitBuffers(pBufferPool, pPacket);
  1351. }
  1352. return Status;
  1353. }
  1354. VOID
  1355. BufferPoolFreeXmitBuffers(
  1356. IN PDLC_BUFFER_POOL pBufferPool,
  1357. IN PDLC_PACKET pXmitNode
  1358. )
  1359. /*++
  1360. Routine Description:
  1361. Function unlocks the xmit buffers that are not in the buffer pool.
  1362. The caller must use DeallocateBufferPool routine to
  1363. and deallocates and the buffers are returned back to the pool.
  1364. The function has to separate the MDLs of user buffers and
  1365. buffer pool MDLs.
  1366. Arguments:
  1367. pBufferPool - handle of buffer pool data structure.
  1368. pXmitNode - pointer to a structure, that includes the buffer header list,
  1369. MDL chain or it chains serveral transmits nodes and IRP together.
  1370. Return Value:
  1371. None
  1372. --*/
  1373. {
  1374. PDLC_BUFFER_HEADER pBuffer;
  1375. PDLC_BUFFER_HEADER pOtherBuffer = NULL;
  1376. PDLC_BUFFER_HEADER pNextBuffer = NULL;
  1377. PMDL pMdl, pNextMdl;
  1378. KIRQL irql;
  1379. #if LLC_DBG
  1380. BOOLEAN FrameCounted = FALSE;
  1381. #endif
  1382. //
  1383. // Free all DLC buffers and MDLs linked in the transmit node.
  1384. // MDL list may be larger than the buffer header list.
  1385. //
  1386. if (pXmitNode != NULL) {
  1387. if (pBufferPool != NULL) {
  1388. LOCK_BUFFER_POOL();
  1389. }
  1390. pBuffer = pXmitNode->Node.pNextSegment;
  1391. for (pMdl = pXmitNode->Node.pMdl; pMdl != NULL; pMdl = pNextMdl) {
  1392. pNextMdl = pMdl->Next;
  1393. pMdl->Next = NULL;
  1394. //
  1395. // Unlock only those MDLs, that are outside the buffer pool.
  1396. //
  1397. if ((pBuffer == NULL || pBuffer->FrameBuffer.pMdl != pMdl)
  1398. && (pOtherBuffer = GetBufferHeader(pBufferPool, MmGetMdlVirtualAddress(pMdl))) == NULL) {
  1399. #if LLC_DBG
  1400. cUnlockedXmitBuffers++;
  1401. #endif
  1402. UnlockAndFreeMdl(pMdl);
  1403. } else {
  1404. //
  1405. // This pointer can be NULL only if the first condition
  1406. // if the previous 'if statement' was true => this cannot
  1407. // be an orginal buffer header.
  1408. //
  1409. if (pOtherBuffer != NULL) {
  1410. //
  1411. // This is not the first reference of the buffer pool
  1412. // segment, but a partial MDL created by a new
  1413. // reference to a buffer segment already in use.
  1414. // Free the paritial MDL and setup the buffer
  1415. // pointer for the next loop.
  1416. //
  1417. pNextBuffer = pBuffer;
  1418. pBuffer = pOtherBuffer;
  1419. pOtherBuffer = NULL;
  1420. IoFreeMdl(pMdl);
  1421. DBG_INTERLOCKED_DECREMENT(AllocatedMdlCount);
  1422. } else if (pBuffer != NULL) {
  1423. //
  1424. // This is the orginal refence of the buffer pool
  1425. // segment, we may advance also in the buffer header
  1426. // link list.
  1427. //
  1428. pNextBuffer = pBuffer->FrameBuffer.pNextSegment;
  1429. }
  1430. //
  1431. // The same DLC buffer may be referenced several times.
  1432. // Decrement the reference counter and free the
  1433. // list if this was the last released reference.
  1434. //
  1435. pBuffer->FrameBuffer.ReferenceCount--;
  1436. if (pBuffer->FrameBuffer.ReferenceCount == 0) {
  1437. if (pBuffer->FrameBuffer.BufferState & DEALLOCATE_AFTER_USE) {
  1438. //
  1439. // Set the buffer state READY and restore the modified
  1440. // size and offset fields in MDL
  1441. //
  1442. pBuffer->FreeBuffer.BufferState = BUF_READY;
  1443. pBuffer->FreeBuffer.pParent->Header.FreeSegments += pBuffer->FreeBuffer.Size;
  1444. #if LLC_DBG
  1445. if (pBuffer->FreeBuffer.pParent->Header.FreeSegments > 16) {
  1446. DbgPrint("Invalid buffer size.\n");
  1447. DbgBreakPoint();
  1448. }
  1449. CHECK_FREE_SEGMENT_COUNT(pBuffer);
  1450. #endif
  1451. pBufferPool->FreeSpace += (UINT)pBuffer->FreeBuffer.Size * MIN_DLC_BUFFER_SEGMENT;
  1452. pBufferPool->UncommittedSpace += (UINT)pBuffer->FreeBuffer.Size * MIN_DLC_BUFFER_SEGMENT;
  1453. LlcInsertTailList(&pBufferPool->FreeLists[pBuffer->FreeBuffer.FreeListIndex], pBuffer);
  1454. #if LLC_DBG
  1455. if (FrameCounted == FALSE) {
  1456. FrameCounted = TRUE;
  1457. cFramesReleased++;
  1458. }
  1459. #endif
  1460. } else {
  1461. pBuffer->FreeBuffer.BufferState = BUF_USER;
  1462. }
  1463. }
  1464. pBuffer = pNextBuffer;
  1465. }
  1466. }
  1467. if (pBufferPool != NULL) {
  1468. UNLOCK_BUFFER_POOL();
  1469. }
  1470. }
  1471. }
  1472. PDLC_BUFFER_HEADER
  1473. GetBufferHeader(
  1474. IN PDLC_BUFFER_POOL pBufferPool,
  1475. IN PVOID pUserBuffer
  1476. )
  1477. /*++
  1478. Routine Description:
  1479. Function returns the buffer pool header of the given
  1480. buffer in the user address space or NULL, if the given
  1481. address has no buffer.
  1482. Arguments:
  1483. pBufferPool - handle of buffer pool data structure.
  1484. pUserBuffer - DLC buffer address in user memory
  1485. Return Value:
  1486. Pointer of DLC buffer header
  1487. or NULL (if not found)
  1488. --*/
  1489. {
  1490. UINT PageTableIndex;
  1491. UINT IndexWithinPage;
  1492. PDLC_BUFFER_HEADER pBuffer;
  1493. //
  1494. // The buffer pool may not exist, when we are transmitting frames.
  1495. //
  1496. if (pBufferPool == NULL) {
  1497. return NULL;
  1498. }
  1499. PageTableIndex = (UINT)(((ULONG_PTR)pUserBuffer - (ULONG_PTR)pBufferPool->BaseOffset)
  1500. / MAX_DLC_BUFFER_SEGMENT);
  1501. //
  1502. // We simply discard the buffers outside the preallocated
  1503. // virtual buffer in user space. We must also check,
  1504. // that the buffer is really reserved and locked (ie.
  1505. // it is not in the free list of unlocked entries).
  1506. // Note, that the buffer pool base address have been aligned with
  1507. // the maximum buffer segment size.
  1508. //
  1509. if (PageTableIndex >= (UINT)pBufferPool->MaximumIndex
  1510. || ((ULONG_PTR)pBufferPool->BufferHeaders[PageTableIndex] >= (ULONG_PTR)pBufferPool->BufferHeaders
  1511. && (ULONG_PTR)pBufferPool->BufferHeaders[PageTableIndex] < (ULONG_PTR)&pBufferPool->BufferHeaders[pBufferPool->MaximumIndex])) {
  1512. return NULL;
  1513. }
  1514. IndexWithinPage = (UINT)(((ULONG_PTR)pUserBuffer & (MAX_DLC_BUFFER_SEGMENT - 1)) / MIN_DLC_BUFFER_SEGMENT);
  1515. for (
  1516. pBuffer = pBufferPool->BufferHeaders[PageTableIndex]->Header.pNextChild;
  1517. pBuffer != NULL;
  1518. pBuffer = pBuffer->FreeBuffer.pNextChild) {
  1519. if (pBuffer->FreeBuffer.Index == (UCHAR)IndexWithinPage) {
  1520. //
  1521. // We MUST not return a locked buffer, otherwise the app
  1522. // will corrupt the whole buffer pool.
  1523. //
  1524. if ((pBuffer->FreeBuffer.BufferState & BUF_USER) == 0) {
  1525. return NULL;
  1526. } else {
  1527. return pBuffer;
  1528. }
  1529. }
  1530. }
  1531. return NULL;
  1532. }
  1533. VOID
  1534. BufferPoolDereference(
  1535. #if DBG
  1536. IN PDLC_FILE_CONTEXT pFileContext,
  1537. #endif
  1538. IN PDLC_BUFFER_POOL *ppBufferPool
  1539. )
  1540. /*++
  1541. Routine Description:
  1542. This routine decrements the reference count of the buffer pool
  1543. and deletes it when the reference count hits to zero.
  1544. Arguments:
  1545. pFileContext - pointer to DLC_FILE_CONTEXT
  1546. pBufferPool - opaque handle of buffer pool data structure.
  1547. Return Value:
  1548. None
  1549. --*/
  1550. {
  1551. PDLC_BUFFER_HEADER pBufferHeader, pNextHeader;
  1552. KIRQL irql;
  1553. PDLC_BUFFER_POOL pBufferPool = *ppBufferPool;
  1554. ASSUME_IRQL(ANY_IRQL);
  1555. *ppBufferPool = NULL;
  1556. if (pBufferPool == NULL) {
  1557. return;
  1558. }
  1559. LOCK_BUFFER_POOL();
  1560. if (pBufferPool->ReferenceCount != 0) {
  1561. pBufferPool->ReferenceCount--;
  1562. }
  1563. if (pBufferPool->ReferenceCount == 0) {
  1564. KIRQL Irql2;
  1565. ACQUIRE_DLC_LOCK(Irql2);
  1566. RemoveFromLinkList((PVOID*)&pBufferPools, pBufferPool);
  1567. RELEASE_DLC_LOCK(Irql2);
  1568. //
  1569. // The buffer pool does not exist any more !!!
  1570. // => we can remove the spin lock and free all resources
  1571. //
  1572. UNLOCK_BUFFER_POOL();
  1573. for (pBufferHeader = (PDLC_BUFFER_HEADER)pBufferPool->PageHeaders.Flink;
  1574. !IsListEmpty(&pBufferPool->PageHeaders);
  1575. pBufferHeader = pNextHeader) {
  1576. pNextHeader = pBufferHeader->Header.pNextHeader;
  1577. #if DBG
  1578. DeallocateBuffer(pFileContext, pBufferPool, pBufferHeader);
  1579. #else
  1580. DeallocateBuffer(pBufferPool, pBufferHeader);
  1581. #endif
  1582. }
  1583. DELETE_BUFFER_POOL_FILE(&pBufferPool->hHeaderPool);
  1584. FREE_MEMORY_DRIVER(pBufferPool);
  1585. } else {
  1586. #if DBG
  1587. DbgPrint("Buffer pool not released, reference count = %d\n",
  1588. pBufferPool->ReferenceCount
  1589. );
  1590. #endif
  1591. UNLOCK_BUFFER_POOL();
  1592. }
  1593. }
  1594. NTSTATUS
  1595. BufferPoolReference(
  1596. IN HANDLE hExternalHandle,
  1597. OUT PVOID *phOpaqueHandle
  1598. )
  1599. /*++
  1600. Routine Description:
  1601. This routine translates the the external buffer pool handle to
  1602. a local opaque handle (=void pointer of the structure) and
  1603. optioanlly checks the access rights of the current process to
  1604. the buffer pool memory. The probing may raise an exeption to
  1605. the IO- system, that will return error when this terminates.
  1606. The function also increments the reference count of the buffer pool.
  1607. Arguments:
  1608. hExternalHandle - buffer handle allocated from the handle table
  1609. phOpaqueHandle - opaque handle of buffer pool data structure
  1610. Return Value:
  1611. None
  1612. --*/
  1613. {
  1614. PDLC_BUFFER_POOL pBufferPool;
  1615. NTSTATUS Status;
  1616. KIRQL irql;
  1617. ASSUME_IRQL(DISPATCH_LEVEL);
  1618. ACQUIRE_DLC_LOCK(irql);
  1619. for (pBufferPool = pBufferPools; pBufferPool != NULL; pBufferPool = pBufferPool->pNext) {
  1620. if (pBufferPool == hExternalHandle) {
  1621. break;
  1622. }
  1623. }
  1624. RELEASE_DLC_LOCK(irql);
  1625. if (pBufferPool == NULL) {
  1626. return DLC_STATUS_INVALID_BUFFER_HANDLE;
  1627. }
  1628. //
  1629. // We must do the optional probing outside of the spinlocks
  1630. // and before we have incremented the reference count.
  1631. // We do only read probing, because it is simpler.
  1632. //
  1633. RELEASE_DRIVER_LOCK();
  1634. Status = ProbeVirtualBuffer(pBufferPool->BaseOffset, pBufferPool->BufferPoolSize);
  1635. ACQUIRE_DRIVER_LOCK();
  1636. if (Status == STATUS_SUCCESS) {
  1637. LOCK_BUFFER_POOL();
  1638. pBufferPool->ReferenceCount++;
  1639. *phOpaqueHandle = (PVOID)pBufferPool;
  1640. UNLOCK_BUFFER_POOL();
  1641. }
  1642. return Status;
  1643. }
  1644. NTSTATUS
  1645. ProbeVirtualBuffer(
  1646. IN PUCHAR pBuffer,
  1647. IN LONG Length
  1648. )
  1649. /*++
  1650. Routine Description:
  1651. Tests an address range for accessability. Actually reads the first and last
  1652. DWORDs in the address range, and assumes the rest of the memory is paged-in.
  1653. Arguments:
  1654. pBuffer - address to test
  1655. Length - in bytes of region to check
  1656. Return Value:
  1657. NTSTATUS
  1658. Success - STATUS_SUCCESS
  1659. Failure - DLC_STATUS_MEMORY_LOCK_FAILED
  1660. --*/
  1661. {
  1662. NTSTATUS status = STATUS_SUCCESS;
  1663. ASSUME_IRQL(PASSIVE_LEVEL);
  1664. try {
  1665. ProbeForRead(pBuffer, Length, sizeof(UCHAR));
  1666. } except(EXCEPTION_EXECUTE_HANDLER) {
  1667. #if DBG
  1668. DbgPrint("DLC.ProbeVirtualBuffer: Error: Can't ProbeForRead a=%x, l=%x\n",
  1669. pBuffer,
  1670. Length
  1671. );
  1672. #endif
  1673. status = DLC_STATUS_MEMORY_LOCK_FAILED;
  1674. }
  1675. return status;
  1676. }
  1677. PMDL
  1678. AllocateProbeAndLockMdl(
  1679. IN PVOID UserBuffer,
  1680. IN UINT UserBufferLength
  1681. )
  1682. /*++
  1683. Routine Description:
  1684. This function just allocates, probes, locks and optionally maps
  1685. any user buffer to kernel space. Returns NULL, if the operation
  1686. fails for any reason.
  1687. Remarks:
  1688. This routine can be called only below DPC level and when the user
  1689. context is known (ie. a spin locks must not be set!).
  1690. Arguments:
  1691. UserBuffer - user space address
  1692. UserBufferLength - length of that buffer is user space
  1693. Return Value:
  1694. PMDL - pointer if successful
  1695. NULL if not successful
  1696. --*/
  1697. {
  1698. PMDL pMdl;
  1699. ASSUME_IRQL(PASSIVE_LEVEL);
  1700. try {
  1701. pMdl = IoAllocateMdl(UserBuffer,
  1702. UserBufferLength,
  1703. FALSE, // not used (no IRP)
  1704. FALSE, // we don't charge the non-paged pool quota
  1705. NULL // Do not link it to IRP
  1706. );
  1707. if (pMdl != NULL) {
  1708. #if DBG
  1709. IF_DIAG(MDL_ALLOC) {
  1710. PVOID caller, callerscaller;
  1711. RtlGetCallersAddress(&caller, &callerscaller);
  1712. DbgPrint("A: pMdl=%#x caller=%#x caller's=%#x\n",
  1713. pMdl,
  1714. caller,
  1715. callerscaller
  1716. );
  1717. }
  1718. #endif
  1719. DBG_INTERLOCKED_INCREMENT(AllocatedMdlCount);
  1720. MmProbeAndLockPages(pMdl,
  1721. UserMode, // Current user must have access!
  1722. IoModifyAccess
  1723. );
  1724. DBG_INTERLOCKED_ADD(
  1725. LockedPageCount,
  1726. +(ADDRESS_AND_SIZE_TO_SPAN_PAGES(
  1727. ((ULONG)pMdl->StartVa | pMdl->ByteOffset),
  1728. pMdl->ByteCount))
  1729. );
  1730. }
  1731. } except(EXCEPTION_EXECUTE_HANDLER) {
  1732. DBG_INTERLOCKED_INCREMENT(FailedMemoryLockings);
  1733. if (pMdl != NULL) {
  1734. IoFreeMdl(pMdl);
  1735. DBG_INTERLOCKED_DECREMENT(AllocatedMdlCount);
  1736. pMdl = NULL;
  1737. }
  1738. }
  1739. return pMdl;
  1740. }
  1741. VOID
  1742. BuildMappedPartialMdl(
  1743. IN PMDL pSourceMdl,
  1744. IN OUT PMDL pTargetMdl,
  1745. IN PVOID BaseVa,
  1746. IN ULONG Length
  1747. )
  1748. /*++
  1749. Routine Description:
  1750. This function builds a partial MDL from a mapped source MDL.
  1751. The target MDL must have been initialized for the given size.
  1752. The target MDL cannot be used after the source MDL has been
  1753. unmapped.
  1754. Remarks:
  1755. MDL_PARTIAL_HAS_BEEN_MAPPED flag is not set in MdlFlag to
  1756. prevent IoFreeMdl to unmap the virtual address.
  1757. Arguments:
  1758. pSourceMdl - Mapped source MDL
  1759. pTargetMdl - Allocate MDL
  1760. BaseVa - virtual base address
  1761. Length - length of the data
  1762. Return Value:
  1763. None
  1764. --*/
  1765. {
  1766. ASSUME_IRQL(ANY_IRQL);
  1767. if (Length) {
  1768. LlcMemCpy(&pTargetMdl[1],
  1769. &pSourceMdl[1],
  1770. (UINT)(sizeof(ULONG) * ADDRESS_AND_SIZE_TO_SPAN_PAGES(BaseVa, Length))
  1771. );
  1772. }
  1773. pTargetMdl->Next = NULL;
  1774. pTargetMdl->StartVa = (PVOID)PAGE_ALIGN(BaseVa);
  1775. pTargetMdl->ByteOffset = BYTE_OFFSET(BaseVa);
  1776. pTargetMdl->ByteCount = Length;
  1777. //
  1778. // HACK-HACK-HACK-HACK-HACK-HACK-HACK-HACK-HACK-HACK-HACK-HACK
  1779. //
  1780. // The excellent NT memory manager doesn't provide any fast way to
  1781. // create temporary MDLs that will be deallocated before their
  1782. // actual source MDLs.
  1783. // We will never map this MDL, because its mapped orginal source mdl
  1784. // will be kept in memory until this (and its peers) have been
  1785. // deallocated.
  1786. //
  1787. pTargetMdl->MdlFlags = (UCHAR)((pTargetMdl->MdlFlags & ~MDL_MAPPED_TO_SYSTEM_VA)
  1788. | MDL_SOURCE_IS_NONPAGED_POOL);
  1789. pTargetMdl->MappedSystemVa = (PVOID)((PCHAR)MmGetSystemAddressForMdl(pSourceMdl)
  1790. + ((ULONG_PTR)BaseVa - (ULONG_PTR)MmGetMdlVirtualAddress(pSourceMdl)));
  1791. }
  1792. VOID
  1793. UnlockAndFreeMdl(
  1794. PMDL pMdl
  1795. )
  1796. /*++
  1797. Routine Description:
  1798. This function unmaps (if not a partial buffer), unlocks and
  1799. and free a MDL.
  1800. OK to call at DISPATCH_LEVEL
  1801. Arguments:
  1802. pMdl - pointer to MDL to free
  1803. Return Value:
  1804. None
  1805. --*/
  1806. {
  1807. ASSUME_IRQL(ANY_IRQL);
  1808. DBG_INTERLOCKED_DECREMENT(AllocatedMdlCount);
  1809. DBG_INTERLOCKED_ADD(LockedPageCount,
  1810. -(ADDRESS_AND_SIZE_TO_SPAN_PAGES(
  1811. ((ULONG)((PMDL)pMdl)->StartVa | ((PMDL)pMdl)->ByteOffset),
  1812. (((PMDL)pMdl)->ByteCount)))
  1813. );
  1814. MmUnlockPages((PMDL)pMdl);
  1815. IoFreeMdl((PMDL)pMdl);
  1816. #if DBG
  1817. IF_DIAG(MDL_ALLOC) {
  1818. PVOID caller, callerscaller;
  1819. RtlGetCallersAddress(&caller, &callerscaller);
  1820. DbgPrint("F: pMdl=%#x caller=%#x caller's=%#x\n",
  1821. pMdl,
  1822. caller,
  1823. callerscaller
  1824. );
  1825. }
  1826. #endif
  1827. }