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.

2259 lines
71 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. copysup.c
  5. Abstract:
  6. This module implements the copy 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. // Define our debug constant
  14. //
  15. #define me 0x00000004
  16. #ifdef ALLOC_PRAGMA
  17. #pragma alloc_text(PAGE,CcCopyRead)
  18. #pragma alloc_text(PAGE,CcFastCopyRead)
  19. #endif
  20. BOOLEAN
  21. CcCopyRead (
  22. IN PFILE_OBJECT FileObject,
  23. IN PLARGE_INTEGER FileOffset,
  24. IN ULONG Length,
  25. IN BOOLEAN Wait,
  26. OUT PVOID Buffer,
  27. OUT PIO_STATUS_BLOCK IoStatus
  28. )
  29. /*++
  30. Routine Description:
  31. This routine attempts to copy the specified file data from the cache
  32. into the output buffer, and deliver the correct I/O status. It is *not*
  33. safe to call this routine from Dpc level.
  34. If the caller does not want to block (such as for disk I/O), then
  35. Wait should be supplied as FALSE. If Wait was supplied as FALSE and
  36. it is currently impossible to supply all of the requested data without
  37. blocking, then this routine will return FALSE. However, if the
  38. data is immediately accessible in the cache and no blocking is
  39. required, this routine copies the data and returns TRUE.
  40. If the caller supplies Wait as TRUE, then this routine is guaranteed
  41. to copy the data and return TRUE. If the data is immediately
  42. accessible in the cache, then no blocking will occur. Otherwise,
  43. the the data transfer from the file into the cache will be initiated,
  44. and the caller will be blocked until the data can be returned.
  45. File system Fsd's should typically supply Wait = TRUE if they are
  46. processing a synchronous I/O requests, or Wait = FALSE if they are
  47. processing an asynchronous request.
  48. File system or Server Fsp threads should supply Wait = TRUE.
  49. Arguments:
  50. FileObject - Pointer to the file object for a file which was
  51. opened with NO_INTERMEDIATE_BUFFERING clear, i.e., for
  52. which CcInitializeCacheMap was called by the file system.
  53. FileOffset - Byte offset in file for desired data.
  54. Length - Length of desired data in bytes.
  55. Wait - FALSE if caller may not block, TRUE otherwise (see description
  56. above)
  57. Buffer - Pointer to output buffer to which data should be copied.
  58. IoStatus - Pointer to standard I/O status block to receive the status
  59. for the transfer. (STATUS_SUCCESS guaranteed for cache
  60. hits, otherwise the actual I/O status is returned.)
  61. Note that even if FALSE is returned, the IoStatus.Information
  62. field will return the count of any bytes successfully
  63. transferred before a blocking condition occured. The caller
  64. may either choose to ignore this information, or resume
  65. the copy later accounting for bytes transferred.
  66. Return Value:
  67. FALSE - if Wait was supplied as FALSE and the data was not delivered
  68. TRUE - if the data is being delivered
  69. --*/
  70. {
  71. PSHARED_CACHE_MAP SharedCacheMap;
  72. PPRIVATE_CACHE_MAP PrivateCacheMap;
  73. PVOID CacheBuffer;
  74. LARGE_INTEGER FOffset;
  75. PVACB Vacb;
  76. PBCB Bcb;
  77. PVACB ActiveVacb;
  78. ULONG ActivePage;
  79. ULONG PageIsDirty;
  80. ULONG SavedState;
  81. ULONG PagesToGo;
  82. ULONG MoveLength;
  83. ULONG LengthToGo;
  84. NTSTATUS Status;
  85. ULONG OriginalLength = Length;
  86. PETHREAD Thread = PsGetCurrentThread();
  87. ULONG GotAMiss = 0;
  88. DebugTrace(+1, me, "CcCopyRead\n", 0 );
  89. MmSavePageFaultReadAhead( Thread, &SavedState );
  90. //
  91. // Get pointer to shared and private cache maps
  92. //
  93. SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
  94. PrivateCacheMap = FileObject->PrivateCacheMap;
  95. //
  96. // Check for read past file size, the caller must filter this case out.
  97. //
  98. ASSERT( ( FileOffset->QuadPart + (LONGLONG)Length) <= SharedCacheMap->FileSize.QuadPart );
  99. //
  100. // If read ahead is enabled, then do the read ahead here so it
  101. // overlaps with the copy (otherwise we will do it below).
  102. // Note that we are assuming that we will not get ahead of our
  103. // current transfer - if read ahead is working it should either
  104. // already be in memory or else underway.
  105. //
  106. if (PrivateCacheMap->Flags.ReadAheadEnabled && (PrivateCacheMap->ReadAheadLength[1] == 0)) {
  107. CcScheduleReadAhead( FileObject, FileOffset, Length );
  108. }
  109. FOffset = *FileOffset;
  110. //
  111. // Increment performance counters
  112. //
  113. if (Wait) {
  114. HOT_STATISTIC(CcCopyReadWait) += 1;
  115. //
  116. // This is not an exact solution, but when IoPageRead gets a miss,
  117. // it cannot tell whether it was CcCopyRead or CcMdlRead, but since
  118. // the miss should occur very soon, by loading the pointer here
  119. // probably the right counter will get incremented, and in any case,
  120. // we hope the errrors average out!
  121. //
  122. CcMissCounter = &CcCopyReadWaitMiss;
  123. } else {
  124. HOT_STATISTIC(CcCopyReadNoWait) += 1;
  125. }
  126. //
  127. // See if we have an active Vacb, that we can just copy to.
  128. //
  129. GetActiveVacb( SharedCacheMap, OldIrql, ActiveVacb, ActivePage, PageIsDirty );
  130. if (ActiveVacb != NULL) {
  131. if ((ULONG)(FOffset.QuadPart >> VACB_OFFSET_SHIFT) == (ActivePage >> (VACB_OFFSET_SHIFT - PAGE_SHIFT))) {
  132. ULONG LengthToCopy = VACB_MAPPING_GRANULARITY - (FOffset.LowPart & (VACB_MAPPING_GRANULARITY - 1));
  133. if (SharedCacheMap->NeedToZero != NULL) {
  134. CcFreeActiveVacb( SharedCacheMap, NULL, 0, FALSE );
  135. }
  136. //
  137. // Get the starting point in the view.
  138. //
  139. CacheBuffer = (PVOID)((PCHAR)ActiveVacb->BaseAddress +
  140. (FOffset.LowPart & (VACB_MAPPING_GRANULARITY - 1)));
  141. //
  142. // Reduce LengthToCopy if it is greater than our caller's length.
  143. //
  144. if (LengthToCopy > Length) {
  145. LengthToCopy = Length;
  146. }
  147. //
  148. // Like the logic for the normal case below, we want to spin around
  149. // making sure Mm only reads the pages we will need.
  150. //
  151. PagesToGo = ADDRESS_AND_SIZE_TO_SPAN_PAGES( CacheBuffer,
  152. LengthToCopy ) - 1;
  153. //
  154. // Copy the data to the user buffer.
  155. //
  156. try {
  157. if (PagesToGo != 0) {
  158. LengthToGo = LengthToCopy;
  159. while (LengthToGo != 0) {
  160. MoveLength = (ULONG)((PCHAR)(ROUND_TO_PAGES(((PCHAR)CacheBuffer + 1))) -
  161. (PCHAR)CacheBuffer);
  162. if (MoveLength > LengthToGo) {
  163. MoveLength = LengthToGo;
  164. }
  165. //
  166. // Here's hoping that it is cheaper to call Mm to see if
  167. // the page is valid. If not let Mm know how many pages
  168. // we are after before doing the move.
  169. //
  170. MmSetPageFaultReadAhead( Thread, PagesToGo );
  171. GotAMiss |= !MmCheckCachedPageState( CacheBuffer, FALSE );
  172. RtlCopyBytes( Buffer, CacheBuffer, MoveLength );
  173. PagesToGo -= 1;
  174. LengthToGo -= MoveLength;
  175. Buffer = (PCHAR)Buffer + MoveLength;
  176. CacheBuffer = (PCHAR)CacheBuffer + MoveLength;
  177. }
  178. //
  179. // Handle the read here that stays on a single page.
  180. //
  181. } else {
  182. //
  183. // Here's hoping that it is cheaper to call Mm to see if
  184. // the page is valid. If not let Mm know how many pages
  185. // we are after before doing the move.
  186. //
  187. MmSetPageFaultReadAhead( Thread, 0 );
  188. GotAMiss |= !MmCheckCachedPageState( CacheBuffer, FALSE );
  189. RtlCopyBytes( Buffer, CacheBuffer, LengthToCopy );
  190. Buffer = (PCHAR)Buffer + LengthToCopy;
  191. }
  192. } except( CcCopyReadExceptionFilter( GetExceptionInformation(),
  193. &Status ) ) {
  194. MmResetPageFaultReadAhead( Thread, SavedState );
  195. SetActiveVacb( SharedCacheMap, OldIrql, ActiveVacb, ActivePage, PageIsDirty );
  196. //
  197. // If we got an access violation, then the user buffer went
  198. // away. Otherwise we must have gotten an I/O error trying
  199. // to bring the data in.
  200. //
  201. if (Status == STATUS_ACCESS_VIOLATION) {
  202. ExRaiseStatus( STATUS_INVALID_USER_BUFFER );
  203. }
  204. else {
  205. ExRaiseStatus( FsRtlNormalizeNtstatus( Status,
  206. STATUS_UNEXPECTED_IO_ERROR ));
  207. }
  208. }
  209. //
  210. // Now adjust FOffset and Length by what we copied.
  211. //
  212. FOffset.QuadPart = FOffset.QuadPart + (LONGLONG)LengthToCopy;
  213. Length -= LengthToCopy;
  214. }
  215. //
  216. // If that was all the data, then remember the Vacb
  217. //
  218. if (Length == 0) {
  219. SetActiveVacb( SharedCacheMap, OldIrql, ActiveVacb, ActivePage, PageIsDirty );
  220. //
  221. // Otherwise we must free it because we will map other vacbs below.
  222. //
  223. } else {
  224. CcFreeActiveVacb( SharedCacheMap, ActiveVacb, ActivePage, PageIsDirty );
  225. }
  226. }
  227. //
  228. // Not all of the transfer will come back at once, so we have to loop
  229. // until the entire transfer is complete.
  230. //
  231. while (Length != 0) {
  232. ULONG ReceivedLength;
  233. LARGE_INTEGER BeyondLastByte;
  234. //
  235. // Call local routine to Map or Access the file data, then move the data,
  236. // then call another local routine to free the data. If we cannot map
  237. // the data because of a Wait condition, return FALSE.
  238. //
  239. // Note that this call may result in an exception, however, if it
  240. // does no Bcb is returned and this routine has absolutely no
  241. // cleanup to perform. Therefore, we do not have a try-finally
  242. // and we allow the possibility that we will simply be unwound
  243. // without notice.
  244. //
  245. if (Wait) {
  246. CacheBuffer = CcGetVirtualAddress( SharedCacheMap,
  247. FOffset,
  248. &Vacb,
  249. &ReceivedLength );
  250. BeyondLastByte.QuadPart = FOffset.QuadPart + (LONGLONG)ReceivedLength;
  251. } else if (!CcPinFileData( FileObject,
  252. &FOffset,
  253. Length,
  254. TRUE,
  255. FALSE,
  256. FALSE,
  257. &Bcb,
  258. &CacheBuffer,
  259. &BeyondLastByte )) {
  260. DebugTrace(-1, me, "CcCopyRead -> FALSE\n", 0 );
  261. HOT_STATISTIC(CcCopyReadNoWaitMiss) += 1;
  262. //
  263. // Enable ReadAhead if we missed.
  264. //
  265. if (!FlagOn( FileObject->Flags, FO_RANDOM_ACCESS ) &&
  266. !PrivateCacheMap->Flags.ReadAheadEnabled) {
  267. CC_SET_PRIVATE_CACHE_MAP (PrivateCacheMap, PRIVATE_CACHE_MAP_READ_AHEAD_ENABLED);
  268. }
  269. return FALSE;
  270. } else {
  271. //
  272. // Calculate how much data is described by Bcb starting at our desired
  273. // file offset.
  274. //
  275. ReceivedLength = (ULONG)(BeyondLastByte.QuadPart - FOffset.QuadPart);
  276. }
  277. //
  278. // If we got more than we need, make sure to only transfer
  279. // the right amount.
  280. //
  281. if (ReceivedLength > Length) {
  282. ReceivedLength = Length;
  283. }
  284. //
  285. // It is possible for the user buffer to become no longer accessible
  286. // since it was last checked by the I/O system. If we fail to access
  287. // the buffer we must raise a status that the caller's exception
  288. // filter considers as "expected". Also we unmap the Bcb here, since
  289. // we otherwise would have no other reason to put a try-finally around
  290. // this loop.
  291. //
  292. try {
  293. PagesToGo = ADDRESS_AND_SIZE_TO_SPAN_PAGES( CacheBuffer,
  294. ReceivedLength ) - 1;
  295. //
  296. // We know exactly how much we want to read here, and we do not
  297. // want to read any more in case the caller is doing random access.
  298. // Our read ahead logic takes care of detecting sequential reads,
  299. // and tends to do large asynchronous read aheads. So far we have
  300. // only mapped the data and we have not forced any in. What we
  301. // do now is get into a loop where we copy a page at a time and
  302. // just prior to each move, we tell MM how many additional pages
  303. // we would like to have read in, in the event that we take a
  304. // fault. With this strategy, for cache hits we never make a single
  305. // expensive call to MM to guarantee that the data is in, yet if we
  306. // do take a fault, we are guaranteed to only take one fault because
  307. // we will read all of the data in for the rest of the transfer.
  308. //
  309. // We test first for the multiple page case, to keep the small
  310. // reads faster.
  311. //
  312. if (PagesToGo != 0) {
  313. LengthToGo = ReceivedLength;
  314. while (LengthToGo != 0) {
  315. MoveLength = (ULONG)((PCHAR)(ROUND_TO_PAGES(((PCHAR)CacheBuffer + 1))) -
  316. (PCHAR)CacheBuffer);
  317. if (MoveLength > LengthToGo) {
  318. MoveLength = LengthToGo;
  319. }
  320. //
  321. // Here's hoping that it is cheaper to call Mm to see if
  322. // the page is valid. If not let Mm know how many pages
  323. // we are after before doing the move.
  324. //
  325. MmSetPageFaultReadAhead( Thread, PagesToGo );
  326. GotAMiss |= !MmCheckCachedPageState( CacheBuffer, FALSE );
  327. RtlCopyBytes( Buffer, CacheBuffer, MoveLength );
  328. PagesToGo -= 1;
  329. LengthToGo -= MoveLength;
  330. Buffer = (PCHAR)Buffer + MoveLength;
  331. CacheBuffer = (PCHAR)CacheBuffer + MoveLength;
  332. }
  333. //
  334. // Handle the read here that stays on a single page.
  335. //
  336. } else {
  337. //
  338. // Here's hoping that it is cheaper to call Mm to see if
  339. // the page is valid. If not let Mm know how many pages
  340. // we are after before doing the move.
  341. //
  342. MmSetPageFaultReadAhead( Thread, 0 );
  343. GotAMiss |= !MmCheckCachedPageState( CacheBuffer, FALSE );
  344. RtlCopyBytes( Buffer, CacheBuffer, ReceivedLength );
  345. Buffer = (PCHAR)Buffer + ReceivedLength;
  346. }
  347. }
  348. except( CcCopyReadExceptionFilter( GetExceptionInformation(),
  349. &Status ) ) {
  350. CcMissCounter = &CcThrowAway;
  351. //
  352. // If we get an exception, then we have to renable page fault
  353. // clustering and unmap on the way out.
  354. //
  355. MmResetPageFaultReadAhead( Thread, SavedState );
  356. if (Wait) {
  357. CcFreeVirtualAddress( Vacb );
  358. } else {
  359. CcUnpinFileData( Bcb, TRUE, UNPIN );
  360. }
  361. //
  362. // If we got an access violation, then the user buffer went
  363. // away. Otherwise we must have gotten an I/O error trying
  364. // to bring the data in.
  365. //
  366. if (Status == STATUS_ACCESS_VIOLATION) {
  367. ExRaiseStatus( STATUS_INVALID_USER_BUFFER );
  368. }
  369. else {
  370. ExRaiseStatus( FsRtlNormalizeNtstatus( Status,
  371. STATUS_UNEXPECTED_IO_ERROR ));
  372. }
  373. }
  374. //
  375. // Update number of bytes transferred.
  376. //
  377. Length -= ReceivedLength;
  378. //
  379. // Unmap the data now, and calculate length left to transfer.
  380. //
  381. if (Wait) {
  382. //
  383. // If there is more to go, just free this vacb.
  384. //
  385. if (Length != 0) {
  386. CcFreeVirtualAddress( Vacb );
  387. //
  388. // Otherwise save it for the next time through.
  389. //
  390. } else {
  391. SetActiveVacb( SharedCacheMap, OldIrql, Vacb, (ULONG)(FOffset.QuadPart >> PAGE_SHIFT), 0 );
  392. break;
  393. }
  394. } else {
  395. CcUnpinFileData( Bcb, TRUE, UNPIN );
  396. }
  397. //
  398. // Assume we did not get all the data we wanted, and set FOffset
  399. // to the end of the returned data.
  400. //
  401. FOffset = BeyondLastByte;
  402. }
  403. MmResetPageFaultReadAhead( Thread, SavedState );
  404. CcMissCounter = &CcThrowAway;
  405. //
  406. // Now enable read ahead if it looks like we got any misses, and do
  407. // the first one.
  408. //
  409. if (GotAMiss &&
  410. !FlagOn( FileObject->Flags, FO_RANDOM_ACCESS ) &&
  411. !PrivateCacheMap->Flags.ReadAheadEnabled) {
  412. CC_SET_PRIVATE_CACHE_MAP (PrivateCacheMap, PRIVATE_CACHE_MAP_READ_AHEAD_ENABLED);
  413. CcScheduleReadAhead( FileObject, FileOffset, OriginalLength );
  414. }
  415. //
  416. // Now that we have described our desired read ahead, let's
  417. // shift the read history down.
  418. //
  419. PrivateCacheMap->FileOffset1 = PrivateCacheMap->FileOffset2;
  420. PrivateCacheMap->BeyondLastByte1 = PrivateCacheMap->BeyondLastByte2;
  421. PrivateCacheMap->FileOffset2 = *FileOffset;
  422. PrivateCacheMap->BeyondLastByte2.QuadPart =
  423. FileOffset->QuadPart + (LONGLONG)OriginalLength;
  424. IoStatus->Status = STATUS_SUCCESS;
  425. IoStatus->Information = OriginalLength;
  426. DebugTrace(-1, me, "CcCopyRead -> TRUE\n", 0 );
  427. return TRUE;
  428. }
  429. VOID
  430. CcFastCopyRead (
  431. IN PFILE_OBJECT FileObject,
  432. IN ULONG FileOffset,
  433. IN ULONG Length,
  434. IN ULONG PageCount,
  435. OUT PVOID Buffer,
  436. OUT PIO_STATUS_BLOCK IoStatus
  437. )
  438. /*++
  439. Routine Description:
  440. This routine attempts to copy the specified file data from the cache
  441. into the output buffer, and deliver the correct I/O status.
  442. This is a faster version of CcCopyRead which only supports 32-bit file
  443. offsets and synchronicity (Wait = TRUE).
  444. Arguments:
  445. FileObject - Pointer to the file object for a file which was
  446. opened with NO_INTERMEDIATE_BUFFERING clear, i.e., for
  447. which CcInitializeCacheMap was called by the file system.
  448. FileOffset - Byte offset in file for desired data.
  449. Length - Length of desired data in bytes.
  450. PageCount - Number of pages spanned by the read.
  451. Buffer - Pointer to output buffer to which data should be copied.
  452. IoStatus - Pointer to standard I/O status block to receive the status
  453. for the transfer. (STATUS_SUCCESS guaranteed for cache
  454. hits, otherwise the actual I/O status is returned.)
  455. Note that even if FALSE is returned, the IoStatus.Information
  456. field will return the count of any bytes successfully
  457. transferred before a blocking condition occured. The caller
  458. may either choose to ignore this information, or resume
  459. the copy later accounting for bytes transferred.
  460. Return Value:
  461. None
  462. --*/
  463. {
  464. PSHARED_CACHE_MAP SharedCacheMap;
  465. PPRIVATE_CACHE_MAP PrivateCacheMap;
  466. PVOID CacheBuffer;
  467. LARGE_INTEGER FOffset;
  468. PVACB Vacb;
  469. PVACB ActiveVacb;
  470. ULONG ActivePage;
  471. ULONG PageIsDirty;
  472. ULONG SavedState;
  473. ULONG PagesToGo;
  474. ULONG MoveLength;
  475. ULONG LengthToGo;
  476. NTSTATUS Status;
  477. LARGE_INTEGER OriginalOffset;
  478. ULONG OriginalLength = Length;
  479. PETHREAD Thread = PsGetCurrentThread();
  480. ULONG GotAMiss = 0;
  481. UNREFERENCED_PARAMETER (PageCount);
  482. DebugTrace(+1, me, "CcFastCopyRead\n", 0 );
  483. MmSavePageFaultReadAhead( Thread, &SavedState );
  484. //
  485. // Get pointer to shared and private cache maps
  486. //
  487. SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
  488. PrivateCacheMap = FileObject->PrivateCacheMap;
  489. //
  490. // Check for read past file size, the caller must filter this case out.
  491. //
  492. ASSERT( (FileOffset + Length) <= SharedCacheMap->FileSize.LowPart );
  493. //
  494. // If read ahead is enabled, then do the read ahead here so it
  495. // overlaps with the copy (otherwise we will do it below).
  496. // Note that we are assuming that we will not get ahead of our
  497. // current transfer - if read ahead is working it should either
  498. // already be in memory or else underway.
  499. //
  500. OriginalOffset.LowPart = FileOffset;
  501. OriginalOffset.HighPart = 0;
  502. if (PrivateCacheMap->Flags.ReadAheadEnabled && (PrivateCacheMap->ReadAheadLength[1] == 0)) {
  503. CcScheduleReadAhead( FileObject, &OriginalOffset, Length );
  504. }
  505. //
  506. // This is not an exact solution, but when IoPageRead gets a miss,
  507. // it cannot tell whether it was CcCopyRead or CcMdlRead, but since
  508. // the miss should occur very soon, by loading the pointer here
  509. // probably the right counter will get incremented, and in any case,
  510. // we hope the errrors average out!
  511. //
  512. CcMissCounter = &CcCopyReadWaitMiss;
  513. //
  514. // Increment performance counters
  515. //
  516. HOT_STATISTIC(CcCopyReadWait) += 1;
  517. //
  518. // See if we have an active Vacb, that we can just copy to.
  519. //
  520. GetActiveVacb( SharedCacheMap, OldIrql, ActiveVacb, ActivePage, PageIsDirty );
  521. if (ActiveVacb != NULL) {
  522. if ((FileOffset >> VACB_OFFSET_SHIFT) == (ActivePage >> (VACB_OFFSET_SHIFT - PAGE_SHIFT))) {
  523. ULONG LengthToCopy = VACB_MAPPING_GRANULARITY - (FileOffset & (VACB_MAPPING_GRANULARITY - 1));
  524. if (SharedCacheMap->NeedToZero != NULL) {
  525. CcFreeActiveVacb( SharedCacheMap, NULL, 0, FALSE );
  526. }
  527. //
  528. // Get the starting point in the view.
  529. //
  530. CacheBuffer = (PVOID)((PCHAR)ActiveVacb->BaseAddress +
  531. (FileOffset & (VACB_MAPPING_GRANULARITY - 1)));
  532. //
  533. // Reduce LengthToCopy if it is greater than our caller's length.
  534. //
  535. if (LengthToCopy > Length) {
  536. LengthToCopy = Length;
  537. }
  538. //
  539. // Like the logic for the normal case below, we want to spin around
  540. // making sure Mm only reads the pages we will need.
  541. //
  542. PagesToGo = ADDRESS_AND_SIZE_TO_SPAN_PAGES( CacheBuffer,
  543. LengthToCopy ) - 1;
  544. //
  545. // Copy the data to the user buffer.
  546. //
  547. try {
  548. if (PagesToGo != 0) {
  549. LengthToGo = LengthToCopy;
  550. while (LengthToGo != 0) {
  551. MoveLength = (ULONG)((PCHAR)(ROUND_TO_PAGES(((PCHAR)CacheBuffer + 1))) -
  552. (PCHAR)CacheBuffer);
  553. if (MoveLength > LengthToGo) {
  554. MoveLength = LengthToGo;
  555. }
  556. //
  557. // Here's hoping that it is cheaper to call Mm to see if
  558. // the page is valid. If not let Mm know how many pages
  559. // we are after before doing the move.
  560. //
  561. MmSetPageFaultReadAhead( Thread, PagesToGo );
  562. GotAMiss |= !MmCheckCachedPageState( CacheBuffer, FALSE );
  563. RtlCopyBytes( Buffer, CacheBuffer, MoveLength );
  564. PagesToGo -= 1;
  565. LengthToGo -= MoveLength;
  566. Buffer = (PCHAR)Buffer + MoveLength;
  567. CacheBuffer = (PCHAR)CacheBuffer + MoveLength;
  568. }
  569. //
  570. // Handle the read here that stays on a single page.
  571. //
  572. } else {
  573. //
  574. // Here's hoping that it is cheaper to call Mm to see if
  575. // the page is valid. If not let Mm know how many pages
  576. // we are after before doing the move.
  577. //
  578. MmSetPageFaultReadAhead( Thread, 0 );
  579. GotAMiss |= !MmCheckCachedPageState( CacheBuffer, FALSE );
  580. RtlCopyBytes( Buffer, CacheBuffer, LengthToCopy );
  581. Buffer = (PCHAR)Buffer + LengthToCopy;
  582. }
  583. } except( CcCopyReadExceptionFilter( GetExceptionInformation(),
  584. &Status ) ) {
  585. MmResetPageFaultReadAhead( Thread, SavedState );
  586. SetActiveVacb( SharedCacheMap, OldIrql, ActiveVacb, ActivePage, PageIsDirty );
  587. //
  588. // If we got an access violation, then the user buffer went
  589. // away. Otherwise we must have gotten an I/O error trying
  590. // to bring the data in.
  591. //
  592. if (Status == STATUS_ACCESS_VIOLATION) {
  593. ExRaiseStatus( STATUS_INVALID_USER_BUFFER );
  594. }
  595. else {
  596. ExRaiseStatus( FsRtlNormalizeNtstatus( Status,
  597. STATUS_UNEXPECTED_IO_ERROR ));
  598. }
  599. }
  600. //
  601. // Now adjust FileOffset and Length by what we copied.
  602. //
  603. FileOffset += LengthToCopy;
  604. Length -= LengthToCopy;
  605. }
  606. //
  607. // If that was all the data, then remember the Vacb
  608. //
  609. if (Length == 0) {
  610. SetActiveVacb( SharedCacheMap, OldIrql, ActiveVacb, ActivePage, PageIsDirty );
  611. //
  612. // Otherwise we must free it because we will map other vacbs below.
  613. //
  614. } else {
  615. CcFreeActiveVacb( SharedCacheMap, ActiveVacb, ActivePage, PageIsDirty );
  616. }
  617. }
  618. //
  619. // Not all of the transfer will come back at once, so we have to loop
  620. // until the entire transfer is complete.
  621. //
  622. FOffset.HighPart = 0;
  623. FOffset.LowPart = FileOffset;
  624. while (Length != 0) {
  625. ULONG ReceivedLength;
  626. ULONG BeyondLastByte;
  627. //
  628. // Call local routine to Map or Access the file data, then move the data,
  629. // then call another local routine to free the data. If we cannot map
  630. // the data because of a Wait condition, return FALSE.
  631. //
  632. // Note that this call may result in an exception, however, if it
  633. // does no Bcb is returned and this routine has absolutely no
  634. // cleanup to perform. Therefore, we do not have a try-finally
  635. // and we allow the possibility that we will simply be unwound
  636. // without notice.
  637. //
  638. CacheBuffer = CcGetVirtualAddress( SharedCacheMap,
  639. FOffset,
  640. &Vacb,
  641. &ReceivedLength );
  642. BeyondLastByte = FOffset.LowPart + ReceivedLength;
  643. //
  644. // If we got more than we need, make sure to only transfer
  645. // the right amount.
  646. //
  647. if (ReceivedLength > Length) {
  648. ReceivedLength = Length;
  649. }
  650. //
  651. // It is possible for the user buffer to become no longer accessible
  652. // since it was last checked by the I/O system. If we fail to access
  653. // the buffer we must raise a status that the caller's exception
  654. // filter considers as "expected". Also we unmap the Bcb here, since
  655. // we otherwise would have no other reason to put a try-finally around
  656. // this loop.
  657. //
  658. try {
  659. PagesToGo = ADDRESS_AND_SIZE_TO_SPAN_PAGES( CacheBuffer,
  660. ReceivedLength ) - 1;
  661. //
  662. // We know exactly how much we want to read here, and we do not
  663. // want to read any more in case the caller is doing random access.
  664. // Our read ahead logic takes care of detecting sequential reads,
  665. // and tends to do large asynchronous read aheads. So far we have
  666. // only mapped the data and we have not forced any in. What we
  667. // do now is get into a loop where we copy a page at a time and
  668. // just prior to each move, we tell MM how many additional pages
  669. // we would like to have read in, in the event that we take a
  670. // fault. With this strategy, for cache hits we never make a single
  671. // expensive call to MM to guarantee that the data is in, yet if we
  672. // do take a fault, we are guaranteed to only take one fault because
  673. // we will read all of the data in for the rest of the transfer.
  674. //
  675. // We test first for the multiple page case, to keep the small
  676. // reads faster.
  677. //
  678. if (PagesToGo != 0) {
  679. LengthToGo = ReceivedLength;
  680. while (LengthToGo != 0) {
  681. MoveLength = (ULONG)((PCHAR)(ROUND_TO_PAGES(((PCHAR)CacheBuffer + 1))) -
  682. (PCHAR)CacheBuffer);
  683. if (MoveLength > LengthToGo) {
  684. MoveLength = LengthToGo;
  685. }
  686. //
  687. // Here's hoping that it is cheaper to call Mm to see if
  688. // the page is valid. If not let Mm know how many pages
  689. // we are after before doing the move.
  690. //
  691. MmSetPageFaultReadAhead( Thread, PagesToGo );
  692. GotAMiss |= !MmCheckCachedPageState( CacheBuffer, FALSE );
  693. RtlCopyBytes( Buffer, CacheBuffer, MoveLength );
  694. PagesToGo -= 1;
  695. LengthToGo -= MoveLength;
  696. Buffer = (PCHAR)Buffer + MoveLength;
  697. CacheBuffer = (PCHAR)CacheBuffer + MoveLength;
  698. }
  699. //
  700. // Handle the read here that stays on a single page.
  701. //
  702. } else {
  703. //
  704. // Here's hoping that it is cheaper to call Mm to see if
  705. // the page is valid. If not let Mm know how many pages
  706. // we are after before doing the move.
  707. //
  708. MmSetPageFaultReadAhead( Thread, 0 );
  709. GotAMiss |= !MmCheckCachedPageState( CacheBuffer, FALSE );
  710. RtlCopyBytes( Buffer, CacheBuffer, ReceivedLength );
  711. Buffer = (PCHAR)Buffer + ReceivedLength;
  712. }
  713. }
  714. except( CcCopyReadExceptionFilter( GetExceptionInformation(),
  715. &Status ) ) {
  716. CcMissCounter = &CcThrowAway;
  717. //
  718. // If we get an exception, then we have to renable page fault
  719. // clustering and unmap on the way out.
  720. //
  721. MmResetPageFaultReadAhead( Thread, SavedState );
  722. CcFreeVirtualAddress( Vacb );
  723. //
  724. // If we got an access violation, then the user buffer went
  725. // away. Otherwise we must have gotten an I/O error trying
  726. // to bring the data in.
  727. //
  728. if (Status == STATUS_ACCESS_VIOLATION) {
  729. ExRaiseStatus( STATUS_INVALID_USER_BUFFER );
  730. }
  731. else {
  732. ExRaiseStatus( FsRtlNormalizeNtstatus( Status,
  733. STATUS_UNEXPECTED_IO_ERROR ));
  734. }
  735. }
  736. //
  737. // Update number of bytes transferred.
  738. //
  739. Length -= ReceivedLength;
  740. //
  741. // Unmap the data now, and calculate length left to transfer.
  742. //
  743. if (Length != 0) {
  744. //
  745. // If there is more to go, just free this vacb.
  746. //
  747. CcFreeVirtualAddress( Vacb );
  748. } else {
  749. //
  750. // Otherwise save it for the next time through.
  751. //
  752. SetActiveVacb( SharedCacheMap, OldIrql, Vacb, (FOffset.LowPart >> PAGE_SHIFT), 0 );
  753. break;
  754. }
  755. //
  756. // Assume we did not get all the data we wanted, and set FOffset
  757. // to the end of the returned data.
  758. //
  759. FOffset.LowPart = BeyondLastByte;
  760. }
  761. MmResetPageFaultReadAhead( Thread, SavedState );
  762. CcMissCounter = &CcThrowAway;
  763. //
  764. // Now enable read ahead if it looks like we got any misses, and do
  765. // the first one.
  766. //
  767. if (GotAMiss &&
  768. !FlagOn( FileObject->Flags, FO_RANDOM_ACCESS ) &&
  769. !PrivateCacheMap->Flags.ReadAheadEnabled) {
  770. CC_SET_PRIVATE_CACHE_MAP (PrivateCacheMap, PRIVATE_CACHE_MAP_READ_AHEAD_ENABLED);
  771. CcScheduleReadAhead( FileObject, &OriginalOffset, OriginalLength );
  772. }
  773. //
  774. // Now that we have described our desired read ahead, let's
  775. // shift the read history down.
  776. //
  777. PrivateCacheMap->FileOffset1.LowPart = PrivateCacheMap->FileOffset2.LowPart;
  778. PrivateCacheMap->BeyondLastByte1.LowPart = PrivateCacheMap->BeyondLastByte2.LowPart;
  779. PrivateCacheMap->FileOffset2.LowPart = OriginalOffset.LowPart;
  780. PrivateCacheMap->BeyondLastByte2.LowPart = OriginalOffset.LowPart + OriginalLength;
  781. IoStatus->Status = STATUS_SUCCESS;
  782. IoStatus->Information = OriginalLength;
  783. DebugTrace(-1, me, "CcFastCopyRead -> VOID\n", 0 );
  784. }
  785. BOOLEAN
  786. CcCopyWrite (
  787. IN PFILE_OBJECT FileObject,
  788. IN PLARGE_INTEGER FileOffset,
  789. IN ULONG Length,
  790. IN BOOLEAN Wait,
  791. IN PVOID Buffer
  792. )
  793. /*++
  794. Routine Description:
  795. This routine attempts to copy the specified file data from the specified
  796. buffer into the Cache, and deliver the correct I/O status. It is *not*
  797. safe to call this routine from Dpc level.
  798. If the caller does not want to block (such as for disk I/O), then
  799. Wait should be supplied as FALSE. If Wait was supplied as FALSE and
  800. it is currently impossible to receive all of the requested data without
  801. blocking, then this routine will return FALSE. However, if the
  802. correct space is immediately accessible in the cache and no blocking is
  803. required, this routine copies the data and returns TRUE.
  804. If the caller supplies Wait as TRUE, then this routine is guaranteed
  805. to copy the data and return TRUE. If the correct space is immediately
  806. accessible in the cache, then no blocking will occur. Otherwise,
  807. the necessary work will be initiated to read and/or free cache data,
  808. and the caller will be blocked until the data can be received.
  809. File system Fsd's should typically supply Wait = TRUE if they are
  810. processing a synchronous I/O requests, or Wait = FALSE if they are
  811. processing an asynchronous request.
  812. File system or Server Fsp threads should supply Wait = TRUE.
  813. Arguments:
  814. FileObject - Pointer to the file object for a file which was
  815. opened with NO_INTERMEDIATE_BUFFERING clear, i.e., for
  816. which CcInitializeCacheMap was called by the file system.
  817. FileOffset - Byte offset in file to receive the data.
  818. Length - Length of data in bytes.
  819. Wait - FALSE if caller may not block, TRUE otherwise (see description
  820. above)
  821. Buffer - Pointer to input buffer from which data should be copied.
  822. Return Value:
  823. FALSE - if Wait was supplied as FALSE and the data was not copied.
  824. TRUE - if the data has been copied.
  825. Raises:
  826. STATUS_INSUFFICIENT_RESOURCES - If a pool allocation failure occurs.
  827. This can only occur if Wait was specified as TRUE. (If Wait is
  828. specified as FALSE, and an allocation failure occurs, this
  829. routine simply returns FALSE.)
  830. --*/
  831. {
  832. PSHARED_CACHE_MAP SharedCacheMap;
  833. PFSRTL_ADVANCED_FCB_HEADER FcbHeader;
  834. PVACB ActiveVacb;
  835. ULONG ActivePage;
  836. PVOID ActiveAddress;
  837. ULONG PageIsDirty;
  838. KIRQL OldIrql;
  839. NTSTATUS Status;
  840. PVOID CacheBuffer;
  841. LARGE_INTEGER FOffset;
  842. PBCB Bcb;
  843. ULONG ZeroFlags;
  844. LARGE_INTEGER Temp;
  845. DebugTrace(+1, me, "CcCopyWrite\n", 0 );
  846. //
  847. // If the caller specified Wait == FALSE, but the FileObject is WriteThrough,
  848. // then we need to just get out.
  849. //
  850. if ((FileObject->Flags & FO_WRITE_THROUGH) && !Wait) {
  851. DebugTrace(-1, me, "CcCopyWrite->FALSE (WriteThrough && !Wait)\n", 0 );
  852. return FALSE;
  853. }
  854. //
  855. // Get pointer to shared cache map
  856. //
  857. SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
  858. FOffset = *FileOffset;
  859. //
  860. // See if we have an active Vacb, that we can just copy to.
  861. //
  862. GetActiveVacb( SharedCacheMap, OldIrql, ActiveVacb, ActivePage, PageIsDirty );
  863. if (ActiveVacb != NULL) {
  864. //
  865. // See if the request starts in the ActivePage. WriteThrough requests must
  866. // go the longer route through CcMapAndCopy, where WriteThrough flushes are
  867. // implemented.
  868. //
  869. if (((ULONG)(FOffset.QuadPart >> PAGE_SHIFT) == ActivePage) && (Length != 0) &&
  870. !FlagOn( FileObject->Flags, FO_WRITE_THROUGH )) {
  871. ULONG LengthToCopy = PAGE_SIZE - (FOffset.LowPart & (PAGE_SIZE - 1));
  872. //
  873. // Reduce LengthToCopy if it is greater than our caller's length.
  874. //
  875. if (LengthToCopy > Length) {
  876. LengthToCopy = Length;
  877. }
  878. //
  879. // Copy the data to the user buffer.
  880. //
  881. try {
  882. //
  883. // If we are copying to a page that is locked down, then
  884. // we have to do it under our spinlock, and update the
  885. // NeedToZero field.
  886. //
  887. OldIrql = 0xFF;
  888. CacheBuffer = (PVOID)((PCHAR)ActiveVacb->BaseAddress +
  889. (FOffset.LowPart & (VACB_MAPPING_GRANULARITY - 1)));
  890. if (SharedCacheMap->NeedToZero != NULL) {
  891. //
  892. // The FastLock may not write our "flag".
  893. //
  894. OldIrql = 0;
  895. ExAcquireFastLock( &SharedCacheMap->ActiveVacbSpinLock, &OldIrql );
  896. //
  897. // Note that the NeedToZero could be cleared, since we
  898. // tested it without the spinlock.
  899. //
  900. ActiveAddress = SharedCacheMap->NeedToZero;
  901. if ((ActiveAddress != NULL) &&
  902. (ActiveVacb == SharedCacheMap->NeedToZeroVacb) &&
  903. (((PCHAR)CacheBuffer + LengthToCopy) > (PCHAR)ActiveAddress)) {
  904. //
  905. // If we are skipping some bytes in the page, then we need
  906. // to zero them.
  907. //
  908. if ((PCHAR)CacheBuffer > (PCHAR)ActiveAddress) {
  909. RtlZeroMemory( ActiveAddress, (PCHAR)CacheBuffer - (PCHAR)ActiveAddress );
  910. }
  911. SharedCacheMap->NeedToZero = (PVOID)((PCHAR)CacheBuffer + LengthToCopy);
  912. }
  913. ExReleaseFastLock( &SharedCacheMap->ActiveVacbSpinLock, OldIrql );
  914. }
  915. RtlCopyBytes( CacheBuffer, Buffer, LengthToCopy );
  916. } except( CcCopyReadExceptionFilter( GetExceptionInformation(),
  917. &Status ) ) {
  918. //
  919. // If we failed to overwrite the uninitialized data,
  920. // zero it now (we cannot safely restore NeedToZero).
  921. //
  922. if (OldIrql != 0xFF) {
  923. RtlZeroBytes( CacheBuffer, LengthToCopy );
  924. }
  925. SetActiveVacb( SharedCacheMap, OldIrql, ActiveVacb, ActivePage, ACTIVE_PAGE_IS_DIRTY );
  926. //
  927. // If we got an access violation, then the user buffer went
  928. // away. Otherwise we must have gotten an I/O error trying
  929. // to bring the data in.
  930. //
  931. if (Status == STATUS_ACCESS_VIOLATION) {
  932. ExRaiseStatus( STATUS_INVALID_USER_BUFFER );
  933. }
  934. else {
  935. ExRaiseStatus( FsRtlNormalizeNtstatus( Status,
  936. STATUS_UNEXPECTED_IO_ERROR ));
  937. }
  938. }
  939. //
  940. // Now adjust FOffset and Length by what we copied.
  941. //
  942. Buffer = (PVOID)((PCHAR)Buffer + LengthToCopy);
  943. FOffset.QuadPart = FOffset.QuadPart + (LONGLONG)LengthToCopy;
  944. Length -= LengthToCopy;
  945. //
  946. // If that was all the data, then get outski...
  947. //
  948. if (Length == 0) {
  949. SetActiveVacb( SharedCacheMap, OldIrql, ActiveVacb, ActivePage, ACTIVE_PAGE_IS_DIRTY );
  950. return TRUE;
  951. }
  952. //
  953. // Remember that the page is dirty now.
  954. //
  955. PageIsDirty |= ACTIVE_PAGE_IS_DIRTY;
  956. }
  957. CcFreeActiveVacb( SharedCacheMap, ActiveVacb, ActivePage, PageIsDirty );
  958. //
  959. // Else someone else could have the active page, and may want to zero
  960. // the range we plan to write!
  961. //
  962. } else if (SharedCacheMap->NeedToZero != NULL) {
  963. CcFreeActiveVacb( SharedCacheMap, NULL, 0, FALSE );
  964. }
  965. //
  966. // At this point we can calculate the ZeroFlags.
  967. //
  968. //
  969. // We can always zero middle pages, if any.
  970. //
  971. ZeroFlags = ZERO_MIDDLE_PAGES;
  972. if (((FOffset.LowPart & (PAGE_SIZE - 1)) == 0) &&
  973. (Length >= PAGE_SIZE)) {
  974. ZeroFlags |= ZERO_FIRST_PAGE;
  975. }
  976. if (((FOffset.LowPart + Length) & (PAGE_SIZE - 1)) == 0) {
  977. ZeroFlags |= ZERO_LAST_PAGE;
  978. }
  979. Temp = FOffset;
  980. Temp.LowPart &= ~(PAGE_SIZE -1);
  981. //
  982. // If there is an advanced header, then we can acquire the FastMutex to
  983. // make capturing ValidDataLength atomic. Currently our other file systems
  984. // are either RO or do not really support 64-bits.
  985. //
  986. FcbHeader = (PFSRTL_ADVANCED_FCB_HEADER)FileObject->FsContext;
  987. if (FlagOn(FcbHeader->Flags, FSRTL_FLAG_ADVANCED_HEADER)) {
  988. ExAcquireFastMutex( FcbHeader->FastMutex );
  989. Temp.QuadPart = ((PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext)->ValidDataLength.QuadPart -
  990. Temp.QuadPart;
  991. ExReleaseFastMutex( FcbHeader->FastMutex );
  992. } else {
  993. Temp.QuadPart = ((PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext)->ValidDataLength.QuadPart -
  994. Temp.QuadPart;
  995. }
  996. if (Temp.QuadPart <= 0) {
  997. ZeroFlags |= ZERO_FIRST_PAGE | ZERO_MIDDLE_PAGES | ZERO_LAST_PAGE;
  998. } else if ((Temp.HighPart == 0) && (Temp.LowPart <= PAGE_SIZE)) {
  999. ZeroFlags |= ZERO_MIDDLE_PAGES | ZERO_LAST_PAGE;
  1000. }
  1001. //
  1002. // Call a routine to map and copy the data in Mm and get out.
  1003. //
  1004. if (Wait) {
  1005. CcMapAndCopy( SharedCacheMap,
  1006. Buffer,
  1007. &FOffset,
  1008. Length,
  1009. ZeroFlags,
  1010. FileObject );
  1011. return TRUE;
  1012. }
  1013. //
  1014. // The rest of this routine is the Wait == FALSE case.
  1015. //
  1016. // Not all of the transfer will come back at once, so we have to loop
  1017. // until the entire transfer is complete.
  1018. //
  1019. while (Length != 0) {
  1020. ULONG ReceivedLength;
  1021. LARGE_INTEGER BeyondLastByte;
  1022. if (!CcPinFileData( FileObject,
  1023. &FOffset,
  1024. Length,
  1025. FALSE,
  1026. TRUE,
  1027. FALSE,
  1028. &Bcb,
  1029. &CacheBuffer,
  1030. &BeyondLastByte )) {
  1031. DebugTrace(-1, me, "CcCopyWrite -> FALSE\n", 0 );
  1032. return FALSE;
  1033. } else {
  1034. //
  1035. // Calculate how much data is described by Bcb starting at our desired
  1036. // file offset.
  1037. //
  1038. ReceivedLength = (ULONG)(BeyondLastByte.QuadPart - FOffset.QuadPart);
  1039. //
  1040. // If we got more than we need, make sure to only transfer
  1041. // the right amount.
  1042. //
  1043. if (ReceivedLength > Length) {
  1044. ReceivedLength = Length;
  1045. }
  1046. }
  1047. //
  1048. // It is possible for the user buffer to become no longer accessible
  1049. // since it was last checked by the I/O system. If we fail to access
  1050. // the buffer we must raise a status that the caller's exception
  1051. // filter considers as "expected". Also we unmap the Bcb here, since
  1052. // we otherwise would have no other reason to put a try-finally around
  1053. // this loop.
  1054. //
  1055. try {
  1056. RtlCopyBytes( CacheBuffer, Buffer, ReceivedLength );
  1057. CcSetDirtyPinnedData( Bcb, NULL );
  1058. CcUnpinFileData( Bcb, FALSE, UNPIN );
  1059. }
  1060. except( CcCopyReadExceptionFilter( GetExceptionInformation(),
  1061. &Status ) ) {
  1062. CcUnpinFileData( Bcb, TRUE, UNPIN );
  1063. //
  1064. // If we got an access violation, then the user buffer went
  1065. // away. Otherwise we must have gotten an I/O error trying
  1066. // to bring the data in.
  1067. //
  1068. if (Status == STATUS_ACCESS_VIOLATION) {
  1069. ExRaiseStatus( STATUS_INVALID_USER_BUFFER );
  1070. }
  1071. else {
  1072. ExRaiseStatus(FsRtlNormalizeNtstatus( Status, STATUS_UNEXPECTED_IO_ERROR ));
  1073. }
  1074. }
  1075. //
  1076. // Assume we did not get all the data we wanted, and set FOffset
  1077. // to the end of the returned data and adjust the Buffer and Length.
  1078. //
  1079. FOffset = BeyondLastByte;
  1080. Buffer = (PCHAR)Buffer + ReceivedLength;
  1081. Length -= ReceivedLength;
  1082. }
  1083. DebugTrace(-1, me, "CcCopyWrite -> TRUE\n", 0 );
  1084. return TRUE;
  1085. }
  1086. VOID
  1087. CcFastCopyWrite (
  1088. IN PFILE_OBJECT FileObject,
  1089. IN ULONG FileOffset,
  1090. IN ULONG Length,
  1091. IN PVOID Buffer
  1092. )
  1093. /*++
  1094. Routine Description:
  1095. This routine attempts to copy the specified file data from the specified
  1096. buffer into the Cache, and deliver the correct I/O status.
  1097. This is a faster version of CcCopyWrite which only supports 32-bit file
  1098. offsets and synchronicity (Wait = TRUE) and no Write Through.
  1099. Arguments:
  1100. FileObject - Pointer to the file object for a file which was
  1101. opened with NO_INTERMEDIATE_BUFFERING clear, i.e., for
  1102. which CcInitializeCacheMap was called by the file system.
  1103. FileOffset - Byte offset in file to receive the data.
  1104. Length - Length of data in bytes.
  1105. Buffer - Pointer to input buffer from which data should be copied.
  1106. Return Value:
  1107. None
  1108. Raises:
  1109. STATUS_INSUFFICIENT_RESOURCES - If a pool allocation failure occurs.
  1110. This can only occur if Wait was specified as TRUE. (If Wait is
  1111. specified as FALSE, and an allocation failure occurs, this
  1112. routine simply returns FALSE.)
  1113. --*/
  1114. {
  1115. PSHARED_CACHE_MAP SharedCacheMap;
  1116. PVOID CacheBuffer;
  1117. PVACB ActiveVacb;
  1118. ULONG ActivePage;
  1119. PVOID ActiveAddress;
  1120. ULONG PageIsDirty;
  1121. KIRQL OldIrql;
  1122. NTSTATUS Status;
  1123. ULONG ZeroFlags;
  1124. ULONG ValidDataLength;
  1125. LARGE_INTEGER FOffset;
  1126. DebugTrace(+1, me, "CcFastCopyWrite\n", 0 );
  1127. //
  1128. // Get pointer to shared cache map and a copy of valid data length
  1129. //
  1130. SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
  1131. //
  1132. // See if we have an active Vacb, that we can just copy to.
  1133. //
  1134. GetActiveVacb( SharedCacheMap, OldIrql, ActiveVacb, ActivePage, PageIsDirty );
  1135. if (ActiveVacb != NULL) {
  1136. //
  1137. // See if the request starts in the ActivePage. WriteThrough requests must
  1138. // go the longer route through CcMapAndCopy, where WriteThrough flushes are
  1139. // implemented.
  1140. //
  1141. if (((FileOffset >> PAGE_SHIFT) == ActivePage) && (Length != 0) &&
  1142. !FlagOn( FileObject->Flags, FO_WRITE_THROUGH )) {
  1143. ULONG LengthToCopy = PAGE_SIZE - (FileOffset & (PAGE_SIZE - 1));
  1144. //
  1145. // Reduce LengthToCopy if it is greater than our caller's length.
  1146. //
  1147. if (LengthToCopy > Length) {
  1148. LengthToCopy = Length;
  1149. }
  1150. //
  1151. // Copy the data to the user buffer.
  1152. //
  1153. try {
  1154. //
  1155. // If we are copying to a page that is locked down, then
  1156. // we have to do it under our spinlock, and update the
  1157. // NeedToZero field.
  1158. //
  1159. OldIrql = 0xFF;
  1160. CacheBuffer = (PVOID)((PCHAR)ActiveVacb->BaseAddress +
  1161. (FileOffset & (VACB_MAPPING_GRANULARITY - 1)));
  1162. if (SharedCacheMap->NeedToZero != NULL) {
  1163. //
  1164. // The FastLock may not write our "flag".
  1165. //
  1166. OldIrql = 0;
  1167. ExAcquireFastLock( &SharedCacheMap->ActiveVacbSpinLock, &OldIrql );
  1168. //
  1169. // Note that the NeedToZero could be cleared, since we
  1170. // tested it without the spinlock.
  1171. //
  1172. ActiveAddress = SharedCacheMap->NeedToZero;
  1173. if ((ActiveAddress != NULL) &&
  1174. (ActiveVacb == SharedCacheMap->NeedToZeroVacb) &&
  1175. (((PCHAR)CacheBuffer + LengthToCopy) > (PCHAR)ActiveAddress)) {
  1176. //
  1177. // If we are skipping some bytes in the page, then we need
  1178. // to zero them.
  1179. //
  1180. if ((PCHAR)CacheBuffer > (PCHAR)ActiveAddress) {
  1181. RtlZeroMemory( ActiveAddress, (PCHAR)CacheBuffer - (PCHAR)ActiveAddress );
  1182. }
  1183. SharedCacheMap->NeedToZero = (PVOID)((PCHAR)CacheBuffer + LengthToCopy);
  1184. }
  1185. ExReleaseFastLock( &SharedCacheMap->ActiveVacbSpinLock, OldIrql );
  1186. }
  1187. RtlCopyBytes( CacheBuffer, Buffer, LengthToCopy );
  1188. } except( CcCopyReadExceptionFilter( GetExceptionInformation(),
  1189. &Status ) ) {
  1190. //
  1191. // If we failed to overwrite the uninitialized data,
  1192. // zero it now (we cannot safely restore NeedToZero).
  1193. //
  1194. if (OldIrql != 0xFF) {
  1195. RtlZeroBytes( CacheBuffer, LengthToCopy );
  1196. }
  1197. SetActiveVacb( SharedCacheMap, OldIrql, ActiveVacb, ActivePage, ACTIVE_PAGE_IS_DIRTY );
  1198. //
  1199. // If we got an access violation, then the user buffer went
  1200. // away. Otherwise we must have gotten an I/O error trying
  1201. // to bring the data in.
  1202. //
  1203. if (Status == STATUS_ACCESS_VIOLATION) {
  1204. ExRaiseStatus( STATUS_INVALID_USER_BUFFER );
  1205. }
  1206. else {
  1207. ExRaiseStatus( FsRtlNormalizeNtstatus( Status,
  1208. STATUS_UNEXPECTED_IO_ERROR ));
  1209. }
  1210. }
  1211. //
  1212. // Now adjust FileOffset and Length by what we copied.
  1213. //
  1214. Buffer = (PVOID)((PCHAR)Buffer + LengthToCopy);
  1215. FileOffset += LengthToCopy;
  1216. Length -= LengthToCopy;
  1217. //
  1218. // If that was all the data, then get outski...
  1219. //
  1220. if (Length == 0) {
  1221. SetActiveVacb( SharedCacheMap, OldIrql, ActiveVacb, ActivePage, ACTIVE_PAGE_IS_DIRTY );
  1222. return;
  1223. }
  1224. //
  1225. // Remember that the page is dirty now.
  1226. //
  1227. PageIsDirty |= ACTIVE_PAGE_IS_DIRTY;
  1228. }
  1229. CcFreeActiveVacb( SharedCacheMap, ActiveVacb, ActivePage, PageIsDirty );
  1230. //
  1231. // Else someone else could have the active page, and may want to zero
  1232. // the range we plan to write!
  1233. //
  1234. } else if (SharedCacheMap->NeedToZero != NULL) {
  1235. CcFreeActiveVacb( SharedCacheMap, NULL, 0, FALSE );
  1236. }
  1237. //
  1238. // Set up for call to CcMapAndCopy
  1239. //
  1240. FOffset.LowPart = FileOffset;
  1241. FOffset.HighPart = 0;
  1242. ValidDataLength = ((PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext)->ValidDataLength.LowPart;
  1243. ASSERT((ValidDataLength == MAXULONG) ||
  1244. (((PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext)->ValidDataLength.HighPart == 0));
  1245. //
  1246. // At this point we can calculate the ReadOnly flag for
  1247. // the purposes of whether to use the Bcb resource, and
  1248. // we can calculate the ZeroFlags.
  1249. //
  1250. //
  1251. // We can always zero middle pages, if any.
  1252. //
  1253. ZeroFlags = ZERO_MIDDLE_PAGES;
  1254. if (((FileOffset & (PAGE_SIZE - 1)) == 0) &&
  1255. (Length >= PAGE_SIZE)) {
  1256. ZeroFlags |= ZERO_FIRST_PAGE;
  1257. }
  1258. if (((FileOffset + Length) & (PAGE_SIZE - 1)) == 0) {
  1259. ZeroFlags |= ZERO_LAST_PAGE;
  1260. }
  1261. if ((FileOffset & ~(PAGE_SIZE - 1)) >= ValidDataLength) {
  1262. ZeroFlags |= ZERO_FIRST_PAGE | ZERO_MIDDLE_PAGES | ZERO_LAST_PAGE;
  1263. } else if (((FileOffset & ~(PAGE_SIZE - 1)) + PAGE_SIZE) >= ValidDataLength) {
  1264. ZeroFlags |= ZERO_MIDDLE_PAGES | ZERO_LAST_PAGE;
  1265. }
  1266. //
  1267. // Call a routine to map and copy the data in Mm and get out.
  1268. //
  1269. CcMapAndCopy( SharedCacheMap,
  1270. Buffer,
  1271. &FOffset,
  1272. Length,
  1273. ZeroFlags,
  1274. FileObject );
  1275. DebugTrace(-1, me, "CcFastCopyWrite -> VOID\n", 0 );
  1276. }
  1277. LONG
  1278. CcCopyReadExceptionFilter(
  1279. IN PEXCEPTION_POINTERS ExceptionPointer,
  1280. IN PNTSTATUS ExceptionCode
  1281. )
  1282. /*++
  1283. Routine Description:
  1284. This routine serves as a exception filter and has the special job of
  1285. extracting the "real" I/O error when Mm raises STATUS_IN_PAGE_ERROR
  1286. beneath us.
  1287. Arguments:
  1288. ExceptionPointer - A pointer to the exception record that contains
  1289. the real Io Status.
  1290. ExceptionCode - A pointer to an NTSTATUS that is to receive the real
  1291. status.
  1292. Return Value:
  1293. EXCEPTION_EXECUTE_HANDLER
  1294. --*/
  1295. {
  1296. *ExceptionCode = ExceptionPointer->ExceptionRecord->ExceptionCode;
  1297. if ( (*ExceptionCode == STATUS_IN_PAGE_ERROR) &&
  1298. (ExceptionPointer->ExceptionRecord->NumberParameters >= 3) ) {
  1299. *ExceptionCode = (NTSTATUS) ExceptionPointer->ExceptionRecord->ExceptionInformation[2];
  1300. }
  1301. ASSERT( !NT_SUCCESS(*ExceptionCode) );
  1302. return EXCEPTION_EXECUTE_HANDLER;
  1303. }
  1304. BOOLEAN
  1305. CcCanIWrite (
  1306. IN PFILE_OBJECT FileObject,
  1307. IN ULONG BytesToWrite,
  1308. IN BOOLEAN Wait,
  1309. IN UCHAR Retrying
  1310. )
  1311. /*++
  1312. Routine Description:
  1313. This routine tests whether it is ok to do a write to the cache
  1314. or not, according to the Thresholds of dirty bytes and available
  1315. pages. The first time this routine is called for a request (Retrying
  1316. FALSE), we automatically make the new request queue if there are other
  1317. requests in the queue.
  1318. Note that the ListEmpty test is important to prevent small requests from sneaking
  1319. in and starving large requests.
  1320. Arguments:
  1321. FileObject - for the file to be written
  1322. BytesToWrite - Number of bytes caller wishes to write to the Cache.
  1323. Wait - TRUE if the caller owns no resources, and can block inside this routine
  1324. until it is ok to write.
  1325. Retrying - Specified as FALSE when the request is first received, and
  1326. otherwise specified as TRUE if this write has already entered
  1327. the queue. Special non-zero value of MAXUCHAR indicates that
  1328. we were called within the cache manager with a MasterSpinLock held,
  1329. so do not attempt to acquire it here. MAXUCHAR - 1 means we
  1330. were called within the Cache Manager with some other spinlock
  1331. held. MAXUCHAR - 2 means we want to enforce throttling, even if
  1332. the file object is flagged as being of remote origin. For either
  1333. of the first two special values, we do not touch the FsRtl header.
  1334. Return Value:
  1335. TRUE if it is ok to write.
  1336. FALSE if the caller should defer the write via a call to CcDeferWrite.
  1337. --*/
  1338. {
  1339. PSHARED_CACHE_MAP SharedCacheMap;
  1340. KEVENT Event;
  1341. KIRQL OldIrql;
  1342. ULONG PagesToWrite;
  1343. BOOLEAN ExceededPerFileThreshold;
  1344. DEFERRED_WRITE DeferredWrite;
  1345. PSECTION_OBJECT_POINTERS SectionObjectPointers;
  1346. //
  1347. // If this file is writethrough or of remote origin, exempt it from throttling
  1348. // and let it write. We do this under the assumption that it has been throttled
  1349. // at the remote location and we do not want to block it here. If we were called
  1350. // with Retrying set to MAXUCHAR - 2, enforce the throttle regardless of the
  1351. // file object origin (see above).
  1352. //
  1353. if (BooleanFlagOn( FileObject->Flags, FO_WRITE_THROUGH)) {
  1354. return TRUE;
  1355. }
  1356. //
  1357. // Do a special test here for file objects that keep track of dirty
  1358. // pages on a per-file basis. This is used mainly for slow links.
  1359. //
  1360. ExceededPerFileThreshold = FALSE;
  1361. PagesToWrite = ((BytesToWrite < WRITE_CHARGE_THRESHOLD ?
  1362. BytesToWrite : WRITE_CHARGE_THRESHOLD) + (PAGE_SIZE - 1)) / PAGE_SIZE;
  1363. //
  1364. // Don't dereference the FsContext field if we were called while holding
  1365. // a spinlock.
  1366. //
  1367. if ((Retrying >= MAXUCHAR - 1) ||
  1368. FlagOn(((PFSRTL_COMMON_FCB_HEADER)(FileObject->FsContext))->Flags,
  1369. FSRTL_FLAG_LIMIT_MODIFIED_PAGES)) {
  1370. if (Retrying != MAXUCHAR) {
  1371. CcAcquireMasterLock( &OldIrql );
  1372. }
  1373. if (((SectionObjectPointers = FileObject->SectionObjectPointer) != NULL) &&
  1374. ((SharedCacheMap = SectionObjectPointers->SharedCacheMap) != NULL) &&
  1375. (SharedCacheMap->DirtyPageThreshold != 0) &&
  1376. (SharedCacheMap->DirtyPages != 0) &&
  1377. ((PagesToWrite + SharedCacheMap->DirtyPages) >
  1378. SharedCacheMap->DirtyPageThreshold)) {
  1379. ExceededPerFileThreshold = TRUE;
  1380. }
  1381. if (Retrying != MAXUCHAR) {
  1382. CcReleaseMasterLock( OldIrql );
  1383. }
  1384. }
  1385. //
  1386. // See if it is ok to do the write right now
  1387. //
  1388. if ((Retrying || IsListEmpty(&CcDeferredWrites))
  1389. &&
  1390. (CcTotalDirtyPages + PagesToWrite < CcDirtyPageThreshold)
  1391. &&
  1392. MmEnoughMemoryForWrite()
  1393. &&
  1394. !ExceededPerFileThreshold) {
  1395. return TRUE;
  1396. }
  1397. //
  1398. // Otherwise, if our caller is synchronous, we will just wait here.
  1399. //
  1400. if (Wait) {
  1401. if (IsListEmpty(&CcDeferredWrites) ) {
  1402. //
  1403. // Get a write scan to occur NOW
  1404. //
  1405. CcAcquireMasterLock( &OldIrql );
  1406. CcScheduleLazyWriteScan( TRUE );
  1407. CcReleaseMasterLock( OldIrql );
  1408. }
  1409. KeInitializeEvent( &Event, NotificationEvent, FALSE );
  1410. //
  1411. // Fill in the block. Note that we can access the Fsrtl Common Header
  1412. // even if it's paged because Wait will be FALSE if called from
  1413. // within the cache.
  1414. //
  1415. DeferredWrite.NodeTypeCode = CACHE_NTC_DEFERRED_WRITE;
  1416. DeferredWrite.NodeByteSize = sizeof(DEFERRED_WRITE);
  1417. DeferredWrite.FileObject = FileObject;
  1418. DeferredWrite.BytesToWrite = BytesToWrite;
  1419. DeferredWrite.Event = &Event;
  1420. DeferredWrite.LimitModifiedPages = BooleanFlagOn(((PFSRTL_COMMON_FCB_HEADER)(FileObject->FsContext))->Flags,
  1421. FSRTL_FLAG_LIMIT_MODIFIED_PAGES);
  1422. //
  1423. // Now insert at the appropriate end of the list
  1424. //
  1425. if (Retrying) {
  1426. ExInterlockedInsertHeadList( &CcDeferredWrites,
  1427. &DeferredWrite.DeferredWriteLinks,
  1428. &CcDeferredWriteSpinLock );
  1429. } else {
  1430. ExInterlockedInsertTailList( &CcDeferredWrites,
  1431. &DeferredWrite.DeferredWriteLinks,
  1432. &CcDeferredWriteSpinLock );
  1433. }
  1434. while (TRUE) {
  1435. //
  1436. // Now since we really didn't synchronize anything but the insertion,
  1437. // we call the post routine to make sure that in some wierd case we
  1438. // do not leave anyone hanging with no dirty bytes for the Lazy Writer.
  1439. //
  1440. CcPostDeferredWrites();
  1441. //
  1442. // Finally wait until the event is signalled and we can write
  1443. // and return to tell the guy he can write.
  1444. //
  1445. if (KeWaitForSingleObject( &Event,
  1446. Executive,
  1447. KernelMode,
  1448. FALSE,
  1449. &CcIdleDelay ) == STATUS_SUCCESS) {
  1450. return TRUE;
  1451. }
  1452. }
  1453. } else {
  1454. return FALSE;
  1455. }
  1456. }
  1457. VOID
  1458. CcDeferWrite (
  1459. IN PFILE_OBJECT FileObject,
  1460. IN PCC_POST_DEFERRED_WRITE PostRoutine,
  1461. IN PVOID Context1,
  1462. IN PVOID Context2,
  1463. IN ULONG BytesToWrite,
  1464. IN BOOLEAN Retrying
  1465. )
  1466. /*++
  1467. Routine Description:
  1468. This routine may be called to have the Cache Manager defer posting
  1469. of a write until the Lazy Writer makes some progress writing, or
  1470. there are more available pages. A file system would normally call
  1471. this routine after receiving FALSE from CcCanIWrite, and preparing
  1472. the request to be posted.
  1473. Arguments:
  1474. FileObject - for the file to be written
  1475. PostRoutine - Address of the PostRoutine that the Cache Manager can
  1476. call to post the request when conditions are right. Note
  1477. that it is possible that this routine will be called
  1478. immediately from this routine.
  1479. Context1 - First context parameter for the post routine.
  1480. Context2 - Secont parameter for the post routine.
  1481. BytesToWrite - Number of bytes that the request is trying to write
  1482. to the cache.
  1483. Retrying - Supplied as FALSE if the request is being posted for the
  1484. first time, TRUE otherwise.
  1485. Return Value:
  1486. None
  1487. --*/
  1488. {
  1489. PDEFERRED_WRITE DeferredWrite;
  1490. KIRQL OldIrql;
  1491. //
  1492. // Attempt to allocate a deferred write block, and if we do not get
  1493. // one, just post it immediately rather than gobbling up must succeed
  1494. // pool.
  1495. //
  1496. DeferredWrite = ExAllocatePoolWithTag( NonPagedPool, sizeof(DEFERRED_WRITE), 'wDcC' );
  1497. if (DeferredWrite == NULL) {
  1498. (*PostRoutine)( Context1, Context2 );
  1499. return;
  1500. }
  1501. //
  1502. // Fill in the block.
  1503. //
  1504. DeferredWrite->NodeTypeCode = CACHE_NTC_DEFERRED_WRITE;
  1505. DeferredWrite->NodeByteSize = sizeof(DEFERRED_WRITE);
  1506. DeferredWrite->FileObject = FileObject;
  1507. DeferredWrite->BytesToWrite = BytesToWrite;
  1508. DeferredWrite->Event = NULL;
  1509. DeferredWrite->PostRoutine = PostRoutine;
  1510. DeferredWrite->Context1 = Context1;
  1511. DeferredWrite->Context2 = Context2;
  1512. DeferredWrite->LimitModifiedPages = BooleanFlagOn(((PFSRTL_COMMON_FCB_HEADER)(FileObject->FsContext))->Flags,
  1513. FSRTL_FLAG_LIMIT_MODIFIED_PAGES);
  1514. //
  1515. // Now insert at the appropriate end of the list
  1516. //
  1517. if (Retrying) {
  1518. ExInterlockedInsertHeadList( &CcDeferredWrites,
  1519. &DeferredWrite->DeferredWriteLinks,
  1520. &CcDeferredWriteSpinLock );
  1521. } else {
  1522. ExInterlockedInsertTailList( &CcDeferredWrites,
  1523. &DeferredWrite->DeferredWriteLinks,
  1524. &CcDeferredWriteSpinLock );
  1525. }
  1526. //
  1527. // Now since we really didn't synchronize anything but the insertion,
  1528. // we call the post routine to make sure that in some wierd case we
  1529. // do not leave anyone hanging with no dirty bytes for the Lazy Writer.
  1530. //
  1531. CcPostDeferredWrites();
  1532. //
  1533. // Schedule the lazy writer in case the reason we're blocking
  1534. // is that we're waiting for Mm (or some other external flag)
  1535. // to lower and let this write happen. He will be the one to
  1536. // keep coming back and checking if this can proceed, even if
  1537. // there are no cache manager pages to write.
  1538. //
  1539. CcAcquireMasterLock( &OldIrql);
  1540. if (!LazyWriter.ScanActive) {
  1541. CcScheduleLazyWriteScan( FALSE );
  1542. }
  1543. CcReleaseMasterLock( OldIrql);
  1544. }
  1545. VOID
  1546. CcPostDeferredWrites (
  1547. )
  1548. /*++
  1549. Routine Description:
  1550. This routine may be called to see if any deferred writes should be posted
  1551. now, and to post them. It should be called any time the status of the
  1552. queue may have changed, such as when a new entry has been added, or the
  1553. Lazy Writer has finished writing out buffers and set them clean.
  1554. Arguments:
  1555. None
  1556. Return Value:
  1557. None
  1558. --*/
  1559. {
  1560. PDEFERRED_WRITE DeferredWrite;
  1561. ULONG TotalBytesLetLoose = 0;
  1562. KIRQL OldIrql;
  1563. do {
  1564. //
  1565. // Initially clear the deferred write structure pointer
  1566. // and syncrhronize.
  1567. //
  1568. DeferredWrite = NULL;
  1569. ExAcquireSpinLock( &CcDeferredWriteSpinLock, &OldIrql );
  1570. //
  1571. // If the list is empty we are done.
  1572. //
  1573. if (!IsListEmpty(&CcDeferredWrites)) {
  1574. PLIST_ENTRY Entry;
  1575. Entry = CcDeferredWrites.Flink;
  1576. while (Entry != &CcDeferredWrites) {
  1577. DeferredWrite = CONTAINING_RECORD( Entry,
  1578. DEFERRED_WRITE,
  1579. DeferredWriteLinks );
  1580. //
  1581. // Check for a paranoid case here that TotalBytesLetLoose
  1582. // wraps. We stop processing the list at this time.
  1583. //
  1584. TotalBytesLetLoose += DeferredWrite->BytesToWrite;
  1585. if (TotalBytesLetLoose < DeferredWrite->BytesToWrite) {
  1586. DeferredWrite = NULL;
  1587. break;
  1588. }
  1589. //
  1590. // If it is now ok to post this write, remove him from
  1591. // the list.
  1592. //
  1593. if (CcCanIWrite( DeferredWrite->FileObject,
  1594. TotalBytesLetLoose,
  1595. FALSE,
  1596. MAXUCHAR - 1 )) {
  1597. RemoveEntryList( &DeferredWrite->DeferredWriteLinks );
  1598. break;
  1599. //
  1600. // Otherwise, it is time to stop processing the list, so
  1601. // we clear the pointer again unless we throttled this item
  1602. // because of a private dirty page limit.
  1603. //
  1604. } else {
  1605. //
  1606. // If this was a private throttle, skip over it and
  1607. // remove its byte count from the running total.
  1608. //
  1609. if (DeferredWrite->LimitModifiedPages) {
  1610. Entry = Entry->Flink;
  1611. TotalBytesLetLoose -= DeferredWrite->BytesToWrite;
  1612. DeferredWrite = NULL;
  1613. continue;
  1614. } else {
  1615. DeferredWrite = NULL;
  1616. break;
  1617. }
  1618. }
  1619. }
  1620. }
  1621. ExReleaseSpinLock( &CcDeferredWriteSpinLock, OldIrql );
  1622. //
  1623. // If we got something, set the event or call the post routine
  1624. // and deallocate the structure.
  1625. //
  1626. if (DeferredWrite != NULL) {
  1627. if (DeferredWrite->Event != NULL) {
  1628. KeSetEvent( DeferredWrite->Event, 0, FALSE );
  1629. } else {
  1630. (*DeferredWrite->PostRoutine)( DeferredWrite->Context1,
  1631. DeferredWrite->Context2 );
  1632. ExFreePool( DeferredWrite );
  1633. }
  1634. }
  1635. //
  1636. // Loop until we find no more work to do.
  1637. //
  1638. } while (DeferredWrite != NULL);
  1639. }