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.

1155 lines
32 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. mdlsup.c
  5. Abstract:
  6. This module implements the Mdl support routines for the Cache subsystem.
  7. Author:
  8. Tom Miller [TomM] 4-May-1990
  9. Revision History:
  10. --*/
  11. #include "cc.h"
  12. //
  13. // Debug Trace Level
  14. //
  15. #define me (0x00000010)
  16. #ifdef ALLOC_PRAGMA
  17. #pragma alloc_text(PAGE,CcMdlRead)
  18. #pragma alloc_text(PAGE,CcMdlReadComplete)
  19. #pragma alloc_text(PAGE,CcMdlReadComplete2)
  20. #pragma alloc_text(PAGE,CcMdlWriteComplete)
  21. #endif
  22. VOID
  23. CcMdlRead (
  24. IN PFILE_OBJECT FileObject,
  25. IN PLARGE_INTEGER FileOffset,
  26. IN ULONG Length,
  27. OUT PMDL *MdlChain,
  28. OUT PIO_STATUS_BLOCK IoStatus
  29. )
  30. /*++
  31. Routine Description:
  32. This routine attempts to lock the specified file data in the cache
  33. and return a description of it in an Mdl along with the correct
  34. I/O status. It is *not* safe to call this routine from Dpc level.
  35. This routine is synchronous, and raises on errors.
  36. As each call returns, the pages described by the Mdl are
  37. locked in memory, but not mapped in system space. If the caller
  38. needs the pages mapped in system space, then it must map them.
  39. Note that each call is a "single shot" which should be followed by
  40. a call to CcMdlReadComplete. To resume an Mdl-based transfer, the
  41. caller must form one or more subsequent calls to CcMdlRead with
  42. appropriately adjusted parameters.
  43. Arguments:
  44. FileObject - Pointer to the file object for a file which was
  45. opened with NO_INTERMEDIATE_BUFFERING clear, i.e., for
  46. which CcInitializeCacheMap was called by the file system.
  47. FileOffset - Byte offset in file for desired data.
  48. Length - Length of desired data in bytes.
  49. MdlChain - On output it returns a pointer to an Mdl chain describing
  50. the desired data. Note that even if FALSE is returned,
  51. one or more Mdls may have been allocated, as may be ascertained
  52. by the IoStatus.Information field (see below).
  53. IoStatus - Pointer to standard I/O status block to receive the status
  54. for the transfer. (STATUS_SUCCESS guaranteed for cache
  55. hits, otherwise the actual I/O status is returned.) The
  56. I/O Information Field indicates how many bytes have been
  57. successfully locked down in the Mdl Chain.
  58. Return Value:
  59. None
  60. Raises:
  61. STATUS_INSUFFICIENT_RESOURCES - If a pool allocation failure occurs.
  62. --*/
  63. {
  64. PSHARED_CACHE_MAP SharedCacheMap;
  65. PPRIVATE_CACHE_MAP PrivateCacheMap;
  66. PVOID CacheBuffer;
  67. LARGE_INTEGER FOffset;
  68. PMDL Mdl = NULL;
  69. PMDL MdlTemp;
  70. PETHREAD Thread = PsGetCurrentThread();
  71. ULONG SavedState = 0;
  72. ULONG OriginalLength = Length;
  73. ULONG Information = 0;
  74. PVACB Vacb = NULL;
  75. ULONG SavedMissCounter = 0;
  76. ULONG ActivePage;
  77. ULONG PageIsDirty;
  78. PVACB ActiveVacb = NULL;
  79. DebugTrace(+1, me, "CcMdlRead\n", 0 );
  80. DebugTrace( 0, me, " FileObject = %08lx\n", FileObject );
  81. DebugTrace2(0, me, " FileOffset = %08lx, %08lx\n", FileOffset->LowPart,
  82. FileOffset->HighPart );
  83. DebugTrace( 0, me, " Length = %08lx\n", Length );
  84. //
  85. // Save the current readahead hints.
  86. //
  87. MmSavePageFaultReadAhead( Thread, &SavedState );
  88. //
  89. // Get pointer to SharedCacheMap.
  90. //
  91. SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
  92. PrivateCacheMap = FileObject->PrivateCacheMap;
  93. //
  94. // See if we have an active Vacb, that we need to free.
  95. //
  96. GetActiveVacb( SharedCacheMap, OldIrql, ActiveVacb, ActivePage, PageIsDirty );
  97. //
  98. // If there is an end of a page to be zeroed, then free that page now,
  99. // so we don't send Greg the uninitialized data...
  100. //
  101. if ((ActiveVacb != NULL) || (SharedCacheMap->NeedToZero != NULL)) {
  102. CcFreeActiveVacb( SharedCacheMap, ActiveVacb, ActivePage, PageIsDirty );
  103. }
  104. //
  105. // If read ahead is enabled, then do the read ahead here so it
  106. // overlaps with the copy (otherwise we will do it below).
  107. // Note that we are assuming that we will not get ahead of our
  108. // current transfer - if read ahead is working it should either
  109. // already be in memory or else underway.
  110. //
  111. if (PrivateCacheMap->Flags.ReadAheadEnabled && (PrivateCacheMap->ReadAheadLength[1] == 0)) {
  112. CcScheduleReadAhead( FileObject, FileOffset, Length );
  113. }
  114. //
  115. // Increment performance counters
  116. //
  117. CcMdlReadWait += 1;
  118. //
  119. // This is not an exact solution, but when IoPageRead gets a miss,
  120. // it cannot tell whether it was CcCopyRead or CcMdlRead, but since
  121. // the miss should occur very soon, by loading the pointer here
  122. // probably the right counter will get incremented, and in any case,
  123. // we hope the errrors average out!
  124. //
  125. CcMissCounter = &CcMdlReadWaitMiss;
  126. FOffset = *FileOffset;
  127. //
  128. // Check for read past file size, the caller must filter this case out.
  129. //
  130. ASSERT( ( FOffset.QuadPart + (LONGLONG)Length ) <= SharedCacheMap->FileSize.QuadPart );
  131. //
  132. // Put try-finally around the loop to deal with any exceptions
  133. //
  134. try {
  135. //
  136. // Not all of the transfer will come back at once, so we have to loop
  137. // until the entire transfer is complete.
  138. //
  139. while (Length != 0) {
  140. ULONG ReceivedLength;
  141. LARGE_INTEGER BeyondLastByte;
  142. //
  143. // Map the data and read it in (if necessary) with the
  144. // MmProbeAndLockPages call below.
  145. //
  146. CacheBuffer = CcGetVirtualAddress( SharedCacheMap,
  147. FOffset,
  148. &Vacb,
  149. &ReceivedLength );
  150. if (ReceivedLength > Length) {
  151. ReceivedLength = Length;
  152. }
  153. BeyondLastByte.QuadPart = FOffset.QuadPart + (LONGLONG)ReceivedLength;
  154. //
  155. // Now attempt to allocate an Mdl to describe the mapped data.
  156. //
  157. DebugTrace( 0, mm, "IoAllocateMdl:\n", 0 );
  158. DebugTrace( 0, mm, " BaseAddress = %08lx\n", CacheBuffer );
  159. DebugTrace( 0, mm, " Length = %08lx\n", ReceivedLength );
  160. Mdl = IoAllocateMdl( CacheBuffer,
  161. ReceivedLength,
  162. FALSE,
  163. FALSE,
  164. NULL );
  165. DebugTrace( 0, mm, " <Mdl = %08lx\n", Mdl );
  166. if (Mdl == NULL) {
  167. DebugTrace( 0, 0, "Failed to allocate Mdl\n", 0 );
  168. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  169. }
  170. DebugTrace( 0, mm, "MmProbeAndLockPages:\n", 0 );
  171. DebugTrace( 0, mm, " Mdl = %08lx\n", Mdl );
  172. //
  173. // Set to see if the miss counter changes in order to
  174. // detect when we should turn on read ahead.
  175. //
  176. SavedMissCounter += CcMdlReadWaitMiss;
  177. MmSetPageFaultReadAhead( Thread, ADDRESS_AND_SIZE_TO_SPAN_PAGES( CacheBuffer, ReceivedLength ) - 1);
  178. MmProbeAndLockPages( Mdl, KernelMode, IoReadAccess );
  179. SavedMissCounter -= CcMdlReadWaitMiss;
  180. //
  181. // Unmap the data now, now that the pages are locked down.
  182. //
  183. CcFreeVirtualAddress( Vacb );
  184. Vacb = NULL;
  185. //
  186. // Now link the Mdl into the caller's chain
  187. //
  188. if ( *MdlChain == NULL ) {
  189. *MdlChain = Mdl;
  190. } else {
  191. MdlTemp = CONTAINING_RECORD( *MdlChain, MDL, Next );
  192. while (MdlTemp->Next != NULL) {
  193. MdlTemp = MdlTemp->Next;
  194. }
  195. MdlTemp->Next = Mdl;
  196. }
  197. Mdl = NULL;
  198. //
  199. // Assume we did not get all the data we wanted, and set FOffset
  200. // to the end of the returned data.
  201. //
  202. FOffset = BeyondLastByte;
  203. //
  204. // Update number of bytes transferred.
  205. //
  206. Information += ReceivedLength;
  207. //
  208. // Calculate length left to transfer.
  209. //
  210. Length -= ReceivedLength;
  211. }
  212. }
  213. finally {
  214. CcMissCounter = &CcThrowAway;
  215. //
  216. // Restore the readahead hints.
  217. //
  218. MmResetPageFaultReadAhead( Thread, SavedState );
  219. if (AbnormalTermination()) {
  220. //
  221. // We may have failed to allocate an Mdl while still having
  222. // data mapped.
  223. //
  224. if (Vacb != NULL) {
  225. CcFreeVirtualAddress( Vacb );
  226. }
  227. if (Mdl != NULL) {
  228. IoFreeMdl( Mdl );
  229. }
  230. //
  231. // Otherwise loop to deallocate the Mdls
  232. //
  233. while (*MdlChain != NULL) {
  234. MdlTemp = (*MdlChain)->Next;
  235. DebugTrace( 0, mm, "MmUnlockPages/IoFreeMdl:\n", 0 );
  236. DebugTrace( 0, mm, " Mdl = %08lx\n", *MdlChain );
  237. MmUnlockPages( *MdlChain );
  238. IoFreeMdl( *MdlChain );
  239. *MdlChain = MdlTemp;
  240. }
  241. DebugTrace(-1, me, "CcMdlRead -> Unwinding\n", 0 );
  242. }
  243. else {
  244. //
  245. // Now enable read ahead if it looks like we got any misses, and do
  246. // the first one.
  247. //
  248. if (!FlagOn( FileObject->Flags, FO_RANDOM_ACCESS ) &&
  249. !PrivateCacheMap->Flags.ReadAheadEnabled &&
  250. (SavedMissCounter != 0)) {
  251. CC_CLEAR_PRIVATE_CACHE_MAP (PrivateCacheMap, PRIVATE_CACHE_MAP_READ_AHEAD_ENABLED);
  252. CcScheduleReadAhead( FileObject, FileOffset, OriginalLength );
  253. }
  254. //
  255. // Now that we have described our desired read ahead, let's
  256. // shift the read history down.
  257. //
  258. PrivateCacheMap->FileOffset1 = PrivateCacheMap->FileOffset2;
  259. PrivateCacheMap->BeyondLastByte1 = PrivateCacheMap->BeyondLastByte2;
  260. PrivateCacheMap->FileOffset2 = *FileOffset;
  261. PrivateCacheMap->BeyondLastByte2.QuadPart =
  262. FileOffset->QuadPart + (LONGLONG)OriginalLength;
  263. IoStatus->Status = STATUS_SUCCESS;
  264. IoStatus->Information = Information;
  265. }
  266. }
  267. DebugTrace( 0, me, " <MdlChain = %08lx\n", *MdlChain );
  268. DebugTrace2(0, me, " <IoStatus = %08lx, %08lx\n", IoStatus->Status,
  269. IoStatus->Information );
  270. DebugTrace(-1, me, "CcMdlRead -> VOID\n", 0 );
  271. return;
  272. }
  273. //
  274. // First we have the old routine which checks for an entry in the FastIo vector.
  275. // This routine becomes obsolete for every component that compiles with the new
  276. // definition of FsRtlMdlReadComplete in fsrtl.h.
  277. //
  278. VOID
  279. CcMdlReadComplete (
  280. IN PFILE_OBJECT FileObject,
  281. IN PMDL MdlChain
  282. )
  283. {
  284. PDEVICE_OBJECT DeviceObject;
  285. PFAST_IO_DISPATCH FastIoDispatch;
  286. DeviceObject = IoGetRelatedDeviceObject( FileObject );
  287. FastIoDispatch = DeviceObject->DriverObject->FastIoDispatch;
  288. if ((FastIoDispatch != NULL) &&
  289. (FastIoDispatch->SizeOfFastIoDispatch > FIELD_OFFSET(FAST_IO_DISPATCH, MdlWriteComplete)) &&
  290. (FastIoDispatch->MdlReadComplete != NULL) &&
  291. FastIoDispatch->MdlReadComplete( FileObject, MdlChain, DeviceObject )) {
  292. NOTHING;
  293. } else {
  294. CcMdlReadComplete2( FileObject, MdlChain );
  295. }
  296. }
  297. VOID
  298. CcMdlReadComplete2 (
  299. IN PFILE_OBJECT FileObject,
  300. IN PMDL MdlChain
  301. )
  302. /*++
  303. Routine Description:
  304. This routine must be called at IPL0 after a call to CcMdlRead. The
  305. caller must simply supply the address of the MdlChain returned in
  306. CcMdlRead.
  307. This call does the following:
  308. Deletes the MdlChain
  309. Arguments:
  310. FileObject - Pointer to the file object for a file which was
  311. opened with NO_INTERMEDIATE_BUFFERING clear, i.e., for
  312. which CcInitializeCacheMap was called by the file system.
  313. MdlChain - same as returned from corresponding call to CcMdlRead.
  314. Return Value:
  315. None.
  316. --*/
  317. {
  318. PMDL MdlNext;
  319. UNREFERENCED_PARAMETER (FileObject);
  320. DebugTrace(+1, me, "CcMdlReadComplete\n", 0 );
  321. DebugTrace( 0, me, " FileObject = %08lx\n", FileObject );
  322. DebugTrace( 0, me, " MdlChain = %08lx\n", MdlChain );
  323. //
  324. // Deallocate the Mdls
  325. //
  326. while (MdlChain != NULL) {
  327. MdlNext = MdlChain->Next;
  328. DebugTrace( 0, mm, "MmUnlockPages/IoFreeMdl:\n", 0 );
  329. DebugTrace( 0, mm, " Mdl = %08lx\n", MdlChain );
  330. MmUnlockPages( MdlChain );
  331. IoFreeMdl( MdlChain );
  332. MdlChain = MdlNext;
  333. }
  334. DebugTrace(-1, me, "CcMdlReadComplete -> VOID\n", 0 );
  335. }
  336. VOID
  337. CcPrepareMdlWrite (
  338. IN PFILE_OBJECT FileObject,
  339. IN PLARGE_INTEGER FileOffset,
  340. IN ULONG Length,
  341. OUT PMDL *MdlChain,
  342. OUT PIO_STATUS_BLOCK IoStatus
  343. )
  344. /*++
  345. Routine Description:
  346. This routine attempts to lock the specified file data in the cache
  347. and return a description of it in an Mdl along with the correct
  348. I/O status. Pages to be completely overwritten may be satisfied
  349. with emtpy pages. It is *not* safe to call this routine from Dpc level.
  350. This call is synchronous and raises on error.
  351. When this call returns, the caller may immediately begin
  352. to transfer data into the buffers via the Mdl.
  353. When the call returns with TRUE, the pages described by the Mdl are
  354. locked in memory, but not mapped in system space. If the caller
  355. needs the pages mapped in system space, then it must map them.
  356. On the subsequent call to CcMdlWriteComplete the pages will be
  357. unmapped if they were mapped, and in any case unlocked and the Mdl
  358. deallocated.
  359. Arguments:
  360. FileObject - Pointer to the file object for a file which was
  361. opened with NO_INTERMEDIATE_BUFFERING clear, i.e., for
  362. which CcInitializeCacheMap was called by the file system.
  363. FileOffset - Byte offset in file for desired data.
  364. Length - Length of desired data in bytes.
  365. MdlChain - On output it returns a pointer to an Mdl chain describing
  366. the desired data. Note that even if FALSE is returned,
  367. one or more Mdls may have been allocated, as may be ascertained
  368. by the IoStatus.Information field (see below).
  369. IoStatus - Pointer to standard I/O status block to receive the status
  370. for the in-transfer of the data. (STATUS_SUCCESS guaranteed
  371. for cache hits, otherwise the actual I/O status is returned.)
  372. The I/O Information Field indicates how many bytes have been
  373. successfully locked down in the Mdl Chain.
  374. Return Value:
  375. None
  376. --*/
  377. {
  378. PSHARED_CACHE_MAP SharedCacheMap;
  379. PVOID CacheBuffer;
  380. LARGE_INTEGER FOffset;
  381. PMDL Mdl = NULL;
  382. PMDL MdlTemp;
  383. LARGE_INTEGER Temp;
  384. ULONG SavedState = 0;
  385. ULONG ZeroFlags = 0;
  386. ULONG Information = 0;
  387. KLOCK_QUEUE_HANDLE LockHandle;
  388. ULONG ActivePage;
  389. ULONG PageIsDirty;
  390. PVACB Vacb = NULL;
  391. DebugTrace(+1, me, "CcPrepareMdlWrite\n", 0 );
  392. DebugTrace( 0, me, " FileObject = %08lx\n", FileObject );
  393. DebugTrace2(0, me, " FileOffset = %08lx, %08lx\n", FileOffset->LowPart,
  394. FileOffset->HighPart );
  395. DebugTrace( 0, me, " Length = %08lx\n", Length );
  396. //
  397. // Get pointer to SharedCacheMap.
  398. //
  399. SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
  400. //
  401. // See if we have an active Vacb, that we need to free.
  402. //
  403. GetActiveVacb( SharedCacheMap, LockHandle.OldIrql, Vacb, ActivePage, PageIsDirty );
  404. //
  405. // If there is an end of a page to be zeroed, then free that page now,
  406. // so it does not cause our data to get zeroed. If there is an active
  407. // page, free it so we have the correct ValidDataGoal.
  408. //
  409. if ((Vacb != NULL) || (SharedCacheMap->NeedToZero != NULL)) {
  410. CcFreeActiveVacb( SharedCacheMap, Vacb, ActivePage, PageIsDirty );
  411. Vacb = NULL;
  412. }
  413. FOffset = *FileOffset;
  414. //
  415. // Put try-finally around the loop to deal with exceptions
  416. //
  417. try {
  418. //
  419. // Not all of the transfer will come back at once, so we have to loop
  420. // until the entire transfer is complete.
  421. //
  422. while (Length != 0) {
  423. ULONG ReceivedLength;
  424. LARGE_INTEGER BeyondLastByte;
  425. //
  426. // Map and see how much we could potentially access at this
  427. // FileOffset, then cut it down if it is more than we need.
  428. //
  429. CacheBuffer = CcGetVirtualAddress( SharedCacheMap,
  430. FOffset,
  431. &Vacb,
  432. &ReceivedLength );
  433. if (ReceivedLength > Length) {
  434. ReceivedLength = Length;
  435. }
  436. BeyondLastByte.QuadPart = FOffset.QuadPart + (LONGLONG)ReceivedLength;
  437. //
  438. // At this point we can calculate the ZeroFlags.
  439. //
  440. //
  441. // We can always zero middle pages, if any.
  442. //
  443. ZeroFlags = ZERO_MIDDLE_PAGES;
  444. //
  445. // See if we are completely overwriting the first or last page.
  446. //
  447. if (((FOffset.LowPart & (PAGE_SIZE - 1)) == 0) &&
  448. (ReceivedLength >= PAGE_SIZE)) {
  449. ZeroFlags |= ZERO_FIRST_PAGE;
  450. }
  451. if ((BeyondLastByte.LowPart & (PAGE_SIZE - 1)) == 0) {
  452. ZeroFlags |= ZERO_LAST_PAGE;
  453. }
  454. //
  455. // See if the entire transfer is beyond valid data length,
  456. // or at least starting from the second page.
  457. //
  458. Temp = FOffset;
  459. Temp.LowPart &= ~(PAGE_SIZE -1);
  460. KeAcquireInStackQueuedSpinLock( &SharedCacheMap->BcbSpinLock, &LockHandle );
  461. Temp.QuadPart = SharedCacheMap->ValidDataGoal.QuadPart - Temp.QuadPart;
  462. KeReleaseInStackQueuedSpinLock( &LockHandle );
  463. if (Temp.QuadPart <= 0) {
  464. ZeroFlags |= ZERO_FIRST_PAGE | ZERO_MIDDLE_PAGES | ZERO_LAST_PAGE;
  465. } else if ((Temp.HighPart == 0) && (Temp.LowPart <= PAGE_SIZE)) {
  466. ZeroFlags |= ZERO_MIDDLE_PAGES | ZERO_LAST_PAGE;
  467. }
  468. (VOID)CcMapAndRead( SharedCacheMap,
  469. &FOffset,
  470. ReceivedLength,
  471. ZeroFlags,
  472. TRUE,
  473. CacheBuffer );
  474. //
  475. // Now attempt to allocate an Mdl to describe the mapped data.
  476. //
  477. DebugTrace( 0, mm, "IoAllocateMdl:\n", 0 );
  478. DebugTrace( 0, mm, " BaseAddress = %08lx\n", CacheBuffer );
  479. DebugTrace( 0, mm, " Length = %08lx\n", ReceivedLength );
  480. Mdl = IoAllocateMdl( CacheBuffer,
  481. ReceivedLength,
  482. FALSE,
  483. FALSE,
  484. NULL );
  485. DebugTrace( 0, mm, " <Mdl = %08lx\n", Mdl );
  486. if (Mdl == NULL) {
  487. DebugTrace( 0, 0, "Failed to allocate Mdl\n", 0 );
  488. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  489. }
  490. DebugTrace( 0, mm, "MmProbeAndLockPages:\n", 0 );
  491. DebugTrace( 0, mm, " Mdl = %08lx\n", Mdl );
  492. MmDisablePageFaultClustering(&SavedState);
  493. MmProbeAndLockPages( Mdl, KernelMode, IoWriteAccess );
  494. MmEnablePageFaultClustering(SavedState);
  495. SavedState = 0;
  496. //
  497. // Now that some data (maybe zeros) is locked in memory and
  498. // set dirty, it is safe, and necessary for us to advance
  499. // valid data goal, so that we will not subsequently ask
  500. // for a zero page. Note if we are extending valid data,
  501. // our caller has the file exclusive.
  502. //
  503. KeAcquireInStackQueuedSpinLock( &SharedCacheMap->BcbSpinLock, &LockHandle );
  504. if (BeyondLastByte.QuadPart > SharedCacheMap->ValidDataGoal.QuadPart) {
  505. SharedCacheMap->ValidDataGoal = BeyondLastByte;
  506. }
  507. KeReleaseInStackQueuedSpinLock( &LockHandle );
  508. //
  509. // Unmap the data now, now that the pages are locked down.
  510. //
  511. CcFreeVirtualAddress( Vacb );
  512. Vacb = NULL;
  513. //
  514. // Now link the Mdl into the caller's chain
  515. //
  516. if ( *MdlChain == NULL ) {
  517. *MdlChain = Mdl;
  518. } else {
  519. MdlTemp = CONTAINING_RECORD( *MdlChain, MDL, Next );
  520. while (MdlTemp->Next != NULL) {
  521. MdlTemp = MdlTemp->Next;
  522. }
  523. MdlTemp->Next = Mdl;
  524. }
  525. Mdl = NULL;
  526. //
  527. // Assume we did not get all the data we wanted, and set FOffset
  528. // to the end of the returned data.
  529. //
  530. FOffset = BeyondLastByte;
  531. //
  532. // Update number of bytes transferred.
  533. //
  534. Information += ReceivedLength;
  535. //
  536. // Calculate length left to transfer.
  537. //
  538. Length -= ReceivedLength;
  539. }
  540. }
  541. finally {
  542. if (AbnormalTermination()) {
  543. if (SavedState != 0) {
  544. MmEnablePageFaultClustering(SavedState);
  545. }
  546. if (Vacb != NULL) {
  547. CcFreeVirtualAddress( Vacb );
  548. }
  549. if (Mdl != NULL) {
  550. IoFreeMdl( Mdl );
  551. }
  552. //
  553. // Otherwise loop to deallocate the Mdls
  554. //
  555. FOffset = *FileOffset;
  556. while (*MdlChain != NULL) {
  557. MdlTemp = (*MdlChain)->Next;
  558. DebugTrace( 0, mm, "MmUnlockPages/IoFreeMdl:\n", 0 );
  559. DebugTrace( 0, mm, " Mdl = %08lx\n", *MdlChain );
  560. MmUnlockPages( *MdlChain );
  561. //
  562. // Extract the File Offset for this part of the transfer, and
  563. // tell the lazy writer to write these pages, since we have
  564. // marked them dirty. Ignore the only exception (allocation
  565. // error), and console ourselves for having tried.
  566. //
  567. CcSetDirtyInMask( SharedCacheMap, &FOffset, (*MdlChain)->ByteCount );
  568. FOffset.QuadPart = FOffset.QuadPart + (LONGLONG)((*MdlChain)->ByteCount);
  569. IoFreeMdl( *MdlChain );
  570. *MdlChain = MdlTemp;
  571. }
  572. DebugTrace(-1, me, "CcPrepareMdlWrite -> Unwinding\n", 0 );
  573. }
  574. else {
  575. IoStatus->Status = STATUS_SUCCESS;
  576. IoStatus->Information = Information;
  577. //
  578. // Make sure the SharedCacheMap does not go away while
  579. // the Mdl write is in progress. We decrment below.
  580. //
  581. CcAcquireMasterLock( &LockHandle.OldIrql );
  582. CcIncrementOpenCount( SharedCacheMap, 'ldmP' );
  583. CcReleaseMasterLock( LockHandle.OldIrql );
  584. }
  585. }
  586. DebugTrace( 0, me, " <MdlChain = %08lx\n", *MdlChain );
  587. DebugTrace(-1, me, "CcPrepareMdlWrite -> VOID\n", 0 );
  588. return;
  589. }
  590. //
  591. // First we have the old routine which checks for an entry in the FastIo vector.
  592. // This routine becomes obsolete for every component that compiles with the new
  593. // definition of FsRtlMdlWriteComplete in fsrtl.h.
  594. //
  595. VOID
  596. CcMdlWriteComplete (
  597. IN PFILE_OBJECT FileObject,
  598. IN PLARGE_INTEGER FileOffset,
  599. IN PMDL MdlChain
  600. )
  601. {
  602. PDEVICE_OBJECT DeviceObject;
  603. PFAST_IO_DISPATCH FastIoDispatch;
  604. DeviceObject = IoGetRelatedDeviceObject( FileObject );
  605. FastIoDispatch = DeviceObject->DriverObject->FastIoDispatch;
  606. if ((FastIoDispatch != NULL) &&
  607. (FastIoDispatch->SizeOfFastIoDispatch > FIELD_OFFSET(FAST_IO_DISPATCH, MdlWriteComplete)) &&
  608. (FastIoDispatch->MdlWriteComplete != NULL) &&
  609. FastIoDispatch->MdlWriteComplete( FileObject, FileOffset, MdlChain, DeviceObject )) {
  610. NOTHING;
  611. } else {
  612. CcMdlWriteComplete2( FileObject, FileOffset, MdlChain );
  613. }
  614. }
  615. VOID
  616. CcMdlWriteComplete2 (
  617. IN PFILE_OBJECT FileObject,
  618. IN PLARGE_INTEGER FileOffset,
  619. IN PMDL MdlChain
  620. )
  621. /*++
  622. Routine Description:
  623. This routine must be called at IPL0 after a call to CcPrepareMdlWrite.
  624. The caller supplies the ActualLength of data that it actually wrote
  625. into the buffer, which may be less than or equal to the Length specified
  626. in CcPrepareMdlWrite.
  627. This call does the following:
  628. Makes sure the data up to ActualLength eventually gets written.
  629. If WriteThrough is FALSE, the data will not be written immediately.
  630. If WriteThrough is TRUE, then the data is written synchronously.
  631. Unmaps the pages (if mapped), unlocks them and deletes the MdlChain
  632. Arguments:
  633. FileObject - Pointer to the file object for a file which was
  634. opened with NO_INTERMEDIATE_BUFFERING clear, i.e., for
  635. which CcInitializeCacheMap was called by the file system.
  636. FileOffset - Original file offset read above.
  637. MdlChain - same as returned from corresponding call to CcPrepareMdlWrite.
  638. Return Value:
  639. None
  640. --*/
  641. {
  642. PMDL MdlNext;
  643. PMDL Mdl;
  644. PSHARED_CACHE_MAP SharedCacheMap;
  645. LARGE_INTEGER FOffset;
  646. IO_STATUS_BLOCK IoStatus;
  647. KIRQL OldIrql;
  648. NTSTATUS StatusToRaise = STATUS_SUCCESS;
  649. BOOLEAN First = FALSE;
  650. DebugTrace(+1, me, "CcMdlWriteComplete\n", 0 );
  651. DebugTrace( 0, me, " FileObject = %08lx\n", FileObject );
  652. DebugTrace( 0, me, " MdlChain = %08lx\n", MdlChain );
  653. SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
  654. //
  655. // Deallocate the Mdls
  656. //
  657. FOffset.QuadPart = *(LONGLONG UNALIGNED *)FileOffset;
  658. Mdl = MdlChain;
  659. //
  660. // If the MDL is unlocked, this is a retry.
  661. //
  662. if (FlagOn( MdlChain->MdlFlags, MDL_PAGES_LOCKED )) {
  663. First = TRUE;
  664. }
  665. while (Mdl != NULL) {
  666. MdlNext = Mdl->Next;
  667. DebugTrace( 0, mm, "MmUnlockPages/IoFreeMdl:\n", 0 );
  668. DebugTrace( 0, mm, " Mdl = %08lx\n", Mdl );
  669. //
  670. // Now clear the dirty bits in the Pte and set them in the
  671. // Pfn. The Mdls will not be locked on repeated completion
  672. // attempts.
  673. //
  674. if (First) {
  675. MmUnlockPages( Mdl );
  676. }
  677. //
  678. // Extract the File Offset for this part of the transfer.
  679. //
  680. if (FlagOn(FileObject->Flags, FO_WRITE_THROUGH)) {
  681. MmFlushSection ( FileObject->SectionObjectPointer,
  682. &FOffset,
  683. Mdl->ByteCount,
  684. &IoStatus,
  685. TRUE );
  686. //
  687. // If we got an I/O error, remember it.
  688. //
  689. if (!NT_SUCCESS(IoStatus.Status)) {
  690. StatusToRaise = IoStatus.Status;
  691. }
  692. } else {
  693. //
  694. // Ignore the only exception (allocation error), and console
  695. // ourselves for having tried.
  696. //
  697. CcSetDirtyInMask( SharedCacheMap, &FOffset, Mdl->ByteCount );
  698. }
  699. FOffset.QuadPart = FOffset.QuadPart + (LONGLONG)(Mdl->ByteCount);
  700. Mdl = MdlNext;
  701. }
  702. //
  703. // Remove our open count and check to see if this makes the shared cache
  704. // map eligible for lazy close.
  705. //
  706. // We do this now so, on failure, old filesystems which did not expect
  707. // writethrough to raise continue to work. They will be within exception
  708. // handling with the Mdl still in the IRP.
  709. //
  710. // Note that non-writethrough is the only one that needs the cache map,
  711. // and it'll always work. Removing the open count for writethrough
  712. // could be a minor win.
  713. //
  714. if (First) {
  715. CcAcquireMasterLock( &OldIrql );
  716. CcDecrementOpenCount( SharedCacheMap, 'ldmC' );
  717. if ((SharedCacheMap->OpenCount == 0) &&
  718. !FlagOn(SharedCacheMap->Flags, WRITE_QUEUED) &&
  719. (SharedCacheMap->DirtyPages == 0)) {
  720. //
  721. // Move to the dirty list.
  722. //
  723. RemoveEntryList( &SharedCacheMap->SharedCacheMapLinks );
  724. InsertTailList( &CcDirtySharedCacheMapList.SharedCacheMapLinks,
  725. &SharedCacheMap->SharedCacheMapLinks );
  726. //
  727. // Make sure the Lazy Writer will wake up, because we
  728. // want him to delete this SharedCacheMap.
  729. //
  730. LazyWriter.OtherWork = TRUE;
  731. if (!LazyWriter.ScanActive) {
  732. CcScheduleLazyWriteScan( FALSE );
  733. }
  734. }
  735. CcReleaseMasterLock( OldIrql );
  736. }
  737. //
  738. // If we got an I/O error, raise it now. Note that we have not free'd the Mdl
  739. // yet so the owning filesystem can retry the completion.
  740. //
  741. if (!NT_SUCCESS(StatusToRaise)) {
  742. ExRaiseStatus( FsRtlNormalizeNtstatus( StatusToRaise,
  743. STATUS_UNEXPECTED_IO_ERROR ));
  744. }
  745. //
  746. // Otherwise, free the Mdl chain and clean everything up.
  747. //
  748. Mdl = MdlChain;
  749. while (Mdl != NULL) {
  750. MdlNext = Mdl->Next;
  751. IoFreeMdl( Mdl );
  752. Mdl = MdlNext;
  753. }
  754. DebugTrace(-1, me, "CcMdlWriteComplete -> TRUE\n", 0 );
  755. return;
  756. }
  757. VOID
  758. CcMdlWriteAbort (
  759. IN PFILE_OBJECT FileObject,
  760. IN PMDL MdlChain
  761. )
  762. /*++
  763. Routine Description:
  764. This routine must be called at IPL0 after a call to CcPrepareMdlWrite.
  765. This call does the following:
  766. Unmaps the pages (if mapped), unlocks them and deletes the MdlChain
  767. unlike the CcMdlWriteComplete this is only used to do teardown in a non
  768. success case where we didn't actually write anything
  769. Arguments:
  770. FileObject - Pointer to the file object for a file which was
  771. opened with NO_INTERMEDIATE_BUFFERING clear, i.e., for
  772. which CcInitializeCacheMap was called by the file system.
  773. MdlChain - same as returned from corresponding call to CcPrepareMdlWrite.
  774. Return Value:
  775. None
  776. --*/
  777. {
  778. PMDL MdlNext;
  779. PSHARED_CACHE_MAP SharedCacheMap;
  780. KIRQL OldIrql;
  781. BOOLEAN First = FALSE;
  782. DebugTrace(+1, me, "CcMdlWriteAbort\n", 0 );
  783. DebugTrace( 0, me, " FileObject = %08lx\n", FileObject );
  784. DebugTrace( 0, me, " MdlChain = %08lx\n", MdlChain );
  785. SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
  786. //
  787. // If the MDL is unlocked, we went through completion.
  788. //
  789. if (FlagOn( MdlChain->MdlFlags, MDL_PAGES_LOCKED )) {
  790. First = TRUE;
  791. }
  792. //
  793. // Deallocate the Mdls
  794. //
  795. while (MdlChain != NULL) {
  796. MdlNext = MdlChain->Next;
  797. DebugTrace( 0, mm, "MmUnlockPages/IoFreeMdl:\n", 0 );
  798. DebugTrace( 0, mm, " Mdl = %08lx\n", MdlChain );
  799. if (First) {
  800. MmUnlockPages( MdlChain );
  801. }
  802. IoFreeMdl( MdlChain );
  803. MdlChain = MdlNext;
  804. }
  805. //
  806. // Now release our open count. If this already went through completion,
  807. // the opencount is already dropped.
  808. //
  809. if (First) {
  810. CcAcquireMasterLock( &OldIrql );
  811. CcDecrementOpenCount( SharedCacheMap, 'AdmC' );
  812. //
  813. // Check for a possible deletion, this Mdl write may have been the last
  814. // reference.
  815. //
  816. if ((SharedCacheMap->OpenCount == 0) &&
  817. !FlagOn(SharedCacheMap->Flags, WRITE_QUEUED) &&
  818. (SharedCacheMap->DirtyPages == 0)) {
  819. //
  820. // Move to the dirty list.
  821. //
  822. RemoveEntryList( &SharedCacheMap->SharedCacheMapLinks );
  823. InsertTailList( &CcDirtySharedCacheMapList.SharedCacheMapLinks,
  824. &SharedCacheMap->SharedCacheMapLinks );
  825. //
  826. // Make sure the Lazy Writer will wake up, because we
  827. // want him to delete this SharedCacheMap.
  828. //
  829. LazyWriter.OtherWork = TRUE;
  830. if (!LazyWriter.ScanActive) {
  831. CcScheduleLazyWriteScan( FALSE );
  832. }
  833. }
  834. CcReleaseMasterLock( OldIrql );
  835. }
  836. return;
  837. }