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.

6686 lines
199 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. cachesub.c
  5. Abstract:
  6. This module implements the common subroutines for the Cache subsystem.
  7. Author:
  8. Tom Miller [TomM] 4-May-1990
  9. Revision History:
  10. --*/
  11. #include "cc.h"
  12. //
  13. // The Bug check file id for this module
  14. //
  15. #define BugCheckFileId (CACHE_BUG_CHECK_CACHESUB)
  16. //
  17. // Define our debug constant
  18. //
  19. #define me 0x00000002
  20. //
  21. // Define those errors which should be retried
  22. //
  23. #define RetryError(STS) (((STS) == STATUS_VERIFY_REQUIRED) || ((STS) == STATUS_FILE_LOCK_CONFLICT))
  24. ULONG CcMaxDirtyWrite = 0x10000;
  25. //
  26. // Local support routines
  27. //
  28. BOOLEAN
  29. CcFindBcb (
  30. IN PSHARED_CACHE_MAP SharedCacheMap,
  31. IN PLARGE_INTEGER FileOffset,
  32. IN OUT PLARGE_INTEGER BeyondLastByte,
  33. OUT PBCB *Bcb
  34. );
  35. PBCB
  36. CcAllocateInitializeBcb (
  37. IN OUT PSHARED_CACHE_MAP SharedCacheMap OPTIONAL,
  38. IN OUT PBCB AfterBcb,
  39. IN PLARGE_INTEGER FileOffset,
  40. IN PLARGE_INTEGER Length
  41. );
  42. NTSTATUS
  43. CcSetValidData (
  44. IN PFILE_OBJECT FileObject,
  45. IN PLARGE_INTEGER ValidDataLength
  46. );
  47. BOOLEAN
  48. CcAcquireByteRangeForWrite (
  49. IN PSHARED_CACHE_MAP SharedCacheMap,
  50. IN PLARGE_INTEGER TargetOffset OPTIONAL,
  51. IN ULONG TargetLength,
  52. OUT PLARGE_INTEGER FileOffset,
  53. OUT PULONG Length,
  54. OUT PBCB *FirstBcb
  55. );
  56. VOID
  57. CcReleaseByteRangeFromWrite (
  58. IN PSHARED_CACHE_MAP SharedCacheMap,
  59. IN PLARGE_INTEGER FileOffset,
  60. IN ULONG Length,
  61. IN PBCB FirstBcb,
  62. IN BOOLEAN VerifyRequired
  63. );
  64. PBITMAP_RANGE
  65. CcFindBitmapRangeToDirty (
  66. IN PMBCB Mbcb,
  67. IN LONGLONG Page,
  68. IN PULONG *FreePageForSetting
  69. );
  70. PBITMAP_RANGE
  71. CcFindBitmapRangeToClean (
  72. IN PMBCB Mbcb,
  73. IN LONGLONG Page
  74. );
  75. BOOLEAN
  76. CcLogError(
  77. IN PFILE_OBJECT FileObject,
  78. IN PUNICODE_STRING FileName,
  79. IN NTSTATUS Error,
  80. IN NTSTATUS DeviceError,
  81. IN UCHAR IrpMajorCode
  82. );
  83. //
  84. // Internal support routine
  85. //
  86. BOOLEAN
  87. CcPinFileData (
  88. IN PFILE_OBJECT FileObject,
  89. IN PLARGE_INTEGER FileOffset,
  90. IN ULONG Length,
  91. IN BOOLEAN ReadOnly,
  92. IN BOOLEAN WriteOnly,
  93. IN ULONG Flags,
  94. OUT PBCB *Bcb,
  95. OUT PVOID *BaseAddress,
  96. OUT PLARGE_INTEGER BeyondLastByte
  97. )
  98. /*++
  99. Routine Description:
  100. This routine locks the specified range of file data into memory.
  101. Note that the data desired by the caller (or the first part of it)
  102. may be in one of three states:
  103. No Bcb exists which describes the data
  104. A Bcb exists describing the data, but it is not mapped
  105. (BcbOut->BaseAddress == NULL)
  106. A Bcb exists describing the data, and it is mapped
  107. Given the above three states, and given that the caller may call
  108. with either Wait == FALSE or Wait == TRUE, this routine has basically
  109. six cases. What has to be done, and the order in which things must be
  110. done varies quite a bit with each of these six cases. The most
  111. straight-forward implementation of this routine, with the least amount
  112. of branching, is achieved by determining which of the six cases applies,
  113. and dispatching fairly directly to that case. The handling of the
  114. cases is summarized in the following table:
  115. Wait == TRUE Wait == FALSE
  116. ------------ -------------
  117. no Bcb Case 1: Case 2:
  118. CcAllocateInitializeBcb CcMapAndRead (exit if FALSE)
  119. Acquire Bcb Exclusive CcAllocateInitializeBcb
  120. Release BcbList SpinLock Acquire Bcb Shared if not ReadOnly
  121. CcMapAndRead w/ Wait Release BcbList SpinLock
  122. Convert/Release Bcb Resource
  123. Bcb not Case 3: Case 4:
  124. mapped
  125. Increment PinCount Acquire Bcb Exclusive (exit if FALSE)
  126. Release BcbList SpinLock CcMapAndRead (exit if FALSE)
  127. Acquire Bcb Excl. w/ Wait Increment PinCount
  128. if still not mapped Convert/Release Bcb Resource
  129. CcMapAndRead w/ Wait Release BcbList SpinLock
  130. Convert/Release Bcb Resource
  131. Bcb mapped Case 5: Case 6:
  132. Increment PinCount if not ReadOnly
  133. Release BcbList SpinLock Acquire Bcb shared (exit if FALSE)
  134. if not ReadOnly Increment PinCount
  135. Acquire Bcb Shared Release BcbList SpinLock
  136. It is important to note that most changes to this routine will affect
  137. multiple cases from above.
  138. Arguments:
  139. FileObject - Pointer to File Object for file
  140. FileOffset - Offset in file at which map should begin
  141. Length - Length of desired map in bytes
  142. ReadOnly - Supplies TRUE if caller will only read the mapped data (i.e.,
  143. TRUE for CcCopyRead, CcMapData and CcMdlRead and FALSE for
  144. everyone else)
  145. WriteOnly - The specified range of bytes will only be written.
  146. Flags - (PIN_WAIT, PIN_EXCLUSIVE, PIN_NO_READ, etc. as defined in cache.h)
  147. Bcb - Returns a pointer to the Bcb representing the pinned data.
  148. BaseAddress - Returns base address of desired data
  149. BeyondLastByte - Returns the File Offset of the first byte beyond the
  150. last accessible byte.
  151. Return Value:
  152. FALSE - if PIN_WAIT was set, and it was impossible to lock all
  153. of the data without blocking
  154. TRUE - if the desired data, is being returned
  155. Raises:
  156. STATUS_INSUFFICIENT_RESOURCES - If a pool allocation failure occurs.
  157. This can only occur if Wait was specified as TRUE. (If Wait is
  158. specified as FALSE, and an allocation failure occurs, this
  159. routine simply returns FALSE.)
  160. --*/
  161. {
  162. PSHARED_CACHE_MAP SharedCacheMap;
  163. LARGE_INTEGER TrialBound;
  164. KLOCK_QUEUE_HANDLE LockHandle;
  165. PBCB BcbOut = NULL;
  166. ULONG ZeroFlags = 0;
  167. LOGICAL SpinLockAcquired = FALSE;
  168. BOOLEAN Result = FALSE;
  169. ULONG ReceivedLength;
  170. ULONG ActivePage;
  171. ULONG PageIsDirty;
  172. PVACB Vacb = NULL;
  173. DebugTrace(+1, me, "CcPinFileData:\n", 0 );
  174. DebugTrace( 0, me, " FileObject = %08lx\n", FileObject );
  175. DebugTrace2(0, me, " FileOffset = %08lx, %08lx\n", FileOffset->LowPart,
  176. FileOffset->HighPart );
  177. DebugTrace( 0, me, " Length = %08lx\n", Length );
  178. DebugTrace( 0, me, " Flags = %02lx\n", Flags );
  179. //
  180. // Get pointer to SharedCacheMap via File Object.
  181. //
  182. SharedCacheMap = *(PSHARED_CACHE_MAP *)((PCHAR)FileObject->SectionObjectPointer
  183. + sizeof(PVOID));
  184. //
  185. // See if we have an active Vacb, that we need to free.
  186. //
  187. GetActiveVacb( SharedCacheMap, OldIrql, Vacb, ActivePage, PageIsDirty );
  188. //
  189. // If there is an end of a page to be zeroed, then free that page now,
  190. // so it does not cause our data to get zeroed. If there is an active
  191. // page, free it so we have the correct ValidDataGoal.
  192. //
  193. if ((Vacb != NULL) || (SharedCacheMap->NeedToZero != NULL)) {
  194. CcFreeActiveVacb( SharedCacheMap, Vacb, ActivePage, PageIsDirty );
  195. Vacb = NULL;
  196. }
  197. //
  198. // Make sure the calling file system is not asking to map beyond the
  199. // end of the section, for example, that it did not forget to do
  200. // CcExtendCacheSection.
  201. //
  202. ASSERT( ( FileOffset->QuadPart + (LONGLONG)Length ) <=
  203. SharedCacheMap->SectionSize.QuadPart );
  204. //
  205. // Initially clear output
  206. //
  207. *Bcb = NULL;
  208. *BaseAddress = NULL;
  209. if (!FlagOn(Flags, PIN_NO_READ)) {
  210. *BaseAddress = CcGetVirtualAddress( SharedCacheMap,
  211. *FileOffset,
  212. &Vacb,
  213. &ReceivedLength );
  214. } else {
  215. //
  216. // In the PIN_NO_READ case, we simply need to make sure that the
  217. // sparse structure containing the Bcb listheads is expanded in the
  218. // region of the file we are interested in.
  219. //
  220. // Fake a ReceivedLength that matches the remaining bytes in the view.
  221. //
  222. ReceivedLength = VACB_MAPPING_GRANULARITY -
  223. (ULONG)(FileOffset->QuadPart & (VACB_MAPPING_GRANULARITY - 1));
  224. //
  225. // Now simply cause a reference that will expand a multilevel Vacb.
  226. //
  227. CcReferenceFileOffset( SharedCacheMap, *FileOffset );
  228. }
  229. //
  230. // Acquire Bcb List Exclusive to look for Bcb
  231. //
  232. KeAcquireInStackQueuedSpinLock( &SharedCacheMap->BcbSpinLock, &LockHandle );
  233. SpinLockAcquired = TRUE;
  234. //
  235. // Use try to guarantee cleanup on the way out.
  236. //
  237. try {
  238. LOGICAL Found;
  239. LARGE_INTEGER FOffset;
  240. LARGE_INTEGER TLength;
  241. //
  242. // Search for Bcb describing the largest matching "prefix" byte range,
  243. // or where to insert it.
  244. //
  245. TrialBound.QuadPart = FileOffset->QuadPart + (LONGLONG)Length;
  246. Found = CcFindBcb( SharedCacheMap, FileOffset, &TrialBound, &BcbOut );
  247. //
  248. // Cases 1 and 2 - Bcb was not found.
  249. //
  250. // First caculate data to pin down.
  251. //
  252. if (!Found) {
  253. //
  254. // Get out if the user specified PIN_IF_BCB.
  255. //
  256. if (FlagOn(Flags, PIN_IF_BCB)) {
  257. //
  258. // We need to zap BcbOut since this is a hint to the cleanup code
  259. // to remove the Bcb if we are returning FALSE.
  260. //
  261. BcbOut = NULL;
  262. try_return( Result = FALSE );
  263. }
  264. //
  265. // Not found, calculate data to pin down.
  266. //
  267. // Round local copy of FileOffset down to page boundary, and
  268. // round copies of size and minimum size up. Also make sure that
  269. // we keep the length from crossing the end of the SharedCacheMap.
  270. //
  271. FOffset = *FileOffset;
  272. TLength.QuadPart = TrialBound.QuadPart - FOffset.QuadPart;
  273. TLength.LowPart += FOffset.LowPart & (PAGE_SIZE - 1);
  274. ReceivedLength += FOffset.LowPart & (PAGE_SIZE - 1);
  275. //
  276. // At this point we can calculate the ReadOnly flag for
  277. // the purposes of whether to use the Bcb resource, and
  278. // we can calculate the ZeroFlags.
  279. //
  280. if ((!ReadOnly && !FlagOn(SharedCacheMap->Flags, PIN_ACCESS)) || WriteOnly) {
  281. //
  282. // We can always zero middle pages, if any.
  283. //
  284. ZeroFlags = ZERO_MIDDLE_PAGES;
  285. if (((FOffset.LowPart & (PAGE_SIZE - 1)) == 0) &&
  286. (Length >= PAGE_SIZE)) {
  287. ZeroFlags |= ZERO_FIRST_PAGE;
  288. }
  289. if ((TLength.LowPart & (PAGE_SIZE - 1)) == 0) {
  290. ZeroFlags |= ZERO_LAST_PAGE;
  291. }
  292. }
  293. //
  294. // We treat Bcbs as ReadOnly (do not acquire resource) if they
  295. // are in sections for which we have not disabled modified writing.
  296. //
  297. if (!FlagOn(SharedCacheMap->Flags, MODIFIED_WRITE_DISABLED)) {
  298. ReadOnly = TRUE;
  299. }
  300. TLength.LowPart = (ULONG) ROUND_TO_PAGES( TLength.LowPart );
  301. //
  302. // Round BaseAddress and FOffset down to the bottom of a page.
  303. //
  304. *BaseAddress = ((PCHAR)*BaseAddress - (FileOffset->LowPart & (PAGE_SIZE - 1)));
  305. FOffset.LowPart &= ~(PAGE_SIZE - 1);
  306. //
  307. // Even if we are readonly, we can still zero pages entirely
  308. // beyond valid data length.
  309. //
  310. if (FOffset.QuadPart >= SharedCacheMap->ValidDataGoal.QuadPart) {
  311. ZeroFlags |= ZERO_FIRST_PAGE | ZERO_MIDDLE_PAGES | ZERO_LAST_PAGE;
  312. } else if ((FOffset.QuadPart + (LONGLONG)PAGE_SIZE) >=
  313. SharedCacheMap->ValidDataGoal.QuadPart) {
  314. ZeroFlags |= ZERO_MIDDLE_PAGES | ZERO_LAST_PAGE;
  315. }
  316. //
  317. // We will get into trouble if we try to read more than we
  318. // can map by one Vacb. So make sure that our lengths stay
  319. // within a Vacb.
  320. //
  321. if (TLength.LowPart > ReceivedLength) {
  322. TLength.LowPart = ReceivedLength;
  323. }
  324. //
  325. // Case 1 - Bcb was not found and Wait is TRUE.
  326. //
  327. // Note that it is important to minimize the time that the Bcb
  328. // List spin lock is held, as well as guarantee we do not take
  329. // any faults while holding this lock.
  330. //
  331. // If we can (and perhaps will) wait, then it is important to
  332. // allocate the Bcb acquire it exclusive and free the Bcb List.
  333. // We then procede to read in the data, and anyone else finding
  334. // our Bcb will have to wait shared to insure that the data is
  335. // in.
  336. //
  337. if (FlagOn(Flags, PIN_WAIT)) {
  338. BcbOut = CcAllocateInitializeBcb( SharedCacheMap,
  339. BcbOut,
  340. &FOffset,
  341. &TLength );
  342. if (BcbOut == NULL) {
  343. DebugTrace( 0, 0, "Bcb allocation failure\n", 0 );
  344. KeReleaseInStackQueuedSpinLock( &LockHandle );
  345. SpinLockAcquired = FALSE;
  346. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  347. }
  348. //
  349. // Now just acquire the newly-allocated Bcb shared, and
  350. // release the spin lock.
  351. //
  352. if (!ReadOnly) {
  353. if (FlagOn(Flags, PIN_EXCLUSIVE)) {
  354. (VOID)ExAcquireResourceExclusiveLite( &BcbOut->Resource, TRUE );
  355. } else {
  356. (VOID)ExAcquireSharedStarveExclusive( &BcbOut->Resource, TRUE );
  357. }
  358. }
  359. KeReleaseInStackQueuedSpinLock( &LockHandle );
  360. SpinLockAcquired = FALSE;
  361. //
  362. // Now read in the data.
  363. //
  364. if (!FlagOn(Flags, PIN_NO_READ)) {
  365. (VOID)CcMapAndRead( SharedCacheMap,
  366. &FOffset,
  367. TLength.LowPart,
  368. ZeroFlags,
  369. TRUE,
  370. *BaseAddress );
  371. //
  372. // Now we have to reacquire the Bcb List spinlock to load
  373. // up the mapping if we are the first one, else we collided
  374. // with someone else who loaded the mapping first, and we
  375. // will just free our mapping. It is guaranteed that the
  376. // data will be mapped to the same place.
  377. //
  378. KeAcquireInStackQueuedSpinLock( &SharedCacheMap->BcbSpinLock, &LockHandle );
  379. if (BcbOut->BaseAddress == NULL) {
  380. BcbOut->BaseAddress = *BaseAddress;
  381. BcbOut->Vacb = Vacb;
  382. Vacb = NULL;
  383. }
  384. KeReleaseInStackQueuedSpinLock( &LockHandle );
  385. //
  386. // Calculate Base Address of the data we want.
  387. //
  388. *BaseAddress = (PCHAR)BcbOut->BaseAddress +
  389. (ULONG)( FileOffset->QuadPart - BcbOut->FileOffset.QuadPart );
  390. }
  391. //
  392. // Success!
  393. //
  394. try_return( Result = TRUE );
  395. }
  396. //
  397. // Case 2 - Bcb was not found and Wait is FALSE
  398. //
  399. // If we cannot wait, then we go immediately see if the data is
  400. // there (CcMapAndRead), and then only set up the Bcb and release
  401. // the spin lock if the data is there. Note here we call
  402. // CcMapAndRead while holding the spin lock, because we know we
  403. // will not fault and not block before returning.
  404. //
  405. else {
  406. //
  407. // Now try to allocate and initialize the Bcb. If we
  408. // fail to allocate one, then return FALSE, since we know that
  409. // Wait = FALSE. The caller may get lucky if he calls
  410. // us back with Wait = TRUE.
  411. //
  412. BcbOut = CcAllocateInitializeBcb( SharedCacheMap,
  413. BcbOut,
  414. &FOffset,
  415. &TLength );
  416. if (BcbOut == NULL) {
  417. try_return( Result = FALSE );
  418. }
  419. //
  420. // If we are not ReadOnly, we must acquire the newly-allocated
  421. // resource shared, and then we can free the spin lock.
  422. //
  423. if (!ReadOnly) {
  424. ExAcquireSharedStarveExclusive( &BcbOut->Resource, TRUE );
  425. }
  426. KeReleaseInStackQueuedSpinLock( &LockHandle );
  427. SpinLockAcquired = FALSE;
  428. //
  429. // Note that since this call has Wait = FALSE, it cannot
  430. // get an exception (see procedure header).
  431. //
  432. ASSERT( !FlagOn(Flags, PIN_NO_READ) );
  433. if (!CcMapAndRead( SharedCacheMap,
  434. &FOffset,
  435. TLength.LowPart,
  436. ZeroFlags,
  437. FALSE,
  438. *BaseAddress )) {
  439. try_return( Result = FALSE );
  440. }
  441. //
  442. // Now we have to reacquire the Bcb List spinlock to load
  443. // up the mapping if we are the first one, else we collided
  444. // with someone else who loaded the mapping first, and we
  445. // will just free our mapping. It is guaranteed that the
  446. // data will be mapped to the same place.
  447. //
  448. KeAcquireInStackQueuedSpinLock( &SharedCacheMap->BcbSpinLock, &LockHandle );
  449. if (BcbOut->BaseAddress == NULL) {
  450. BcbOut->BaseAddress = *BaseAddress;
  451. BcbOut->Vacb = Vacb;
  452. Vacb = NULL;
  453. }
  454. KeReleaseInStackQueuedSpinLock( &LockHandle );
  455. //
  456. // Calculate Base Address of the data we want.
  457. //
  458. *BaseAddress = (PCHAR)BcbOut->BaseAddress +
  459. (ULONG)( FileOffset->QuadPart - BcbOut->FileOffset.QuadPart );
  460. //
  461. // Success!
  462. //
  463. try_return( Result = TRUE );
  464. }
  465. } else {
  466. //
  467. // We treat Bcbs as ReadOnly (do not acquire resource) if they
  468. // are in sections for which we have not disabled modified writing.
  469. //
  470. if (!FlagOn(SharedCacheMap->Flags, MODIFIED_WRITE_DISABLED)) {
  471. ReadOnly = TRUE;
  472. }
  473. }
  474. //
  475. // Cases 3 and 4 - Bcb is there but not mapped
  476. //
  477. if (BcbOut->BaseAddress == NULL) {
  478. //
  479. // It is too complicated to attempt to calculate any ZeroFlags in this
  480. // case, because we have to not only do the tests above, but also
  481. // compare to the byte range in the Bcb since we will be passing
  482. // those parameters to CcMapAndRead. Also, the probability of hitting
  483. // some window where zeroing is of any advantage is quite small.
  484. //
  485. //
  486. // Set up to just reread the Bcb exactly as the data in it is
  487. // described.
  488. //
  489. *BaseAddress = ((PCHAR)*BaseAddress - (FileOffset->LowPart - BcbOut->FileOffset.LowPart));
  490. FOffset = BcbOut->FileOffset;
  491. TLength.QuadPart = (LONGLONG)BcbOut->ByteLength;
  492. //
  493. // Case 3 - Bcb is there but not mapped and Wait is TRUE
  494. //
  495. // Increment the PinCount, and then release the BcbList
  496. // SpinLock so that we can wait to acquire the Bcb exclusive.
  497. // Once we have the Bcb exclusive, map and read it in if no
  498. // one beats us to it. Someone may have beat us to it since
  499. // we had to release the SpinLock above.
  500. //
  501. if (FlagOn(Flags, PIN_WAIT)) {
  502. BcbOut->PinCount += 1;
  503. //
  504. // Now we have to release the BcbList SpinLock in order to
  505. // acquire the Bcb shared.
  506. //
  507. KeReleaseInStackQueuedSpinLock( &LockHandle );
  508. SpinLockAcquired = FALSE;
  509. if (!ReadOnly) {
  510. if (FlagOn(Flags, PIN_EXCLUSIVE)) {
  511. (VOID)ExAcquireResourceExclusiveLite( &BcbOut->Resource, TRUE );
  512. } else {
  513. (VOID)ExAcquireSharedStarveExclusive( &BcbOut->Resource, TRUE );
  514. }
  515. }
  516. //
  517. // Now procede to map and read the data in.
  518. //
  519. // Now read in the data.
  520. //
  521. if (!FlagOn(Flags, PIN_NO_READ)) {
  522. (VOID)CcMapAndRead( SharedCacheMap,
  523. &FOffset,
  524. TLength.LowPart,
  525. ZeroFlags,
  526. TRUE,
  527. *BaseAddress );
  528. //
  529. // Now we have to reacquire the Bcb List spinlock to load
  530. // up the mapping if we are the first one, else we collided
  531. // with someone else who loaded the mapping first, and we
  532. // will just free our mapping. It is guaranteed that the
  533. // data will be mapped to the same place.
  534. //
  535. KeAcquireInStackQueuedSpinLock( &SharedCacheMap->BcbSpinLock, &LockHandle );
  536. if (BcbOut->BaseAddress == NULL) {
  537. BcbOut->BaseAddress = *BaseAddress;
  538. BcbOut->Vacb = Vacb;
  539. Vacb = NULL;
  540. }
  541. KeReleaseInStackQueuedSpinLock( &LockHandle );
  542. //
  543. //
  544. // Calculate Base Address of the data we want.
  545. //
  546. *BaseAddress = (PCHAR)BcbOut->BaseAddress +
  547. (ULONG)( FileOffset->QuadPart - BcbOut->FileOffset.QuadPart );
  548. }
  549. //
  550. // Success!
  551. //
  552. try_return( Result = TRUE );
  553. }
  554. //
  555. // Case 4 - Bcb is there but not mapped, and Wait is FALSE
  556. //
  557. // Since we cannot wait, we go immediately see if the data is
  558. // there (CcMapAndRead), and then only set up the Bcb and release
  559. // the spin lock if the data is there. Note here we call
  560. // CcMapAndRead while holding the spin lock, because we know we
  561. // will not fault and not block before returning.
  562. //
  563. else {
  564. if (!ReadOnly && !ExAcquireSharedStarveExclusive( &BcbOut->Resource, FALSE )) {
  565. //
  566. // If we cannot get the resource and have not incremented PinCount, then
  567. // suppress the unpin on cleanup.
  568. //
  569. BcbOut = NULL;
  570. try_return( Result = FALSE );
  571. }
  572. BcbOut->PinCount += 1;
  573. KeReleaseInStackQueuedSpinLock( &LockHandle );
  574. SpinLockAcquired = FALSE;
  575. //
  576. // Note that since this call has Wait = FALSE, it cannot
  577. // get an exception (see procedure header).
  578. //
  579. ASSERT( !FlagOn(Flags, PIN_NO_READ) );
  580. if (!CcMapAndRead( SharedCacheMap,
  581. &BcbOut->FileOffset,
  582. BcbOut->ByteLength,
  583. ZeroFlags,
  584. FALSE,
  585. *BaseAddress )) {
  586. try_return( Result = FALSE );
  587. }
  588. //
  589. // Now we have to reacquire the Bcb List spinlock to load
  590. // up the mapping if we are the first one, else we collided
  591. // with someone else who loaded the mapping first, and we
  592. // will just free our mapping. It is guaranteed that the
  593. // data will be mapped to the same place.
  594. //
  595. KeAcquireInStackQueuedSpinLock( &SharedCacheMap->BcbSpinLock, &LockHandle );
  596. if (BcbOut->BaseAddress == NULL) {
  597. BcbOut->BaseAddress = *BaseAddress;
  598. BcbOut->Vacb = Vacb;
  599. Vacb = NULL;
  600. }
  601. KeReleaseInStackQueuedSpinLock( &LockHandle );
  602. //
  603. // Calculate Base Address of the data we want.
  604. //
  605. *BaseAddress = (PCHAR)BcbOut->BaseAddress +
  606. (ULONG)( FileOffset->QuadPart - BcbOut->FileOffset.QuadPart );
  607. //
  608. // Success!
  609. //
  610. try_return( Result = TRUE );
  611. }
  612. }
  613. //
  614. // Cases 5 and 6 - Bcb is there and it is mapped
  615. //
  616. else {
  617. //
  618. // Case 5 - Bcb is there and mapped, and Wait is TRUE
  619. //
  620. // We can just increment the PinCount, release the SpinLock
  621. // and then acquire the Bcb Shared if we are not ReadOnly.
  622. //
  623. if (FlagOn(Flags, PIN_WAIT)) {
  624. BcbOut->PinCount += 1;
  625. KeReleaseInStackQueuedSpinLock( &LockHandle );
  626. SpinLockAcquired = FALSE;
  627. //
  628. // Acquire Bcb Resource shared to insure that it is in memory.
  629. //
  630. if (!ReadOnly) {
  631. if (FlagOn(Flags, PIN_EXCLUSIVE)) {
  632. (VOID)ExAcquireResourceExclusiveLite( &BcbOut->Resource, TRUE );
  633. } else {
  634. (VOID)ExAcquireSharedStarveExclusive( &BcbOut->Resource, TRUE );
  635. }
  636. }
  637. }
  638. //
  639. // Case 6 - Bcb is there and mapped, and Wait is FALSE
  640. //
  641. // If we are not ReadOnly, we have to first see if we can
  642. // acquire the Bcb shared before incrmenting the PinCount,
  643. // since we will have to return FALSE if we cannot acquire the
  644. // resource.
  645. //
  646. else {
  647. //
  648. // Acquire Bcb Resource shared to insure that it is in memory.
  649. //
  650. if (!ReadOnly && !ExAcquireSharedStarveExclusive( &BcbOut->Resource, FALSE )) {
  651. //
  652. // If we cannot get the resource and have not incremented PinCount, then
  653. // suppress the unpin on cleanup.
  654. //
  655. BcbOut = NULL;
  656. try_return( Result = FALSE );
  657. }
  658. BcbOut->PinCount += 1;
  659. KeReleaseInStackQueuedSpinLock( &LockHandle );
  660. SpinLockAcquired = FALSE;
  661. }
  662. //
  663. // Calculate Base Address of the data we want.
  664. //
  665. *BaseAddress = (PCHAR)BcbOut->BaseAddress +
  666. (ULONG)( FileOffset->QuadPart - BcbOut->FileOffset.QuadPart );
  667. //
  668. // Success!
  669. //
  670. try_return( Result = TRUE );
  671. }
  672. try_exit: NOTHING;
  673. if (FlagOn(Flags, PIN_NO_READ) &&
  674. FlagOn(Flags, PIN_EXCLUSIVE) &&
  675. (BcbOut != NULL) &&
  676. (BcbOut->BaseAddress != NULL)) {
  677. //
  678. // Unmap the Vacb and free the resource if the Bcb is still
  679. // dirty. We have to free the resource before dropping the
  680. // spinlock, and we want to hold the resource until the
  681. // virtual address is freed.
  682. //
  683. CcFreeVirtualAddress( BcbOut->Vacb );
  684. BcbOut->BaseAddress = NULL;
  685. BcbOut->Vacb = NULL;
  686. }
  687. } finally {
  688. //
  689. // Release the spinlock if it is acquired.
  690. //
  691. if (SpinLockAcquired) {
  692. KeReleaseInStackQueuedSpinLock( &LockHandle );
  693. }
  694. //
  695. // If the Vacb was not used for any reason (error or not needed), then free it here.
  696. //
  697. if (Vacb != NULL) {
  698. CcFreeVirtualAddress( Vacb );
  699. }
  700. //
  701. // If we referenced a piece of a multilevel structure, release here.
  702. //
  703. if (FlagOn(Flags, PIN_NO_READ)) {
  704. CcDereferenceFileOffset( SharedCacheMap, *FileOffset );
  705. }
  706. if (Result) {
  707. *Bcb = BcbOut;
  708. *BeyondLastByte = BcbOut->BeyondLastByte;
  709. //
  710. // An abnormal termination can occur on an allocation failure,
  711. // or on a failure to map and read the buffer.
  712. //
  713. } else {
  714. *BaseAddress = NULL;
  715. if (BcbOut != NULL) {
  716. CcUnpinFileData( BcbOut, ReadOnly, UNPIN );
  717. }
  718. }
  719. DebugTrace( 0, me, " <Bcb = %08lx\n", *Bcb );
  720. DebugTrace( 0, me, " <BaseAddress = %08lx\n", *BaseAddress );
  721. DebugTrace(-1, me, "CcPinFileData -> %02lx\n", Result );
  722. }
  723. return Result;
  724. }
  725. //
  726. // Internal Support Routine
  727. //
  728. VOID
  729. FASTCALL
  730. CcUnpinFileData (
  731. IN OUT PBCB Bcb,
  732. IN BOOLEAN ReadOnly,
  733. IN UNMAP_ACTIONS UnmapAction
  734. )
  735. /*++
  736. Routine Description:
  737. This routine umaps and unlocks the specified buffer, which was previously
  738. locked and mapped by calling CcPinFileData.
  739. Arguments:
  740. Bcb - Pointer previously returned from CcPinFileData. As may be
  741. seen above, this pointer may be either a Bcb or a Vacb.
  742. ReadOnly - must specify same value as when data was mapped
  743. UnmapAction - UNPIN or SET_CLEAN
  744. Return Value:
  745. None
  746. --*/
  747. {
  748. KLOCK_QUEUE_HANDLE LockHandle;
  749. PSHARED_CACHE_MAP SharedCacheMap;
  750. DebugTrace(+1, me, "CcUnpinFileData >Bcb = %08lx\n", Bcb );
  751. //
  752. // Note, since we have to allocate so many Vacbs, we do not use
  753. // a node type code. However, the Vacb starts with a BaseAddress,
  754. // so we assume that the low byte of the Bcb node type code has
  755. // some bits set, which a page-aligned Base Address cannot.
  756. //
  757. ASSERT( (CACHE_NTC_BCB & 0xFF) != 0 );
  758. if (Bcb->NodeTypeCode != CACHE_NTC_BCB) {
  759. ASSERT(((PVACB)Bcb >= CcVacbs) && ((PVACB)Bcb < CcBeyondVacbs));
  760. ASSERT(((PVACB)Bcb)->SharedCacheMap->NodeTypeCode == CACHE_NTC_SHARED_CACHE_MAP);
  761. CcFreeVirtualAddress( (PVACB)Bcb );
  762. DebugTrace(-1, me, "CcUnpinFileData -> VOID (simple release)\n", 0 );
  763. return;
  764. }
  765. SharedCacheMap = Bcb->SharedCacheMap;
  766. //
  767. // We treat Bcbs as ReadOnly (do not acquire resource) if they
  768. // are in sections for which we have not disabled modified writing, or
  769. // in this special case if this action is a dereferencing of the BCB.
  770. //
  771. if (!FlagOn(SharedCacheMap->Flags, MODIFIED_WRITE_DISABLED) ||
  772. UnmapAction == UNREF) {
  773. ReadOnly = TRUE;
  774. }
  775. //
  776. // Synchronize
  777. //
  778. KeAcquireInStackQueuedSpinLock( &SharedCacheMap->BcbSpinLock, &LockHandle );
  779. switch (UnmapAction) {
  780. case UNPIN:
  781. case UNREF:
  782. ASSERT( Bcb->PinCount > 0 );
  783. Bcb->PinCount -= 1;
  784. break;
  785. case SET_CLEAN:
  786. if (Bcb->Dirty) {
  787. ULONG Pages = Bcb->ByteLength >> PAGE_SHIFT;
  788. //
  789. // Reverse the rest of the actions taken when the Bcb was set dirty.
  790. //
  791. Bcb->Dirty = FALSE;
  792. CcAcquireMasterLockAtDpcLevel();
  793. CcDeductDirtyPages( SharedCacheMap, Pages );
  794. //
  795. // Normally we need to reduce CcPagesYetToWrite appropriately.
  796. //
  797. if (CcPagesYetToWrite > Pages) {
  798. CcPagesYetToWrite -= Pages;
  799. } else {
  800. CcPagesYetToWrite = 0;
  801. }
  802. //
  803. // Remove SharedCacheMap from dirty list if nothing more dirty,
  804. // and someone still has the cache map opened.
  805. //
  806. if ((SharedCacheMap->DirtyPages == 0) &&
  807. (SharedCacheMap->OpenCount != 0)) {
  808. RemoveEntryList( &SharedCacheMap->SharedCacheMapLinks );
  809. InsertTailList( &CcCleanSharedCacheMapList,
  810. &SharedCacheMap->SharedCacheMapLinks );
  811. }
  812. CcReleaseMasterLockFromDpcLevel();
  813. }
  814. break;
  815. default:
  816. CcBugCheck( UnmapAction, 0, 0 );
  817. }
  818. //
  819. // If we brought it to 0, then we have to kill it.
  820. //
  821. if (Bcb->PinCount == 0) {
  822. //
  823. // If the Bcb is Dirty, we only release the resource and unmap now.
  824. //
  825. if (Bcb->Dirty) {
  826. if (Bcb->BaseAddress != NULL) {
  827. //
  828. // Unmap the Vacb and free the resource if the Bcb is still
  829. // dirty. We have to free the resource before dropping the
  830. // spinlock, and we want to hold the resource until the
  831. // virtual address is freed.
  832. //
  833. CcFreeVirtualAddress( Bcb->Vacb );
  834. Bcb->BaseAddress = NULL;
  835. Bcb->Vacb = NULL;
  836. }
  837. if (!ReadOnly) {
  838. ExReleaseResourceLite( &Bcb->Resource );
  839. }
  840. KeReleaseInStackQueuedSpinLock( &LockHandle );
  841. }
  842. //
  843. // Otherwise, we also delete the Bcb.
  844. //
  845. else {
  846. //
  847. // Since CcCalculateVacbLockCount has to be able to walk
  848. // the BcbList with only the VacbSpinLock, we take that one
  849. // out to change the list and decrement the level.
  850. //
  851. CcAcquireVacbLockAtDpcLevel();
  852. RemoveEntryList( &Bcb->BcbLinks );
  853. //
  854. // For large metadata streams we unlock the Vacb level.
  855. //
  856. CcUnlockVacbLevel( SharedCacheMap, Bcb->FileOffset.QuadPart );
  857. CcReleaseVacbLockFromDpcLevel();
  858. //
  859. // Debug routines used to remove Bcbs from the global list
  860. //
  861. #if LIST_DBG
  862. KeAcquireQueuedSpinLockAtDpcLevel( KeQueuedSpinLockContext(LockQueueBcbLock) );
  863. if (Bcb->CcBcbLinks.Flink != NULL) {
  864. RemoveEntryList( &Bcb->CcBcbLinks );
  865. CcBcbCount -= 1;
  866. }
  867. KeReleaseQueuedSpinLockFromDpcLevel( KeQueuedSpinLockContext(LockQueueBcbLock) );
  868. #endif
  869. if (Bcb->BaseAddress != NULL) {
  870. CcFreeVirtualAddress( Bcb->Vacb );
  871. }
  872. #if DBG
  873. if (!ReadOnly) {
  874. ExReleaseResourceLite( &Bcb->Resource );
  875. }
  876. //
  877. // ASSERT that the resource is unowned.
  878. //
  879. ASSERT( Bcb->Resource.ActiveCount == 0 );
  880. #endif
  881. KeReleaseInStackQueuedSpinLock( &LockHandle );
  882. CcDeallocateBcb( Bcb );
  883. }
  884. }
  885. //
  886. // Else we just have to release our Shared access, if we are not
  887. // readonly. We don't need to do this above, since we deallocate
  888. // the entire Bcb there.
  889. //
  890. else {
  891. if (!ReadOnly) {
  892. ExReleaseResourceLite( &Bcb->Resource );
  893. }
  894. KeReleaseInStackQueuedSpinLock( &LockHandle );
  895. }
  896. DebugTrace(-1, me, "CcUnpinFileData -> VOID\n", 0 );
  897. return;
  898. }
  899. VOID
  900. CcSetReadAheadGranularity (
  901. IN PFILE_OBJECT FileObject,
  902. IN ULONG Granularity
  903. )
  904. /*++
  905. Routine Description:
  906. This routine may be called to set the read ahead granularity used by
  907. the Cache Manager. The default is PAGE_SIZE. The number is decremented
  908. and stored as a mask.
  909. Arguments:
  910. FileObject - File Object for which granularity shall be set
  911. Granularity - new granularity, which must be an even power of 2 and
  912. >= PAGE_SIZE
  913. Return Value:
  914. None
  915. --*/
  916. {
  917. ((PPRIVATE_CACHE_MAP)FileObject->PrivateCacheMap)->ReadAheadMask = Granularity - 1;
  918. }
  919. VOID
  920. CcScheduleReadAhead (
  921. IN PFILE_OBJECT FileObject,
  922. IN PLARGE_INTEGER FileOffset,
  923. IN ULONG Length
  924. )
  925. /*++
  926. Routine Description:
  927. This routine is called by Copy Read and Mdl Read file system routines to
  928. perform common Read Ahead processing. The input parameters describe
  929. the current read which has just been completed, or perhaps only started
  930. in the case of Mdl Reads. Based on these parameters, an
  931. assessment is made on how much data should be read ahead, and whether
  932. that data has already been read ahead.
  933. The processing is divided into two parts:
  934. CALCULATE READ AHEAD REQUIREMENTS (CcScheduleReadAhead)
  935. PERFORM READ AHEAD (CcPerformReadAhead)
  936. File systems should always call CcReadAhead, which will conditionally
  937. call CcScheduleReadAhead (if the read is large enough). If such a call
  938. determines that there is read ahead work to do, and no read ahead is
  939. currently active, then it will set ReadAheadActive and schedule read
  940. ahead to be peformed by the Lazy Writer, who will call CcPeformReadAhead.
  941. Arguments:
  942. FileObject - supplies pointer to FileObject on which readahead should be
  943. considered.
  944. FileOffset - supplies the FileOffset at which the last read just occurred.
  945. Length - supplies the length of the last read.
  946. Return Value:
  947. None
  948. --*/
  949. {
  950. LARGE_INTEGER NewOffset;
  951. LARGE_INTEGER NewBeyond;
  952. LARGE_INTEGER FileOffset1, FileOffset2;
  953. KIRQL OldIrql;
  954. PSHARED_CACHE_MAP SharedCacheMap;
  955. PPRIVATE_CACHE_MAP PrivateCacheMap;
  956. PWORK_QUEUE_ENTRY WorkQueueEntry;
  957. ULONG ReadAheadSize;
  958. LOGICAL Changed = FALSE;
  959. DebugTrace(+1, me, "CcScheduleReadAhead:\n", 0 );
  960. DebugTrace2(0, me, " FileOffset = %08lx, %08lx\n", FileOffset->LowPart,
  961. FileOffset->HighPart );
  962. DebugTrace( 0, me, " Length = %08lx\n", Length );
  963. SharedCacheMap = *(PSHARED_CACHE_MAP *)((PCHAR)FileObject->SectionObjectPointer
  964. + sizeof(PVOID));
  965. PrivateCacheMap = FileObject->PrivateCacheMap;
  966. if ((PrivateCacheMap == NULL) ||
  967. (SharedCacheMap == NULL) ||
  968. FlagOn(SharedCacheMap->Flags, DISABLE_READ_AHEAD)) {
  969. DebugTrace(-1, me, "CcScheduleReadAhead -> VOID (Nooped)\n", 0 );
  970. return;
  971. }
  972. //
  973. // Round boundaries of transfer up to some greater granularity, so that
  974. // sequential reads will be recognized even if a few bytes are skipped
  975. // between records.
  976. //
  977. NewOffset = *FileOffset;
  978. NewBeyond.QuadPart = FileOffset->QuadPart + (LONGLONG)Length;
  979. //
  980. // Find the next read ahead boundary beyond the current read.
  981. //
  982. ReadAheadSize = (Length + PrivateCacheMap->ReadAheadMask) & ~PrivateCacheMap->ReadAheadMask;
  983. FileOffset2.QuadPart = NewBeyond.QuadPart + (LONGLONG)ReadAheadSize;
  984. FileOffset2.LowPart &= ~PrivateCacheMap->ReadAheadMask;
  985. //
  986. // CALCULATE READ AHEAD REQUIREMENTS
  987. //
  988. //
  989. // Take out the ReadAhead spinlock to synchronize our read ahead decision.
  990. //
  991. ExAcquireSpinLock( &PrivateCacheMap->ReadAheadSpinLock, &OldIrql );
  992. //
  993. // Read Ahead Case 0.
  994. //
  995. // Sequential-only hint in the file object. For this case we will
  996. // try and always keep two read ahead granularities read ahead from
  997. // and including the end of the current transfer. This case has the
  998. // lowest overhead, and the code is completely immune to how the
  999. // caller skips around. Sequential files use ReadAheadOffset[1] in
  1000. // the PrivateCacheMap as their "high water mark".
  1001. //
  1002. if (FlagOn(FileObject->Flags, FO_SEQUENTIAL_ONLY)) {
  1003. //
  1004. // If the next boundary is greater than or equal to the high-water mark,
  1005. // then read ahead.
  1006. //
  1007. if (FileOffset2.QuadPart >= PrivateCacheMap->ReadAheadOffset[1].QuadPart) {
  1008. //
  1009. // On the first read if we are using a large read ahead granularity,
  1010. // and the read did not get it all, we will just get the rest of the
  1011. // first data we want.
  1012. //
  1013. if ((FileOffset->QuadPart == 0)
  1014. &&
  1015. (PrivateCacheMap->ReadAheadMask > (PAGE_SIZE - 1))
  1016. &&
  1017. ((Length + PAGE_SIZE - 1) <= PrivateCacheMap->ReadAheadMask)) {
  1018. FileOffset1.QuadPart = (LONGLONG)( ROUND_TO_PAGES(Length) );
  1019. PrivateCacheMap->ReadAheadLength[0] = ReadAheadSize - FileOffset1.LowPart;
  1020. FileOffset2.QuadPart = (LONGLONG)ReadAheadSize;
  1021. //
  1022. // Calculate the next read ahead boundary.
  1023. //
  1024. } else {
  1025. FileOffset1.QuadPart = PrivateCacheMap->ReadAheadOffset[1].QuadPart +
  1026. (LONGLONG)ReadAheadSize;
  1027. //
  1028. // If the end of the current read is actually beyond where we would
  1029. // normally do our read ahead, then we have fallen behind, and we must
  1030. // advance to that spot.
  1031. //
  1032. if (FileOffset2.QuadPart > FileOffset1.QuadPart) {
  1033. FileOffset1 = FileOffset2;
  1034. }
  1035. PrivateCacheMap->ReadAheadLength[0] = ReadAheadSize;
  1036. FileOffset2.QuadPart = FileOffset1.QuadPart + (LONGLONG)ReadAheadSize;
  1037. }
  1038. //
  1039. // Now issue the next two read aheads.
  1040. //
  1041. PrivateCacheMap->ReadAheadOffset[0] = FileOffset1;
  1042. PrivateCacheMap->ReadAheadOffset[1] = FileOffset2;
  1043. PrivateCacheMap->ReadAheadLength[1] = ReadAheadSize;
  1044. Changed = TRUE;
  1045. }
  1046. //
  1047. // Read Ahead Case 1.
  1048. //
  1049. // If this is the third of three sequential reads, then we will see if
  1050. // we can read ahead. Note that if the first read to a file is to
  1051. // offset 0, it passes this test.
  1052. //
  1053. } else if ((NewOffset.HighPart == PrivateCacheMap->BeyondLastByte2.HighPart)
  1054. &&
  1055. ((NewOffset.LowPart & ~NOISE_BITS)
  1056. == (PrivateCacheMap->BeyondLastByte2.LowPart & ~NOISE_BITS))
  1057. &&
  1058. (PrivateCacheMap->FileOffset2.HighPart
  1059. == PrivateCacheMap->BeyondLastByte1.HighPart)
  1060. &&
  1061. ((PrivateCacheMap->FileOffset2.LowPart & ~NOISE_BITS)
  1062. == (PrivateCacheMap->BeyondLastByte1.LowPart & ~NOISE_BITS))) {
  1063. //
  1064. // On the first read if we are using a large read ahead granularity,
  1065. // and the read did not get it all, we will just get the rest of the
  1066. // first data we want.
  1067. //
  1068. if ((FileOffset->QuadPart == 0)
  1069. &&
  1070. (PrivateCacheMap->ReadAheadMask > (PAGE_SIZE - 1))
  1071. &&
  1072. ((Length + PAGE_SIZE - 1) <= PrivateCacheMap->ReadAheadMask)) {
  1073. FileOffset2.QuadPart = (LONGLONG)( ROUND_TO_PAGES(Length) );
  1074. }
  1075. //
  1076. // Round read offset to next read ahead boundary.
  1077. //
  1078. else {
  1079. FileOffset2.QuadPart = NewBeyond.QuadPart + (LONGLONG)ReadAheadSize;
  1080. FileOffset2.LowPart &= ~PrivateCacheMap->ReadAheadMask;
  1081. }
  1082. //
  1083. // Set read ahead length to be the same as for the most recent read,
  1084. // up to our max.
  1085. //
  1086. if (FileOffset2.QuadPart != PrivateCacheMap->ReadAheadOffset[1].QuadPart) {
  1087. ASSERT( FileOffset2.HighPart >= 0 );
  1088. Changed = TRUE;
  1089. PrivateCacheMap->ReadAheadOffset[1] = FileOffset2;
  1090. PrivateCacheMap->ReadAheadLength[1] = ReadAheadSize;
  1091. }
  1092. }
  1093. //
  1094. // Read Ahead Case 2.
  1095. //
  1096. // If this is the third read following a particular stride, then we
  1097. // will see if we can read ahead. One example of an application that
  1098. // might do this is a spreadsheet. Note that this code even works
  1099. // for negative strides.
  1100. //
  1101. else if ( ( NewOffset.QuadPart -
  1102. PrivateCacheMap->FileOffset2.QuadPart ) ==
  1103. ( PrivateCacheMap->FileOffset2.QuadPart -
  1104. PrivateCacheMap->FileOffset1.QuadPart )) {
  1105. //
  1106. // According to the current stride, the next offset will be:
  1107. //
  1108. // NewOffset + (NewOffset - FileOffset2)
  1109. //
  1110. // which is the same as:
  1111. //
  1112. // (NewOffset * 2) - FileOffset2
  1113. //
  1114. FileOffset2.QuadPart = ( NewOffset.QuadPart << 1 ) - PrivateCacheMap->FileOffset2.QuadPart;
  1115. //
  1116. // If our stride is going backwards through the file, we
  1117. // have to detect the case where the next step would wrap.
  1118. //
  1119. if (FileOffset2.HighPart >= 0) {
  1120. //
  1121. // The read ahead length must be extended by the same amount that
  1122. // we will round the PrivateCacheMap->ReadAheadOffset down.
  1123. //
  1124. Length += FileOffset2.LowPart & (PAGE_SIZE - 1);
  1125. //
  1126. // Now round the PrivateCacheMap->ReadAheadOffset down.
  1127. //
  1128. FileOffset2.LowPart &= ~(PAGE_SIZE - 1);
  1129. PrivateCacheMap->ReadAheadOffset[1] = FileOffset2;
  1130. //
  1131. // Round to page boundary.
  1132. //
  1133. PrivateCacheMap->ReadAheadLength[1] = (ULONG) ROUND_TO_PAGES(Length);
  1134. Changed = TRUE;
  1135. }
  1136. }
  1137. //
  1138. // Get out if the ReadAhead requirements did not change.
  1139. //
  1140. if (!Changed || PrivateCacheMap->Flags.ReadAheadActive) {
  1141. DebugTrace( 0, me, "Read ahead already in progress or no change\n", 0 );
  1142. ExReleaseSpinLock( &PrivateCacheMap->ReadAheadSpinLock, OldIrql );
  1143. return;
  1144. }
  1145. //
  1146. // Otherwise, we will proceed and try to schedule the read ahead
  1147. // ourselves.
  1148. //
  1149. CC_SET_PRIVATE_CACHE_MAP (PrivateCacheMap, PRIVATE_CACHE_MAP_READ_AHEAD_ACTIVE);
  1150. //
  1151. // Release spin lock on way out
  1152. //
  1153. ExReleaseSpinLock( &PrivateCacheMap->ReadAheadSpinLock, OldIrql );
  1154. //
  1155. // Queue the read ahead request to the Lazy Writer's work queue.
  1156. //
  1157. DebugTrace( 0, me, "Queueing read ahead to worker thread\n", 0 );
  1158. WorkQueueEntry = CcAllocateWorkQueueEntry();
  1159. //
  1160. // If we failed to allocate a work queue entry, then, we will
  1161. // quietly bag it. Read ahead is only an optimization, and
  1162. // no one ever requires that it occur.
  1163. //
  1164. if (WorkQueueEntry != NULL) {
  1165. //
  1166. // We must reference this file object so that it cannot go away
  1167. // until we finish Read Ahead processing in the Worker Thread.
  1168. //
  1169. ObReferenceObject ( FileObject );
  1170. //
  1171. // Increment open count to make sure the SharedCacheMap stays around.
  1172. //
  1173. CcAcquireMasterLock( &OldIrql );
  1174. CcIncrementOpenCount( SharedCacheMap, 'adRQ' );
  1175. CcReleaseMasterLock( OldIrql );
  1176. WorkQueueEntry->Function = (UCHAR)ReadAhead;
  1177. WorkQueueEntry->Parameters.Read.FileObject = FileObject;
  1178. CcPostWorkQueue( WorkQueueEntry, &CcExpressWorkQueue );
  1179. }
  1180. //
  1181. // If we failed to allocate a Work Queue Entry, or all of the pages
  1182. // are resident we must set the active flag false.
  1183. //
  1184. else {
  1185. ExAcquireFastLock( &PrivateCacheMap->ReadAheadSpinLock, &OldIrql );
  1186. CC_CLEAR_PRIVATE_CACHE_MAP (PrivateCacheMap, PRIVATE_CACHE_MAP_READ_AHEAD_ACTIVE);
  1187. ExReleaseFastLock( &PrivateCacheMap->ReadAheadSpinLock, OldIrql );
  1188. }
  1189. DebugTrace(-1, me, "CcScheduleReadAhead -> VOID\n", 0 );
  1190. return;
  1191. }
  1192. VOID
  1193. FASTCALL
  1194. CcPerformReadAhead (
  1195. IN PFILE_OBJECT FileObject
  1196. )
  1197. /*++
  1198. Routine Description:
  1199. This routine is called by the Lazy Writer to perform read ahead which
  1200. has been scheduled for this file by CcScheduleReadAhead.
  1201. Arguments:
  1202. FileObject - supplies pointer to FileObject on which readahead should be
  1203. considered.
  1204. Return Value:
  1205. None
  1206. --*/
  1207. {
  1208. KIRQL OldIrql;
  1209. PSHARED_CACHE_MAP SharedCacheMap;
  1210. PPRIVATE_CACHE_MAP PrivateCacheMap;
  1211. ULONG i;
  1212. LARGE_INTEGER ReadAheadOffset[2];
  1213. ULONG ReadAheadLength[2];
  1214. PCACHE_MANAGER_CALLBACKS Callbacks;
  1215. PVOID Context;
  1216. ULONG SavedState;
  1217. LOGICAL Done;
  1218. LOGICAL HitEof = FALSE;
  1219. LOGICAL ReadAheadPerformed = FALSE;
  1220. ULONG FaultOccurred = 0;
  1221. PETHREAD Thread = PsGetCurrentThread();
  1222. PVACB Vacb = NULL;
  1223. LOGICAL ResourceHeld = FALSE;
  1224. DebugTrace(+1, me, "CcPerformReadAhead:\n", 0 );
  1225. DebugTrace( 0, me, " FileObject = %08lx\n", FileObject );
  1226. MmSavePageFaultReadAhead( Thread, &SavedState );
  1227. try {
  1228. //
  1229. // Since we have the open count biased, we can safely access the
  1230. // SharedCacheMap.
  1231. //
  1232. SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
  1233. Callbacks = SharedCacheMap->Callbacks;
  1234. Context = SharedCacheMap->LazyWriteContext;
  1235. //
  1236. // After the first time, keep looping as long as there are new
  1237. // read ahead requirements. (We will skip out below.)
  1238. //
  1239. while (TRUE) {
  1240. //
  1241. // Get SharedCacheMap and PrivateCacheMap. If either are now NULL, get
  1242. // out.
  1243. //
  1244. CcAcquireMasterLock( &OldIrql );
  1245. PrivateCacheMap = FileObject->PrivateCacheMap;
  1246. //
  1247. // Now capture the information that we need, so that we can drop the
  1248. // SharedList Resource. This information is advisory only anyway, and
  1249. // the caller must guarantee that the FileObject is referenced.
  1250. //
  1251. if (PrivateCacheMap != NULL) {
  1252. ExAcquireSpinLockAtDpcLevel( &PrivateCacheMap->ReadAheadSpinLock );
  1253. //
  1254. // We are done when the lengths are 0
  1255. //
  1256. Done = ((PrivateCacheMap->ReadAheadLength[0] |
  1257. PrivateCacheMap->ReadAheadLength[1]) == 0);
  1258. ReadAheadOffset[0] = PrivateCacheMap->ReadAheadOffset[0];
  1259. ReadAheadOffset[1] = PrivateCacheMap->ReadAheadOffset[1];
  1260. ReadAheadLength[0] = PrivateCacheMap->ReadAheadLength[0];
  1261. ReadAheadLength[1] = PrivateCacheMap->ReadAheadLength[1];
  1262. PrivateCacheMap->ReadAheadLength[0] = 0;
  1263. PrivateCacheMap->ReadAheadLength[1] = 0;
  1264. ExReleaseSpinLockFromDpcLevel( &PrivateCacheMap->ReadAheadSpinLock );
  1265. }
  1266. CcReleaseMasterLock( OldIrql );
  1267. //
  1268. // Acquire the file shared.
  1269. //
  1270. ResourceHeld = (*Callbacks->AcquireForReadAhead)( Context, TRUE );
  1271. if ((PrivateCacheMap == NULL) || Done || !ResourceHeld) {
  1272. try_return( NOTHING );
  1273. }
  1274. //
  1275. // PERFORM READ AHEAD
  1276. //
  1277. //
  1278. // Now loop until everything is read in. The Read ahead is accomplished
  1279. // by touching the pages with an appropriate ReadAhead parameter in MM.
  1280. //
  1281. i = 0;
  1282. do {
  1283. LARGE_INTEGER Offset, SavedOffset;
  1284. ULONG Length, SavedLength;
  1285. Offset = ReadAheadOffset[i];
  1286. Length = ReadAheadLength[i];
  1287. SavedOffset = Offset;
  1288. SavedLength = Length;
  1289. if ((Length != 0)
  1290. &&
  1291. ( Offset.QuadPart <= SharedCacheMap->FileSize.QuadPart )) {
  1292. ReadAheadPerformed = TRUE;
  1293. //
  1294. // Keep length within file and MAX_READ_AHEAD
  1295. //
  1296. if ( ( Offset.QuadPart + (LONGLONG)Length ) >= SharedCacheMap->FileSize.QuadPart ) {
  1297. Length = (ULONG)( SharedCacheMap->FileSize.QuadPart - Offset.QuadPart );
  1298. HitEof = TRUE;
  1299. }
  1300. if (Length > MAX_READ_AHEAD) {
  1301. Length = MAX_READ_AHEAD;
  1302. }
  1303. //
  1304. // Now loop to read all of the desired data in. This loop
  1305. // is more or less like the same loop to read data in
  1306. // CcCopyRead, except that we do not copy anything, just
  1307. // unmap as soon as it is in.
  1308. //
  1309. while (Length != 0) {
  1310. ULONG ReceivedLength;
  1311. PVOID CacheBuffer;
  1312. ULONG PagesToGo;
  1313. //
  1314. // Call local routine to Map or Access the file data.
  1315. // If we cannot map the data because of a Wait condition,
  1316. // return FALSE.
  1317. //
  1318. // Since this routine is intended to be called from
  1319. // the finally handler from file system read modules,
  1320. // it is imperative that it not raise any exceptions.
  1321. // Therefore, if any expected exception is raised, we
  1322. // will simply get out.
  1323. //
  1324. CacheBuffer = CcGetVirtualAddress( SharedCacheMap,
  1325. Offset,
  1326. &Vacb,
  1327. &ReceivedLength );
  1328. //
  1329. // If we got more than we need, make sure to only transfer
  1330. // the right amount.
  1331. //
  1332. if (ReceivedLength > Length) {
  1333. ReceivedLength = Length;
  1334. }
  1335. //
  1336. // Now loop to touch all of the pages, calling MM to insure
  1337. // that if we fault, we take in exactly the number of pages
  1338. // we need.
  1339. //
  1340. PagesToGo = ADDRESS_AND_SIZE_TO_SPAN_PAGES( CacheBuffer,
  1341. ReceivedLength );
  1342. CcMissCounter = &CcReadAheadIos;
  1343. while (PagesToGo) {
  1344. MmSetPageFaultReadAhead( Thread, (PagesToGo - 1) );
  1345. FaultOccurred |= !MmCheckCachedPageState(CacheBuffer, FALSE);
  1346. CacheBuffer = (PCHAR)CacheBuffer + PAGE_SIZE;
  1347. PagesToGo -= 1;
  1348. }
  1349. CcMissCounter = &CcThrowAway;
  1350. //
  1351. // Calculate how much data we have left to go.
  1352. //
  1353. Length -= ReceivedLength;
  1354. //
  1355. // Assume we did not get all the data we wanted, and set
  1356. // Offset to the end of the returned data.
  1357. //
  1358. Offset.QuadPart = Offset.QuadPart + (LONGLONG)ReceivedLength;
  1359. //
  1360. // It was only a page, so we can just leave this loop
  1361. // After freeing the address.
  1362. //
  1363. CcFreeVirtualAddress( Vacb );
  1364. Vacb = NULL;
  1365. }
  1366. }
  1367. i += 1;
  1368. } while (i <= 1);
  1369. //
  1370. // Release the file
  1371. //
  1372. (*Callbacks->ReleaseFromReadAhead)( Context );
  1373. ResourceHeld = FALSE;
  1374. }
  1375. try_exit: NOTHING;
  1376. }
  1377. finally {
  1378. MmResetPageFaultReadAhead(Thread, SavedState);
  1379. CcMissCounter = &CcThrowAway;
  1380. //
  1381. // If we got an error faulting a single page in, release the Vacb
  1382. // here. It is important to free any mapping before dropping the
  1383. // resource to prevent purge problems.
  1384. //
  1385. if (Vacb != NULL) {
  1386. CcFreeVirtualAddress( Vacb );
  1387. }
  1388. //
  1389. // Release the file
  1390. //
  1391. if (ResourceHeld) {
  1392. (*Callbacks->ReleaseFromReadAhead)( Context );
  1393. }
  1394. //
  1395. // To show we are done, we must make sure the PrivateCacheMap is
  1396. // still there.
  1397. //
  1398. CcAcquireMasterLock( &OldIrql );
  1399. PrivateCacheMap = FileObject->PrivateCacheMap;
  1400. //
  1401. // Show readahead is going inactive.
  1402. //
  1403. if (PrivateCacheMap != NULL) {
  1404. ExAcquireSpinLockAtDpcLevel( &PrivateCacheMap->ReadAheadSpinLock );
  1405. CC_CLEAR_PRIVATE_CACHE_MAP (PrivateCacheMap, PRIVATE_CACHE_MAP_READ_AHEAD_ACTIVE);
  1406. //
  1407. // If he said sequential only and we smashed into Eof, then
  1408. // let's reset the highwater mark in case he wants to read the
  1409. // file sequentially again.
  1410. //
  1411. if (HitEof && FlagOn(FileObject->Flags, FO_SEQUENTIAL_ONLY)) {
  1412. PrivateCacheMap->ReadAheadOffset[1].LowPart =
  1413. PrivateCacheMap->ReadAheadOffset[1].HighPart = 0;
  1414. }
  1415. //
  1416. // If no faults occurred, turn read ahead off.
  1417. //
  1418. if (ReadAheadPerformed && !FaultOccurred) {
  1419. CC_CLEAR_PRIVATE_CACHE_MAP (PrivateCacheMap, PRIVATE_CACHE_MAP_READ_AHEAD_ENABLED);
  1420. }
  1421. ExReleaseSpinLockFromDpcLevel( &PrivateCacheMap->ReadAheadSpinLock );
  1422. }
  1423. //
  1424. // Free SharedCacheMap list
  1425. //
  1426. CcReleaseMasterLock( OldIrql );
  1427. ObDereferenceObject( FileObject );
  1428. //
  1429. // Serialize again to decrement the open count.
  1430. //
  1431. CcAcquireMasterLock( &OldIrql );
  1432. CcDecrementOpenCount( SharedCacheMap, 'adRP' );
  1433. if ((SharedCacheMap->OpenCount == 0) &&
  1434. !FlagOn(SharedCacheMap->Flags, WRITE_QUEUED) &&
  1435. (SharedCacheMap->DirtyPages == 0)) {
  1436. //
  1437. // Move to the dirty list.
  1438. //
  1439. RemoveEntryList( &SharedCacheMap->SharedCacheMapLinks );
  1440. InsertTailList( &CcDirtySharedCacheMapList.SharedCacheMapLinks,
  1441. &SharedCacheMap->SharedCacheMapLinks );
  1442. //
  1443. // Make sure the Lazy Writer will wake up, because we
  1444. // want him to delete this SharedCacheMap.
  1445. //
  1446. LazyWriter.OtherWork = TRUE;
  1447. if (!LazyWriter.ScanActive) {
  1448. CcScheduleLazyWriteScan( FALSE );
  1449. }
  1450. }
  1451. CcReleaseMasterLock( OldIrql );
  1452. }
  1453. DebugTrace(-1, me, "CcPerformReadAhead -> VOID\n", 0 );
  1454. return;
  1455. }
  1456. PBITMAP_RANGE
  1457. CcFindBitmapRangeToDirty (
  1458. IN PMBCB Mbcb,
  1459. IN LONGLONG Page,
  1460. IN PULONG *FreePageForSetting
  1461. )
  1462. /*++
  1463. Routine Description:
  1464. This routine looks for the bitmap range containing the specified page.
  1465. If it is found it is returned so the caller can set some dirty bits.
  1466. If it is not found, then an attempt is made to come up with a free range
  1467. and set it up to describe the desired range. To come up with a free range,
  1468. first we attempt to recycle the lowest range that does not currently contain
  1469. any dirty pages. If there is no such range, then we allocate one.
  1470. Arguments:
  1471. Mbcb - Supplies the Mbcb in which to find the range.
  1472. Page - Supplies the page number for the first page to be set dirty.
  1473. FreePageForSetting - Supplies a free bitmap page of zeros from the zone; the
  1474. caller's pointer is cleared on return if this page is used.
  1475. Return Value:
  1476. The desired bitmap range, or NULL if one could not be allocated.
  1477. Environment:
  1478. The BcbSpinLock must be held on entry.
  1479. --*/
  1480. {
  1481. PBITMAP_RANGE BitmapRange, FreeRange;
  1482. PLIST_ENTRY InsertPoint;
  1483. LONGLONG BasePage;
  1484. //
  1485. // Initialize FreeRange and InsertPoint for the case we have
  1486. // to initialize a range.
  1487. //
  1488. FreeRange = NULL;
  1489. InsertPoint = &Mbcb->BitmapRanges;
  1490. //
  1491. // Point to the first bitmap range.
  1492. //
  1493. BitmapRange = (PBITMAP_RANGE)InsertPoint->Flink;
  1494. //
  1495. // Calculate the desired BasePage from the caller's page.
  1496. //
  1497. BasePage = (Page & ~(LONGLONG)((MBCB_BITMAP_BLOCK_SIZE * 8) - 1));
  1498. //
  1499. // Loop through the list until we find the range or we have a free range
  1500. // and correct insertion point.
  1501. //
  1502. do {
  1503. //
  1504. // If we get an exact match, then we must have hit a fully-initialized
  1505. // range which we can return.
  1506. //
  1507. if (BasePage == BitmapRange->BasePage) {
  1508. return BitmapRange;
  1509. //
  1510. // Otherwise, see if the range is free and we have not captured a
  1511. // free range yet.
  1512. //
  1513. } else if ((BitmapRange->DirtyPages == 0) && (FreeRange == NULL)) {
  1514. FreeRange = BitmapRange;
  1515. //
  1516. // If we did not capture a free range, see if we need to update our
  1517. // insertion point.
  1518. //
  1519. } else if (BasePage > BitmapRange->BasePage) {
  1520. InsertPoint = &BitmapRange->Links;
  1521. }
  1522. //
  1523. // Advance to the next range (or possibly back to the listhead).
  1524. //
  1525. BitmapRange = (PBITMAP_RANGE)BitmapRange->Links.Flink;
  1526. //
  1527. // Loop until we hit the end, or we know we are done updating both InsertPoint
  1528. // and FreeRange.
  1529. //
  1530. } while ((BitmapRange != (PBITMAP_RANGE)&Mbcb->BitmapRanges) &&
  1531. ((BasePage >= BitmapRange->BasePage) ||
  1532. (FreeRange == NULL)));
  1533. //
  1534. // If we found a FreeRange we can use, then remove it from the list.
  1535. //
  1536. if (FreeRange != NULL) {
  1537. RemoveEntryList( &FreeRange->Links );
  1538. //
  1539. // Otherwise we have to allocate the small bitmap range structure. We usually
  1540. // try to avoid calling the pool package while owning a spin lock, but note the
  1541. // following things which must be true if we hit this point:
  1542. //
  1543. // The file is larger than 3 bitmap ranges (normally 384MB on Intel).
  1544. // Three ranges plus all previously allocated ranges are simultaneously dirty.
  1545. //
  1546. // The second point is fairly unlikely, especially for a sequential writer. It
  1547. // can occur for a random writer in a large file, but eventually we will allocate
  1548. // enough ranges to always describe how many ranges he can keep dirty at once!
  1549. //
  1550. } else {
  1551. FreeRange = ExAllocatePoolWithTag( NonPagedPool, sizeof(BITMAP_RANGE), 'rBcC' );
  1552. if (FreeRange == NULL) {
  1553. return NULL;
  1554. }
  1555. RtlZeroMemory( FreeRange, sizeof(BITMAP_RANGE) );
  1556. }
  1557. //
  1558. // Insert and initialize.
  1559. //
  1560. InsertHeadList( InsertPoint, &FreeRange->Links );
  1561. FreeRange->BasePage = BasePage;
  1562. FreeRange->FirstDirtyPage = MAXULONG;
  1563. FreeRange->LastDirtyPage = 0;
  1564. //
  1565. // If the range does not have a bitmap yet, then consume the one we were passed
  1566. // in.
  1567. //
  1568. if (FreeRange->Bitmap == NULL) {
  1569. ASSERT(*FreePageForSetting != NULL);
  1570. FreeRange->Bitmap = *FreePageForSetting;
  1571. *FreePageForSetting = NULL;
  1572. }
  1573. return FreeRange;
  1574. }
  1575. PBITMAP_RANGE
  1576. CcFindBitmapRangeToClean (
  1577. IN PMBCB Mbcb,
  1578. IN LONGLONG Page
  1579. )
  1580. /*++
  1581. Routine Description:
  1582. This routine starts from the specified page, and looks for a range with dirty
  1583. pages. The caller must guarantee that some range exists with dirty pages. If
  1584. the end of the ranges is hit before finding any dirty ranges, then this routine
  1585. loops back to the start of the range list.
  1586. Arguments:
  1587. Mbcb - Supplies the Mbcb in which to find the range.
  1588. Page - Supplies the page number for the first page to scan from.
  1589. Return Value:
  1590. The desired bitmap range with dirty pages.
  1591. Environment:
  1592. The BcbSpinLock must be held on entry.
  1593. --*/
  1594. {
  1595. PBITMAP_RANGE BitmapRange;
  1596. //
  1597. // Point to the first bitmap range.
  1598. //
  1599. BitmapRange = (PBITMAP_RANGE)Mbcb->BitmapRanges.Flink;
  1600. //
  1601. // Loop through the list until we find the range to return.
  1602. //
  1603. do {
  1604. //
  1605. // If we hit the listhead, then wrap to find the first dirty range.
  1606. //
  1607. if (BitmapRange == (PBITMAP_RANGE)&Mbcb->BitmapRanges) {
  1608. //
  1609. // If Page is already 0, we are in an infinite loop.
  1610. //
  1611. ASSERT(Page != 0);
  1612. //
  1613. // Clear Page and fall through to advance to first range.
  1614. //
  1615. Page = 0;
  1616. //
  1617. // Otherwise, if we are in range, return the first range
  1618. // with dirty pages.
  1619. //
  1620. } else if ((Page <= (BitmapRange->BasePage + BitmapRange->LastDirtyPage)) &&
  1621. (BitmapRange->DirtyPages != 0)) {
  1622. return BitmapRange;
  1623. }
  1624. //
  1625. // Advance to the next range (or possibly back to the listhead).
  1626. //
  1627. BitmapRange = (PBITMAP_RANGE)BitmapRange->Links.Flink;
  1628. } while (TRUE);
  1629. }
  1630. VOID
  1631. CcSetDirtyInMask (
  1632. IN PSHARED_CACHE_MAP SharedCacheMap,
  1633. IN PLARGE_INTEGER FileOffset,
  1634. IN ULONG Length
  1635. )
  1636. /*++
  1637. Routine Description:
  1638. This routine may be called to set a range of pages dirty in a user data
  1639. file, by just setting the corresponding bits in the mask bcb.
  1640. IMPORTANT NOTE:
  1641. If this routine fails to set any bits due to an allocation failure,
  1642. it just returns quietly without informing the caller. (Note that this
  1643. routine is never called for no modified write sections.) The reason
  1644. for this behavior is that this routine is sometimes called as part of
  1645. error recovery (CcFreeActiveVacb, CcMdlWriteComplete, etc.) when it is
  1646. essential to just keep on moving. Note that if an allocation failure does
  1647. occur, this only means that MM will have to flush the modified page in
  1648. time, since the Lazy Writer will not do it.
  1649. Arguments:
  1650. SharedCacheMap - SharedCacheMap where the pages are to be set dirty.
  1651. FileOffset - FileOffset of first page to set dirty
  1652. Length - Used in conjunction with FileOffset to determine how many pages
  1653. to set dirty.
  1654. Return Value:
  1655. None
  1656. --*/
  1657. {
  1658. KLOCK_QUEUE_HANDLE LockHandle;
  1659. PMBCB Mbcb;
  1660. PBITMAP_RANGE BitmapRange;
  1661. LONGLONG FirstPage;
  1662. LONGLONG LastPage;
  1663. PULONG MaskPtr;
  1664. ULONG Mask = 0;
  1665. PULONG Bitmap = NULL;
  1666. //
  1667. // We assume no caller can cross a bitmap range boundary (currently not even
  1668. // a view boundary!), so we do not want to loop through bitmap ranges.
  1669. //
  1670. ASSERT((FileOffset->QuadPart / MBCB_BITMAP_RANGE) ==
  1671. ((FileOffset->QuadPart + Length - 1) / MBCB_BITMAP_RANGE));
  1672. //
  1673. // Initialize our locals.
  1674. //
  1675. FirstPage = FileOffset->QuadPart >> PAGE_SHIFT;
  1676. LastPage = ((FileOffset->QuadPart + Length - 1) >> PAGE_SHIFT);
  1677. //
  1678. // PREfix correctly notes that Mbcb grande promotion test and the one
  1679. // that decides to preallocate the bitmap buffer ever disagree, we will
  1680. // be able to have a NULL Bitmap and die. This will not happen since we
  1681. // guarantee that section size >= filesize. Assert this case, and we will
  1682. // also assert that Bitmap is never NULL when needed - this should convince
  1683. // PREfix we're OK.
  1684. //
  1685. ASSERT( (SharedCacheMap->SectionSize.QuadPart / PAGE_SIZE) > LastPage );
  1686. //
  1687. // If we have to convert to an Mbcb grande, we will loop back here to
  1688. // preallocate another buffer.
  1689. //
  1690. do {
  1691. //
  1692. // For large streams, we need to preallocate a block we use for
  1693. // we use for bitmaps. We allocate one, then loop back in the rare
  1694. // case where we will need another. We free it at the bottom if we
  1695. // don't need one.
  1696. //
  1697. if (SharedCacheMap->SectionSize.QuadPart > (MBCB_BITMAP_INITIAL_SIZE * 8 * PAGE_SIZE)) {
  1698. //
  1699. // If we could not preallocate, break out into common cleanup code and
  1700. // return quietly.
  1701. //
  1702. if (!CcPrefillVacbLevelZone( 1, &LockHandle.OldIrql, FALSE )) {
  1703. return;
  1704. }
  1705. Bitmap = (PULONG)CcAllocateVacbLevel( FALSE );
  1706. CcReleaseVacbLock( LockHandle.OldIrql );
  1707. }
  1708. //
  1709. // Acquire the Mbcb spinlock.
  1710. //
  1711. KeAcquireInStackQueuedSpinLock( &SharedCacheMap->BcbSpinLock, &LockHandle );
  1712. //
  1713. // If there is no Mbcb, we will have to allocate one.
  1714. //
  1715. Mbcb = SharedCacheMap->Mbcb;
  1716. if (Mbcb == NULL) {
  1717. //
  1718. // Since we use the Bcb zone, we must assume that Bcbs are big enough.
  1719. //
  1720. ASSERT(QuadAlign(sizeof(MBCB)) <= QuadAlign(sizeof(BCB)));
  1721. //
  1722. // Allocate the Mbcb from the Bcb zone.
  1723. //
  1724. Mbcb = (PMBCB)CcAllocateInitializeBcb( NULL, NULL, NULL, NULL );
  1725. //
  1726. // If we could not allocate an Mbcb, break out to clean up and return
  1727. //
  1728. if (Mbcb == NULL) {
  1729. break;
  1730. }
  1731. //
  1732. // Set in the node type, and initialize the listhead of ranges.
  1733. //
  1734. Mbcb->NodeTypeCode = CACHE_NTC_MBCB;
  1735. InitializeListHead( &Mbcb->BitmapRanges );
  1736. //
  1737. // Insert and initialize the first range.
  1738. //
  1739. InsertTailList( &Mbcb->BitmapRanges, &Mbcb->BitmapRange1.Links );
  1740. Mbcb->BitmapRange1.FirstDirtyPage = MAXULONG;
  1741. //
  1742. // Use the rest of the Mbcb as the initial bitmap.
  1743. //
  1744. Mbcb->BitmapRange1.Bitmap = (PULONG)&Mbcb->BitmapRange2;
  1745. //
  1746. // Now set to use our new Mbcb.
  1747. //
  1748. SharedCacheMap->Mbcb = Mbcb;
  1749. }
  1750. //
  1751. // Now see if we need to switch to the Mbcb grande format.
  1752. //
  1753. if ((LastPage >= (MBCB_BITMAP_INITIAL_SIZE * 8)) &&
  1754. (Mbcb->NodeTypeCode != CACHE_NTC_MBCB_GRANDE)) {
  1755. ASSERT( Bitmap != NULL );
  1756. //
  1757. // If there are any dirty pages, copy the initial bitmap over, and zero
  1758. // out the original end of the Mbcb for reuse.
  1759. //
  1760. if (Mbcb->BitmapRange1.DirtyPages != 0) {
  1761. RtlCopyMemory( Bitmap, Mbcb->BitmapRange1.Bitmap, MBCB_BITMAP_INITIAL_SIZE );
  1762. RtlZeroMemory( Mbcb->BitmapRange1.Bitmap, MBCB_BITMAP_INITIAL_SIZE );
  1763. }
  1764. //
  1765. // Store the new bitmap pointer and show we have consumed this one.
  1766. //
  1767. Mbcb->BitmapRange1.Bitmap = Bitmap;
  1768. Bitmap = NULL;
  1769. //
  1770. // Insert and initialize the first range.
  1771. //
  1772. InsertTailList( &Mbcb->BitmapRanges, &Mbcb->BitmapRange2.Links );
  1773. Mbcb->BitmapRange2.BasePage = MAXLONGLONG;
  1774. Mbcb->BitmapRange2.FirstDirtyPage = MAXULONG;
  1775. InsertTailList( &Mbcb->BitmapRanges, &Mbcb->BitmapRange3.Links );
  1776. Mbcb->BitmapRange3.BasePage = MAXLONGLONG;
  1777. Mbcb->BitmapRange3.FirstDirtyPage = MAXULONG;
  1778. Mbcb->NodeTypeCode = CACHE_NTC_MBCB_GRANDE;
  1779. //
  1780. // This is a one-time event - converting to the large Mbcb. Continue back
  1781. // to preallocate another buffer for CcFindBitmapRangeToDirty.
  1782. //
  1783. KeReleaseInStackQueuedSpinLock( &LockHandle );
  1784. continue;
  1785. }
  1786. //
  1787. // Now find the Bitmap range we are setting bits in.
  1788. //
  1789. BitmapRange = CcFindBitmapRangeToDirty( Mbcb, FirstPage, &Bitmap );
  1790. //
  1791. // If we could not allocate this dinky structure, break out quietly.
  1792. //
  1793. if (BitmapRange == NULL) {
  1794. break;
  1795. }
  1796. //
  1797. // Now update the first and last dirty page indices and the bitmap.
  1798. //
  1799. if (FirstPage < (BitmapRange->BasePage + BitmapRange->FirstDirtyPage)) {
  1800. BitmapRange->FirstDirtyPage = (ULONG)(FirstPage - BitmapRange->BasePage);
  1801. }
  1802. if (LastPage > (BitmapRange->BasePage + BitmapRange->LastDirtyPage)) {
  1803. BitmapRange->LastDirtyPage = (ULONG)(LastPage - BitmapRange->BasePage);
  1804. }
  1805. //
  1806. // We have to acquire the shared cache map list, because we
  1807. // may be changing lists.
  1808. //
  1809. CcAcquireMasterLockAtDpcLevel();
  1810. //
  1811. // If this is the first dirty page for this cache map, there is some work
  1812. // to do.
  1813. //
  1814. if (SharedCacheMap->DirtyPages == 0) {
  1815. //
  1816. // If the lazy write scan is not active, then start it.
  1817. //
  1818. if (!LazyWriter.ScanActive) {
  1819. CcScheduleLazyWriteScan( FALSE );
  1820. }
  1821. //
  1822. // Move to the dirty list.
  1823. //
  1824. RemoveEntryList( &SharedCacheMap->SharedCacheMapLinks );
  1825. InsertTailList( &CcDirtySharedCacheMapList.SharedCacheMapLinks,
  1826. &SharedCacheMap->SharedCacheMapLinks );
  1827. Mbcb->ResumeWritePage = FirstPage;
  1828. }
  1829. MaskPtr = &BitmapRange->Bitmap[(ULONG)(FirstPage - BitmapRange->BasePage) / 32];
  1830. Mask = 1 << ((ULONG)FirstPage % 32);
  1831. //
  1832. // Loop to set all of the bits and adjust the DirtyPage totals.
  1833. //
  1834. for ( ; FirstPage <= LastPage; FirstPage++) {
  1835. if ((*MaskPtr & Mask) == 0) {
  1836. CcChargeMaskDirtyPages( SharedCacheMap, Mbcb, BitmapRange, 1 );
  1837. *MaskPtr |= Mask;
  1838. }
  1839. Mask <<= 1;
  1840. if (Mask == 0) {
  1841. MaskPtr += 1;
  1842. Mask = 1;
  1843. }
  1844. }
  1845. //
  1846. // See if we need to advance our goal for ValidDataLength.
  1847. //
  1848. LastPage = FileOffset->QuadPart + Length;
  1849. if (LastPage > SharedCacheMap->ValidDataGoal.QuadPart) {
  1850. SharedCacheMap->ValidDataGoal.QuadPart = (LONGLONG)LastPage;
  1851. }
  1852. CcReleaseMasterLockFromDpcLevel();
  1853. //
  1854. // Continue until we have actually set the bits (there is a continue
  1855. // which just wants to loop back and allocate another buffer).
  1856. //
  1857. } while (Mask == 0);
  1858. //
  1859. // Now if we preallocated a bitmap buffer, free it on the way out.
  1860. //
  1861. if (Bitmap != NULL) {
  1862. CcAcquireVacbLockAtDpcLevel();
  1863. CcDeallocateVacbLevel( (PVACB *)Bitmap, FALSE );
  1864. CcReleaseVacbLockFromDpcLevel();
  1865. }
  1866. KeReleaseInStackQueuedSpinLock( &LockHandle );
  1867. }
  1868. VOID
  1869. CcSetDirtyPinnedData (
  1870. IN PVOID BcbVoid,
  1871. IN PLARGE_INTEGER Lsn OPTIONAL
  1872. )
  1873. /*++
  1874. Routine Description:
  1875. This routine may be called to set a Bcb (returned by CcPinFileData)
  1876. dirty, and a candidate for the Lazy Writer. All Bcbs should be set
  1877. dirty by calling this routine, even if they are to be flushed
  1878. another way.
  1879. Arguments:
  1880. Bcb - Supplies a pointer to a pinned (by CcPinFileData) Bcb, to
  1881. be set dirty.
  1882. Lsn - Lsn to be remembered with page.
  1883. Return Value:
  1884. None
  1885. --*/
  1886. {
  1887. PBCB Bcbs[2];
  1888. PBCB *BcbPtrPtr;
  1889. KLOCK_QUEUE_HANDLE LockHandle;
  1890. PSHARED_CACHE_MAP SharedCacheMap;
  1891. DebugTrace(+1, me, "CcSetDirtyPinnedData: Bcb = %08lx\n", BcbVoid );
  1892. //
  1893. // Assume this is a normal Bcb, and set up for loop below.
  1894. //
  1895. Bcbs[0] = (PBCB)BcbVoid;
  1896. Bcbs[1] = NULL;
  1897. BcbPtrPtr = &Bcbs[0];
  1898. //
  1899. // If it is an overlap Bcb, then point into the Bcb vector
  1900. // for the loop.
  1901. //
  1902. if (Bcbs[0]->NodeTypeCode == CACHE_NTC_OBCB) {
  1903. BcbPtrPtr = &((POBCB)Bcbs[0])->Bcbs[0];
  1904. }
  1905. //
  1906. // Loop to set all Bcbs dirty
  1907. //
  1908. while (*BcbPtrPtr != NULL) {
  1909. Bcbs[0] = *(BcbPtrPtr++);
  1910. //
  1911. // Should be no ReadOnly Bcbs
  1912. //
  1913. ASSERT(((ULONG_PTR)Bcbs[0] & 1) != 1);
  1914. SharedCacheMap = Bcbs[0]->SharedCacheMap;
  1915. //
  1916. // We have to acquire the shared cache map list, because we
  1917. // may be changing lists.
  1918. //
  1919. KeAcquireInStackQueuedSpinLock( &SharedCacheMap->BcbSpinLock, &LockHandle );
  1920. if (!Bcbs[0]->Dirty) {
  1921. ULONG Pages = Bcbs[0]->ByteLength >> PAGE_SHIFT;
  1922. //
  1923. // Set dirty to keep the Bcb from going away until
  1924. // it is set Undirty, and assign the next modification time stamp.
  1925. //
  1926. Bcbs[0]->Dirty = TRUE;
  1927. //
  1928. // Initialize the OldestLsn field.
  1929. //
  1930. if (ARGUMENT_PRESENT(Lsn)) {
  1931. Bcbs[0]->OldestLsn = *Lsn;
  1932. Bcbs[0]->NewestLsn = *Lsn;
  1933. }
  1934. //
  1935. // Move it to the dirty list if these are the first dirty pages,
  1936. // and this is not disabled for write behind.
  1937. //
  1938. // Increase the count of dirty bytes in the shared cache map.
  1939. //
  1940. CcAcquireMasterLockAtDpcLevel();
  1941. if ((SharedCacheMap->DirtyPages == 0) &&
  1942. !FlagOn(SharedCacheMap->Flags, DISABLE_WRITE_BEHIND)) {
  1943. //
  1944. // If the lazy write scan is not active, then start it.
  1945. //
  1946. if (!LazyWriter.ScanActive) {
  1947. CcScheduleLazyWriteScan( FALSE );
  1948. }
  1949. RemoveEntryList( &SharedCacheMap->SharedCacheMapLinks );
  1950. InsertTailList( &CcDirtySharedCacheMapList.SharedCacheMapLinks,
  1951. &SharedCacheMap->SharedCacheMapLinks );
  1952. }
  1953. CcChargePinDirtyPages( SharedCacheMap, Pages );
  1954. CcReleaseMasterLockFromDpcLevel();
  1955. }
  1956. //
  1957. // If this Lsn happens to be older/newer than the ones we have stored, then
  1958. // change it.
  1959. //
  1960. if (ARGUMENT_PRESENT(Lsn)) {
  1961. if ((Bcbs[0]->OldestLsn.QuadPart == 0) || (Lsn->QuadPart < Bcbs[0]->OldestLsn.QuadPart)) {
  1962. Bcbs[0]->OldestLsn = *Lsn;
  1963. }
  1964. if (Lsn->QuadPart > Bcbs[0]->NewestLsn.QuadPart) {
  1965. Bcbs[0]->NewestLsn = *Lsn;
  1966. }
  1967. }
  1968. //
  1969. // See if we need to advance our goal for ValidDataLength.
  1970. //
  1971. if ( Bcbs[0]->BeyondLastByte.QuadPart > SharedCacheMap->ValidDataGoal.QuadPart ) {
  1972. SharedCacheMap->ValidDataGoal = Bcbs[0]->BeyondLastByte;
  1973. }
  1974. KeReleaseInStackQueuedSpinLock( &LockHandle );
  1975. }
  1976. DebugTrace(-1, me, "CcSetDirtyPinnedData -> VOID\n", 0 );
  1977. }
  1978. NTSTATUS
  1979. CcSetValidData (
  1980. IN PFILE_OBJECT FileObject,
  1981. IN PLARGE_INTEGER ValidDataLength
  1982. )
  1983. /*++
  1984. Routine Description:
  1985. This routine is used to call the File System to update ValidDataLength
  1986. for a file.
  1987. Arguments:
  1988. FileObject - A pointer to a referenced file object describing which file
  1989. the read should be performed from.
  1990. ValidDataLength - Pointer to new ValidDataLength.
  1991. Return Value:
  1992. Status of operation.
  1993. --*/
  1994. {
  1995. PIO_STACK_LOCATION IrpSp;
  1996. PDEVICE_OBJECT DeviceObject;
  1997. NTSTATUS Status;
  1998. FILE_END_OF_FILE_INFORMATION Buffer;
  1999. IO_STATUS_BLOCK IoStatus;
  2000. KEVENT Event;
  2001. PIRP Irp;
  2002. DebugTrace(+1, me, "CcSetValidData:\n", 0 );
  2003. DebugTrace( 0, me, " FileObject = %08lx\n", FileObject );
  2004. DebugTrace2(0, me, " ValidDataLength = %08lx, %08lx\n",
  2005. ValidDataLength->LowPart, ValidDataLength->HighPart );
  2006. //
  2007. // Copy ValidDataLength to our buffer.
  2008. //
  2009. Buffer.EndOfFile = *ValidDataLength;
  2010. //
  2011. // Initialize the event.
  2012. //
  2013. KeInitializeEvent( &Event, NotificationEvent, FALSE );
  2014. //
  2015. // Begin by getting a pointer to the device object that the file resides
  2016. // on.
  2017. //
  2018. DeviceObject = IoGetRelatedDeviceObject( FileObject );
  2019. //
  2020. // Allocate an I/O Request Packet (IRP) for this in-page operation.
  2021. //
  2022. Irp = IoAllocateIrp( DeviceObject->StackSize, FALSE );
  2023. if (Irp == NULL) {
  2024. DebugTrace(-1, me, "CcSetValidData-> STATUS_INSUFFICIENT_RESOURCES\n", 0 );
  2025. return STATUS_INSUFFICIENT_RESOURCES;
  2026. }
  2027. //
  2028. // Get a pointer to the first stack location in the packet. This location
  2029. // will be used to pass the function codes and parameters to the first
  2030. // driver.
  2031. //
  2032. IrpSp = IoGetNextIrpStackLocation( Irp );
  2033. //
  2034. // Fill in the IRP according to this request, setting the flags to
  2035. // just cause IO to set the event and deallocate the Irp.
  2036. //
  2037. Irp->Flags = IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO;
  2038. Irp->RequestorMode = KernelMode;
  2039. Irp->UserIosb = &IoStatus;
  2040. Irp->UserEvent = &Event;
  2041. Irp->Tail.Overlay.OriginalFileObject = FileObject;
  2042. Irp->Tail.Overlay.Thread = PsGetCurrentThread();
  2043. Irp->AssociatedIrp.SystemBuffer = &Buffer;
  2044. //
  2045. // Fill in the normal read parameters.
  2046. //
  2047. IrpSp->MajorFunction = IRP_MJ_SET_INFORMATION;
  2048. IrpSp->FileObject = FileObject;
  2049. IrpSp->DeviceObject = DeviceObject;
  2050. IrpSp->Parameters.SetFile.Length = sizeof(FILE_END_OF_FILE_INFORMATION);
  2051. IrpSp->Parameters.SetFile.FileInformationClass = FileEndOfFileInformation;
  2052. IrpSp->Parameters.SetFile.FileObject = NULL;
  2053. IrpSp->Parameters.SetFile.AdvanceOnly = TRUE;
  2054. //
  2055. // Queue the packet to the appropriate driver based on whether or not there
  2056. // is a VPB associated with the device. This routine should not raise.
  2057. //
  2058. Status = IoCallDriver( DeviceObject, Irp );
  2059. //
  2060. // If pending is returned (which is a successful status),
  2061. // we must wait for the request to complete.
  2062. //
  2063. if (Status == STATUS_PENDING) {
  2064. KeWaitForSingleObject( &Event,
  2065. Executive,
  2066. KernelMode,
  2067. FALSE,
  2068. (PLARGE_INTEGER)NULL);
  2069. }
  2070. //
  2071. // If we got an error back in Status, then the Iosb
  2072. // was not written, so we will just copy the status
  2073. // there, then test the final status after that.
  2074. //
  2075. if (!NT_SUCCESS(Status)) {
  2076. IoStatus.Status = Status;
  2077. }
  2078. DebugTrace(-1, me, "CcSetValidData-> %08lx\n", IoStatus.Status );
  2079. return IoStatus.Status;
  2080. }
  2081. //
  2082. // Internal Support Routine
  2083. //
  2084. BOOLEAN
  2085. CcAcquireByteRangeForWrite (
  2086. IN PSHARED_CACHE_MAP SharedCacheMap,
  2087. IN PLARGE_INTEGER TargetOffset OPTIONAL,
  2088. IN ULONG TargetLength,
  2089. OUT PLARGE_INTEGER FileOffset,
  2090. OUT PULONG Length,
  2091. OUT PBCB *FirstBcb
  2092. )
  2093. /*++
  2094. Routine Description:
  2095. This routine is called by the Lazy Writer to try to find a contiguous
  2096. range of bytes from the specified SharedCacheMap that are dirty and
  2097. should be flushed. After flushing, these bytes should be released
  2098. by calling CcReleaseByteRangeFromWrite.
  2099. Dirty ranges are returned in strictly increasing order.
  2100. Arguments:
  2101. SharedCacheMap - for the file for which the dirty byte range is sought
  2102. TargetOffset - If specified, then only the specified range is
  2103. to be flushed.
  2104. TargetLength - If target offset specified, this completes the range.
  2105. In any case, this field is zero for the Lazy Writer,
  2106. and nonzero for explicit flush calls.
  2107. FileOffset - Returns the offset for the beginning of the dirty byte
  2108. range to flush
  2109. Length - Returns the length of bytes in the range.
  2110. FirstBcb - Returns the first Bcb in the list for the range, to be used
  2111. when calling CcReleaseByteRangeFromWrite, or NULL if dirty
  2112. pages were found in the mask Bcb.
  2113. Return Value:
  2114. FALSE - if no dirty byte range could be found to match the necessary
  2115. criteria.
  2116. TRUE - if a dirty byte range is being returned.
  2117. --*/
  2118. {
  2119. KLOCK_QUEUE_HANDLE LockHandle;
  2120. PMBCB Mbcb;
  2121. PBCB Bcb;
  2122. LARGE_INTEGER LsnToFlushTo = {0, 0};
  2123. LOGICAL BcbLookasideCheck = FALSE;
  2124. PBITMAP_RANGE BitmapRange;
  2125. PULONG EndPtr;
  2126. PULONG MaskPtr;
  2127. ULONG Mask;
  2128. LONGLONG FirstDirtyPage;
  2129. ULONG OriginalFirstDirtyPage;
  2130. LONGLONG LastDirtyPage = MAXLONGLONG;
  2131. DebugTrace(+1, me, "CcAcquireByteRangeForWrite:\n", 0);
  2132. DebugTrace( 0, me, " SharedCacheMap = %08lx\n", SharedCacheMap);
  2133. //
  2134. // Initially clear outputs.
  2135. //
  2136. FileOffset->QuadPart = 0;
  2137. *Length = 0;
  2138. //
  2139. // We must acquire the SharedCacheMap->BcbSpinLock.
  2140. //
  2141. KeAcquireInStackQueuedSpinLock( &SharedCacheMap->BcbSpinLock, &LockHandle );
  2142. //
  2143. // See if there is a simple Mask Bcb, and if there is anything dirty in
  2144. // it. If so we will simply handle that case here by processing the bitmap.
  2145. //
  2146. Mbcb = SharedCacheMap->Mbcb;
  2147. if ((Mbcb != NULL) &&
  2148. (Mbcb->DirtyPages != 0) &&
  2149. ((Mbcb->PagesToWrite != 0) || (TargetLength != 0))) {
  2150. //
  2151. // If a target range was specified (outside call to CcFlush for a range),
  2152. // then calculate FirstPage and EndPtr based on these inputs.
  2153. //
  2154. if (ARGUMENT_PRESENT(TargetOffset)) {
  2155. FirstDirtyPage = TargetOffset->QuadPart >> PAGE_SHIFT;
  2156. LastDirtyPage = (TargetOffset->QuadPart + TargetLength - 1) >> PAGE_SHIFT;
  2157. //
  2158. // Find the bitmap range containing the first dirty page.
  2159. //
  2160. BitmapRange = CcFindBitmapRangeToClean( Mbcb, FirstDirtyPage );
  2161. //
  2162. // If the target range is not dirty, get out. We may have even
  2163. // gotten back a nonoverlapping bitmap range.
  2164. //
  2165. if ((LastDirtyPage < (BitmapRange->BasePage + BitmapRange->FirstDirtyPage)) ||
  2166. (FirstDirtyPage > (BitmapRange->BasePage + BitmapRange->LastDirtyPage))) {
  2167. goto Scan_Bcbs;
  2168. }
  2169. if (LastDirtyPage < (BitmapRange->BasePage + BitmapRange->LastDirtyPage)) {
  2170. EndPtr = &BitmapRange->Bitmap[(ULONG)(LastDirtyPage - BitmapRange->BasePage) / 32];
  2171. } else {
  2172. EndPtr = &BitmapRange->Bitmap[BitmapRange->LastDirtyPage / 32];
  2173. }
  2174. //
  2175. // Otherwise, for the Lazy Writer pick up where we left off.
  2176. //
  2177. } else {
  2178. //
  2179. // If a length was specified, then it is an explicit flush, and
  2180. // we want to start with the first dirty page, else the Lazy Writer
  2181. // starts from the ResumeWritePage.
  2182. //
  2183. FirstDirtyPage = 0;
  2184. if (TargetLength == 0) {
  2185. FirstDirtyPage = Mbcb->ResumeWritePage;
  2186. }
  2187. //
  2188. // Now find the next (cyclic) dirty page from this point.
  2189. //
  2190. BitmapRange = CcFindBitmapRangeToClean( Mbcb, FirstDirtyPage );
  2191. //
  2192. // If the page we thought we were looking for is beyond the last dirty page
  2193. // of this range, then CcFindBitmapRangeToClean must have wrapped back to
  2194. // the start of the file, and we should resume on the first dirty page of
  2195. // this range.
  2196. //
  2197. if (FirstDirtyPage > (BitmapRange->BasePage + BitmapRange->LastDirtyPage)) {
  2198. FirstDirtyPage = BitmapRange->BasePage + BitmapRange->FirstDirtyPage;
  2199. }
  2200. EndPtr = &BitmapRange->Bitmap[BitmapRange->LastDirtyPage / 32];
  2201. }
  2202. //
  2203. // Now we can skip over any clean pages.
  2204. //
  2205. if (FirstDirtyPage < (BitmapRange->BasePage + BitmapRange->FirstDirtyPage)) {
  2206. FirstDirtyPage = BitmapRange->BasePage + BitmapRange->FirstDirtyPage;
  2207. }
  2208. //
  2209. // Form a few other inputs for our dirty page scan.
  2210. //
  2211. MaskPtr = &BitmapRange->Bitmap[(ULONG)(FirstDirtyPage - BitmapRange->BasePage) / 32];
  2212. Mask = (ULONG)(-1 << (FirstDirtyPage % 32));
  2213. OriginalFirstDirtyPage = (ULONG)(FirstDirtyPage - BitmapRange->BasePage);
  2214. //
  2215. // Because of the possibility of getting stuck on a "hot spot" which gets
  2216. // modified over and over, we want to be very careful to resume exactly
  2217. // at the recorded resume point. If there is nothing there, then we
  2218. // fall into the loop below to scan for nozero long words in the bitmap,
  2219. // starting at the next longword.
  2220. //
  2221. if ((*MaskPtr & Mask) == 0) {
  2222. //
  2223. // Before entering loop, set all mask bits and insure we increment from
  2224. // an even Ulong boundary.
  2225. //
  2226. Mask = MAXULONG;
  2227. FirstDirtyPage &= ~31;
  2228. //
  2229. // To scan the bitmap faster, we scan for entire long words which are
  2230. // nonzero.
  2231. //
  2232. do {
  2233. MaskPtr += 1;
  2234. FirstDirtyPage += 32;
  2235. //
  2236. // If we go beyond the end, then we must wrap back to the first
  2237. // dirty page. We will just go back to the start of the first
  2238. // longword.
  2239. //
  2240. if (MaskPtr > EndPtr) {
  2241. //
  2242. // We can backup the last dirty page hint to where we
  2243. // started scanning, if we are the lazy writer.
  2244. //
  2245. if (TargetLength == 0) {
  2246. ASSERT(OriginalFirstDirtyPage >= BitmapRange->FirstDirtyPage);
  2247. BitmapRange->LastDirtyPage = OriginalFirstDirtyPage - 1;
  2248. }
  2249. //
  2250. // We hit the end of our scan. Let's assume we are supposed
  2251. // to move on to the next range with dirty pages.
  2252. //
  2253. do {
  2254. //
  2255. // Go to the next range.
  2256. //
  2257. BitmapRange = (PBITMAP_RANGE)BitmapRange->Links.Flink;
  2258. //
  2259. // Did we hit the listhead?
  2260. //
  2261. if (BitmapRange == (PBITMAP_RANGE)&Mbcb->BitmapRanges) {
  2262. //
  2263. // If this is an explicit flush, then it is time to
  2264. // get out.
  2265. //
  2266. if (TargetLength != 0) {
  2267. goto Scan_Bcbs;
  2268. }
  2269. //
  2270. // Otherwise, we must wrap back to the first range in the
  2271. // Lazy Writer Scan.
  2272. //
  2273. BitmapRange = (PBITMAP_RANGE)BitmapRange->Links.Flink;
  2274. }
  2275. } while (BitmapRange->DirtyPages == 0);
  2276. //
  2277. // Now we have a new range with dirty pages, but if this is
  2278. // an explicit flush of a specified range, we may be done.
  2279. //
  2280. if ((LastDirtyPage < (BitmapRange->BasePage + BitmapRange->FirstDirtyPage)) ||
  2281. (FirstDirtyPage > (BitmapRange->BasePage + BitmapRange->LastDirtyPage))) {
  2282. goto Scan_Bcbs;
  2283. }
  2284. //
  2285. // Otherwise, we need to set up our context to resume scanning in this
  2286. // range.
  2287. //
  2288. MaskPtr = &BitmapRange->Bitmap[BitmapRange->FirstDirtyPage / 32];
  2289. EndPtr = &BitmapRange->Bitmap[BitmapRange->LastDirtyPage / 32];
  2290. FirstDirtyPage = BitmapRange->BasePage + (BitmapRange->FirstDirtyPage & ~31);
  2291. OriginalFirstDirtyPage = BitmapRange->FirstDirtyPage;
  2292. }
  2293. } while (*MaskPtr == 0);
  2294. }
  2295. //
  2296. // Calculate the first set bit in the mask that we hit on.
  2297. //
  2298. Mask = ~Mask + 1;
  2299. //
  2300. // Now loop to find the first set bit.
  2301. //
  2302. while ((*MaskPtr & Mask) == 0) {
  2303. Mask <<= 1;
  2304. FirstDirtyPage += 1;
  2305. }
  2306. //
  2307. // If a TargetOffset was specified, then make sure we do not start
  2308. // beyond the specified range or a dirty Bcb in the range.
  2309. //
  2310. if (ARGUMENT_PRESENT(TargetOffset)) {
  2311. if (FirstDirtyPage >= ((TargetOffset->QuadPart + TargetLength + PAGE_SIZE - 1) >> PAGE_SHIFT)) {
  2312. goto Scan_Bcbs;
  2313. }
  2314. //
  2315. // If Bcbs are present on this file, we must go scan to see if they
  2316. // describe a range that must be written first. If this is not the
  2317. // case, we'll hop back and continue building the range from the mask Bcb.
  2318. //
  2319. // Note that this case will be very rare. Bcbs are introduced into user
  2320. // files in limited situations (CcZero) and the reverse is never allowed
  2321. // to happen.
  2322. //
  2323. if (!IsListEmpty(&SharedCacheMap->BcbList)) {
  2324. BcbLookasideCheck = TRUE;
  2325. goto Scan_Bcbs;
  2326. }
  2327. }
  2328. Accept_Page:
  2329. //
  2330. // Now loop to count the set bits at that point, clearing them as we
  2331. // go because we plan to write the corresponding pages. Stop as soon
  2332. // as we find a clean page, or we reach our maximum write size. Of
  2333. // course we want to ignore long word boundaries and keep trying to
  2334. // extend the write. We do not check for wrapping around the end of
  2335. // the bitmap here, because we guarantee some zero bits at the end
  2336. // in CcSetDirtyInMask.
  2337. //
  2338. while (((*MaskPtr & Mask) != 0) && (*Length < (MAX_WRITE_BEHIND / PAGE_SIZE)) &&
  2339. (!ARGUMENT_PRESENT(TargetOffset) || ((FirstDirtyPage + *Length) <
  2340. (ULONG)((TargetOffset->QuadPart + TargetLength + PAGE_SIZE - 1) >> PAGE_SHIFT)))) {
  2341. ASSERT(MaskPtr <= (&BitmapRange->Bitmap[BitmapRange->LastDirtyPage / 32]));
  2342. *MaskPtr -= Mask;
  2343. *Length += 1;
  2344. Mask <<= 1;
  2345. if (Mask == 0) {
  2346. MaskPtr += 1;
  2347. Mask = 1;
  2348. if (MaskPtr > EndPtr) {
  2349. break;
  2350. }
  2351. }
  2352. }
  2353. //
  2354. // Now reduce the count of pages we were supposed to write this time,
  2355. // possibly clearing this count.
  2356. //
  2357. if (*Length < Mbcb->PagesToWrite) {
  2358. Mbcb->PagesToWrite -= *Length;
  2359. } else {
  2360. Mbcb->PagesToWrite = 0;
  2361. }
  2362. //
  2363. // Reduce the dirty page counts by the number of pages we just cleared.
  2364. //
  2365. ASSERT(Mbcb->DirtyPages >= *Length);
  2366. Mbcb->DirtyPages -= *Length;
  2367. BitmapRange->DirtyPages -= *Length;
  2368. CcAcquireMasterLockAtDpcLevel();
  2369. CcDeductDirtyPages( SharedCacheMap, *Length );
  2370. //
  2371. // Normally we need to reduce CcPagesYetToWrite appropriately.
  2372. //
  2373. if (CcPagesYetToWrite > *Length) {
  2374. CcPagesYetToWrite -= *Length;
  2375. } else {
  2376. CcPagesYetToWrite = 0;
  2377. }
  2378. //
  2379. // If we took out the last dirty page, then move the SharedCacheMap
  2380. // back to the clean list.
  2381. //
  2382. if (SharedCacheMap->DirtyPages == 0) {
  2383. RemoveEntryList( &SharedCacheMap->SharedCacheMapLinks );
  2384. InsertTailList( &CcCleanSharedCacheMapList,
  2385. &SharedCacheMap->SharedCacheMapLinks );
  2386. }
  2387. CcReleaseMasterLockFromDpcLevel();
  2388. //
  2389. // If the number of dirty pages for the Mbcb went to zero, we can reset
  2390. // our hint fields now.
  2391. //
  2392. if (BitmapRange->DirtyPages == 0) {
  2393. BitmapRange->FirstDirtyPage = MAXULONG;
  2394. BitmapRange->LastDirtyPage = 0;
  2395. //
  2396. // Assume this is a large file and that the resume point should
  2397. // be at the beginning of the next range. In all cases if the resume
  2398. // point is set too high, the next resume will just wrap back to 0 anyway.
  2399. //
  2400. Mbcb->ResumeWritePage = BitmapRange->BasePage + (MBCB_BITMAP_BLOCK_SIZE * 8);
  2401. //
  2402. // Otherwise we have to update the hint fields.
  2403. //
  2404. } else {
  2405. //
  2406. // Advance the first dirty page hint if we can.
  2407. //
  2408. if (BitmapRange->FirstDirtyPage == OriginalFirstDirtyPage) {
  2409. BitmapRange->FirstDirtyPage = (ULONG)(FirstDirtyPage - BitmapRange->BasePage) + *Length;
  2410. }
  2411. //
  2412. // Set to resume the next scan at the next bit for
  2413. // the Lazy Writer.
  2414. //
  2415. if (TargetLength == 0) {
  2416. Mbcb->ResumeWritePage = FirstDirtyPage + *Length;
  2417. }
  2418. }
  2419. //
  2420. // We can save a callback by letting our caller know when
  2421. // we have no more pages to write.
  2422. //
  2423. if (IsListEmpty(&SharedCacheMap->BcbList)) {
  2424. SharedCacheMap->PagesToWrite = Mbcb->PagesToWrite;
  2425. }
  2426. KeReleaseInStackQueuedSpinLock( &LockHandle );
  2427. //
  2428. // Now form all of our outputs. We calculated *Length as a page count,
  2429. // but our caller wants it in bytes.
  2430. //
  2431. *Length <<= PAGE_SHIFT;
  2432. FileOffset->QuadPart = (LONGLONG)FirstDirtyPage << PAGE_SHIFT;
  2433. *FirstBcb = NULL;
  2434. DebugTrace2(0, me, " <FileOffset = %08lx, %08lx\n", FileOffset->LowPart,
  2435. FileOffset->HighPart );
  2436. DebugTrace( 0, me, " <Length = %08lx\n", *Length );
  2437. DebugTrace(-1, me, "CcAcquireByteRangeForWrite -> TRUE\n", 0 );
  2438. return TRUE;
  2439. }
  2440. //
  2441. // We get here if there is no Mbcb or no dirty pages in it. Note that we
  2442. // wouldn't even be here if there were no dirty pages in this SharedCacheMap.
  2443. //
  2444. //
  2445. // Now point to last Bcb in List, and loop until we hit one of the
  2446. // breaks below or the beginning of the list.
  2447. //
  2448. Scan_Bcbs:
  2449. //
  2450. // Use while TRUE to handle case where the current target range wraps
  2451. // (escape is at the bottom).
  2452. //
  2453. while (TRUE) {
  2454. Bcb = CONTAINING_RECORD( SharedCacheMap->BcbList.Blink, BCB, BcbLinks );
  2455. //
  2456. // If we are to resume from a nonzero FileOffset, call CcFindBcb
  2457. // to get a quicker start. This is only useful on files that make
  2458. // use of significant pinned access, of course.
  2459. //
  2460. if (FlagOn(SharedCacheMap->Flags, MODIFIED_WRITE_DISABLED)) {
  2461. PLARGE_INTEGER StartingOffset;
  2462. if (ARGUMENT_PRESENT(TargetOffset)) {
  2463. StartingOffset = TargetOffset;
  2464. } else {
  2465. StartingOffset = (PLARGE_INTEGER)&SharedCacheMap->BeyondLastFlush;
  2466. }
  2467. if (StartingOffset->QuadPart != 0) {
  2468. LARGE_INTEGER StartingOffsetBias;
  2469. StartingOffsetBias.QuadPart = StartingOffset->QuadPart + PAGE_SIZE;
  2470. //
  2471. // Position ourselves. If we did not find a Bcb for the page, then
  2472. // a lower FileOffset was returned, so we want to move forward one.
  2473. //
  2474. if (!CcFindBcb( SharedCacheMap,
  2475. StartingOffset,
  2476. &StartingOffsetBias,
  2477. &Bcb )) {
  2478. Bcb = CONTAINING_RECORD( Bcb->BcbLinks.Blink, BCB, BcbLinks );
  2479. }
  2480. }
  2481. }
  2482. while (&Bcb->BcbLinks != &SharedCacheMap->BcbList) {
  2483. //
  2484. // Skip over this item if it is a listhead.
  2485. //
  2486. if (Bcb->NodeTypeCode != CACHE_NTC_BCB) {
  2487. Bcb = CONTAINING_RECORD( Bcb->BcbLinks.Blink, BCB, BcbLinks );
  2488. continue;
  2489. }
  2490. //
  2491. // If we are doing a specified range, then get out if we hit a
  2492. // higher Bcb.
  2493. //
  2494. if (ARGUMENT_PRESENT(TargetOffset) &&
  2495. ((TargetOffset->QuadPart + TargetLength) <= Bcb->FileOffset.QuadPart)) {
  2496. break;
  2497. }
  2498. //
  2499. // If we have not started a run, then see if this Bcb is a candidate
  2500. // to start one.
  2501. //
  2502. if (*Length == 0) {
  2503. //
  2504. // Else see if the Bcb is dirty, and is in our specified range, if
  2505. // there is one.
  2506. //
  2507. if (!Bcb->Dirty ||
  2508. (ARGUMENT_PRESENT(TargetOffset) && (TargetOffset->QuadPart >= Bcb->BeyondLastByte.QuadPart)) ||
  2509. (!ARGUMENT_PRESENT(TargetOffset) && (Bcb->FileOffset.QuadPart < SharedCacheMap->BeyondLastFlush))) {
  2510. Bcb = CONTAINING_RECORD( Bcb->BcbLinks.Blink, BCB, BcbLinks );
  2511. continue;
  2512. }
  2513. //
  2514. // If we have a candidate dirty page from the mask Bcb, see
  2515. // if it describes a prior range. We must decide to return
  2516. // the first dirty range.
  2517. //
  2518. if (BcbLookasideCheck && FirstDirtyPage <= (ULONG)(Bcb->FileOffset.QuadPart >> PAGE_SHIFT)) {
  2519. goto Accept_Page;
  2520. }
  2521. }
  2522. //
  2523. // Else, if we have started a run, then if this guy cannot be
  2524. // appended to the run, then break. Note that we ignore the
  2525. // Bcb's modification time stamp here to simplify the test.
  2526. //
  2527. // If the Bcb is currently pinned, then there is no sense in causing
  2528. // contention, so we will skip over this guy as well.
  2529. //
  2530. // Finally, if the new Bcb is in the next Vacb level, we will skip it
  2531. // to avoid problems with Bcb listheads going away in the middle of
  2532. // CcReleaseByteRangeFromWrite.
  2533. //
  2534. else {
  2535. if (!Bcb->Dirty || ( Bcb->FileOffset.QuadPart != ( FileOffset->QuadPart + (LONGLONG)*Length)) ||
  2536. (*Length + Bcb->ByteLength > MAX_WRITE_BEHIND) ||
  2537. (Bcb->PinCount != 0) ||
  2538. ((Bcb->FileOffset.QuadPart & (VACB_SIZE_OF_FIRST_LEVEL - 1)) == 0)) {
  2539. break;
  2540. }
  2541. }
  2542. //
  2543. // Increment PinCount to prevent Bcb from going away once the
  2544. // SpinLock is released, or we set it clean for the case where
  2545. // modified write is allowed.
  2546. //
  2547. Bcb->PinCount += 1;
  2548. //
  2549. // Release the SpinLock before waiting on the resource.
  2550. //
  2551. KeReleaseInStackQueuedSpinLock( &LockHandle );
  2552. if (FlagOn(SharedCacheMap->Flags, MODIFIED_WRITE_DISABLED) &&
  2553. !FlagOn(SharedCacheMap->Flags, DISABLE_WRITE_BEHIND)) {
  2554. //
  2555. // Now acquire the Bcb exclusive, so that we know that nobody
  2556. // has it pinned and thus no one can be modifying the described
  2557. // buffer. To acquire the first Bcb in a run, we can afford
  2558. // to wait, because we are not holding any resources. However
  2559. // if we already have a Bcb, then we better not wait, because
  2560. // someone could have this Bcb pinned, and then wait for the
  2561. // Bcb we already have exclusive.
  2562. //
  2563. // For streams for which we have not disabled modified page
  2564. // writing, we do not need to acquire this resource, and the
  2565. // foreground processing will not be acquiring the Bcb either.
  2566. //
  2567. if (!ExAcquireResourceExclusiveLite( &Bcb->Resource,
  2568. (BOOLEAN)(*Length == 0) )) {
  2569. DebugTrace( 0, me, "Could not acquire 2nd Bcb\n", 0 );
  2570. //
  2571. // Release the Bcb count we took out above. We say
  2572. // ReadOnly = TRUE since we do not own the resource,
  2573. // and SetClean = FALSE because we just want to decement
  2574. // the count.
  2575. //
  2576. CcUnpinFileData( Bcb, TRUE, UNPIN );
  2577. //
  2578. // When we leave the loop, we have to have the spin lock
  2579. //
  2580. KeAcquireInStackQueuedSpinLock( &SharedCacheMap->BcbSpinLock, &LockHandle );
  2581. break;
  2582. }
  2583. KeAcquireInStackQueuedSpinLock( &SharedCacheMap->BcbSpinLock, &LockHandle );
  2584. //
  2585. // If someone has the file open WriteThrough, then the Bcb may no
  2586. // longer be dirty. If so, call CcUnpinFileData to decrement the
  2587. // PinCount we incremented and free the resource.
  2588. //
  2589. if (!Bcb->Dirty) {
  2590. //
  2591. // Release the spinlock so that we can call CcUnpinFileData
  2592. //
  2593. KeReleaseInStackQueuedSpinLock( &LockHandle );
  2594. CcUnpinFileData( Bcb, FALSE, UNPIN );
  2595. KeAcquireInStackQueuedSpinLock( &SharedCacheMap->BcbSpinLock, &LockHandle );
  2596. //
  2597. // Now if we already have some data we can just break to return
  2598. // it, otherwise we have to restart the scan, since our Bcb
  2599. // may have gone away.
  2600. //
  2601. if (*Length != 0) {
  2602. break;
  2603. }
  2604. else {
  2605. Bcb = CONTAINING_RECORD( SharedCacheMap->BcbList.Blink, BCB, BcbLinks );
  2606. continue;
  2607. }
  2608. }
  2609. //
  2610. // If we are not in the disable modified write mode (normal user data)
  2611. // then we must set the buffer clean before doing the write, since we
  2612. // are unsynchronized with anyone producing dirty data. That way if we,
  2613. // for example, are writing data out while it is actively being changed,
  2614. // at least the changer will mark the buffer dirty afterwards and cause
  2615. // us to write it again later.
  2616. //
  2617. } else {
  2618. CcUnpinFileData( Bcb, TRUE, SET_CLEAN );
  2619. KeAcquireInStackQueuedSpinLock( &SharedCacheMap->BcbSpinLock, &LockHandle );
  2620. }
  2621. DebugTrace( 0, me, "Adding Bcb = %08lx to run\n", Bcb );
  2622. //
  2623. // No matter what, once we've reached this point we are returning
  2624. // a range from the Bcbs.
  2625. //
  2626. BcbLookasideCheck = FALSE;
  2627. //
  2628. // Update all of our return values. Note that FirstBcb refers to the
  2629. // FirstBcb in terms of how the Bcb list is ordered. Since the Bcb list
  2630. // is ordered by descending file offsets, FirstBcb will actually return
  2631. // the Bcb with the highest FileOffset.
  2632. //
  2633. if (*Length == 0) {
  2634. *FileOffset = Bcb->FileOffset;
  2635. }
  2636. *FirstBcb = Bcb;
  2637. *Length += Bcb->ByteLength;
  2638. //
  2639. // If there is a log file flush callback for this stream, then we must
  2640. // remember the largest Lsn we are about to flush.
  2641. //
  2642. if ((SharedCacheMap->FlushToLsnRoutine != NULL) &&
  2643. (Bcb->NewestLsn.QuadPart > LsnToFlushTo.QuadPart)) {
  2644. LsnToFlushTo = Bcb->NewestLsn;
  2645. }
  2646. Bcb = CONTAINING_RECORD( Bcb->BcbLinks.Blink, BCB, BcbLinks );
  2647. }
  2648. //
  2649. // If we have a candidate dirty page from the mask Bcb, accept it
  2650. // since no Bcb has been found.
  2651. //
  2652. if (BcbLookasideCheck) {
  2653. ASSERT( *Length == 0 );
  2654. goto Accept_Page;
  2655. }
  2656. //
  2657. // If we found something, update our last flush range and reduce
  2658. // PagesToWrite.
  2659. //
  2660. if (*Length != 0) {
  2661. //
  2662. // If this is the Lazy Writer, then update BeyondLastFlush and
  2663. // the PagesToWrite target.
  2664. //
  2665. if (!ARGUMENT_PRESENT(TargetOffset)) {
  2666. SharedCacheMap->BeyondLastFlush = FileOffset->QuadPart + *Length;
  2667. if (SharedCacheMap->PagesToWrite > (*Length >> PAGE_SHIFT)) {
  2668. SharedCacheMap->PagesToWrite -= (*Length >> PAGE_SHIFT);
  2669. } else {
  2670. SharedCacheMap->PagesToWrite = 0;
  2671. }
  2672. }
  2673. break;
  2674. //
  2675. // Else, if we scanned the entire file, get out - nothing to write now.
  2676. //
  2677. } else if ((SharedCacheMap->BeyondLastFlush == 0) || ARGUMENT_PRESENT(TargetOffset)) {
  2678. break;
  2679. }
  2680. //
  2681. // Otherwise, we may have not found anything because there is nothing
  2682. // beyond the last flush. In that case it is time to wrap back to 0
  2683. // and keep scanning.
  2684. //
  2685. SharedCacheMap->BeyondLastFlush = 0;
  2686. }
  2687. //
  2688. // Now release the spinlock file while we go off and do the I/O
  2689. //
  2690. KeReleaseInStackQueuedSpinLock( &LockHandle );
  2691. //
  2692. // If we need to flush to some Lsn, this is the time to do it now
  2693. // that we have found the largest Lsn and freed the spin lock.
  2694. //
  2695. if (LsnToFlushTo.QuadPart != 0) {
  2696. try {
  2697. (*SharedCacheMap->FlushToLsnRoutine) ( SharedCacheMap->LogHandle,
  2698. LsnToFlushTo );
  2699. } except( CcExceptionFilter( GetExceptionCode() )) {
  2700. //
  2701. // If there was an error, it will be raised. We cannot
  2702. // write anything until we successfully flush the log
  2703. // file, so we will release everything here and just
  2704. // return with 0 bytes.
  2705. //
  2706. LARGE_INTEGER LastOffset;
  2707. PBCB NextBcb;
  2708. //
  2709. // Now loop to free up all of the Bcbs. Set the time
  2710. // stamps to 0, so that we are guaranteed to try to
  2711. // flush them again on the next sweep.
  2712. //
  2713. do {
  2714. NextBcb = CONTAINING_RECORD( (*FirstBcb)->BcbLinks.Flink, BCB, BcbLinks );
  2715. //
  2716. // Skip over any listheads.
  2717. //
  2718. if ((*FirstBcb)->NodeTypeCode == CACHE_NTC_BCB) {
  2719. LastOffset = (*FirstBcb)->FileOffset;
  2720. CcUnpinFileData( *FirstBcb,
  2721. BooleanFlagOn(SharedCacheMap->Flags, DISABLE_WRITE_BEHIND),
  2722. UNPIN );
  2723. }
  2724. *FirstBcb = NextBcb;
  2725. } while (FileOffset->QuadPart != LastOffset.QuadPart);
  2726. //
  2727. // Show we did not acquire anything.
  2728. //
  2729. *Length = 0;
  2730. }
  2731. }
  2732. //
  2733. // If we got anything, return TRUE.
  2734. //
  2735. DebugTrace2(0, me, " <FileOffset = %08lx, %08lx\n", FileOffset->LowPart,
  2736. FileOffset->HighPart );
  2737. DebugTrace( 0, me, " <Length = %08lx\n", *Length );
  2738. DebugTrace(-1, me, "CcAcquireByteRangeForWrite -> %02lx\n", *Length != 0 );
  2739. return ((BOOLEAN)(*Length != 0));
  2740. }
  2741. //
  2742. // Internal Support Routine
  2743. //
  2744. VOID
  2745. CcReleaseByteRangeFromWrite (
  2746. IN PSHARED_CACHE_MAP SharedCacheMap,
  2747. IN PLARGE_INTEGER FileOffset,
  2748. IN ULONG Length,
  2749. IN PBCB FirstBcb,
  2750. IN BOOLEAN VerifyRequired
  2751. )
  2752. /*++
  2753. Routine Description:
  2754. This routine is called by the Lazy Writer to free a range of bytes and
  2755. clear all dirty bits, for a byte range returned by CcAcquireByteRangeForWrite.
  2756. Arguments:
  2757. SharedCacheMap - As supplied to CcAcquireByteRangeForWrite
  2758. FileOffset - As returned from CcAcquireByteRangeForWrite
  2759. Length - As returned from CcAcquirebyteRangeForWrite
  2760. FirstBcb - As returned from CcAcquireByteRangeForWrite
  2761. VerifyRequired - supplied as TRUE if a verify required error was received.
  2762. In this case we must mark/leave the data dirty so that
  2763. we will try to write it again.
  2764. Return Value:
  2765. None
  2766. --*/
  2767. {
  2768. LARGE_INTEGER LastOffset;
  2769. PBCB NextBcb;
  2770. DebugTrace(+1, me, "CcReleaseByteRangeFromWrite:\n", 0);
  2771. DebugTrace2(0, me, " FileOffset = %08lx, %08lx\n", FileOffset->LowPart,
  2772. FileOffset->HighPart );
  2773. //
  2774. // If it is a mask Mbcb we are getting, then we only have to check
  2775. // for VerifyRequired.
  2776. //
  2777. if (FirstBcb == NULL) {
  2778. ASSERT(Length != 0);
  2779. if (VerifyRequired) {
  2780. CcSetDirtyInMask( SharedCacheMap, FileOffset, Length );
  2781. }
  2782. DebugTrace(-1, me, "CcReleaseByteRangeFromWrite -> VOID\n", 0);
  2783. return;
  2784. }
  2785. //
  2786. // PREfix correctly notes that if the caller gives us a listhead to start with,
  2787. // we will not have filled in LastOffset by the time we do our first loop test.
  2788. // For PREfix's benefit (and ours), assert we really are starting with a Bcb.
  2789. //
  2790. ASSERT( FirstBcb->NodeTypeCode == CACHE_NTC_BCB );
  2791. //
  2792. // Now loop to free up all of the Bcbs. If modified writing is disabled
  2793. // for each Bcb, then we are to set it clean here, since we are synchronized
  2794. // with callers who set the data dirty. Otherwise we only have the Bcb pinned
  2795. // so it will not go away, and we only unpin it here.
  2796. //
  2797. do {
  2798. NextBcb = CONTAINING_RECORD( FirstBcb->BcbLinks.Flink, BCB, BcbLinks );
  2799. //
  2800. // Skip over any listheads.
  2801. //
  2802. if (FirstBcb->NodeTypeCode == CACHE_NTC_BCB) {
  2803. LastOffset = FirstBcb->FileOffset;
  2804. //
  2805. // If this is file system metadata (we disabled modified writing),
  2806. // then this is the time to mark the buffer clean, so long as we
  2807. // did not get verify required.
  2808. //
  2809. if (FlagOn(SharedCacheMap->Flags, MODIFIED_WRITE_DISABLED)) {
  2810. CcUnpinFileData( FirstBcb,
  2811. BooleanFlagOn(SharedCacheMap->Flags, DISABLE_WRITE_BEHIND),
  2812. SET_CLEAN );
  2813. }
  2814. //
  2815. // If we got verify required, we have to mark the buffer dirty again
  2816. // so we will try again later. Note we have to make this call again
  2817. // to make sure the right thing happens with time stamps.
  2818. //
  2819. if (VerifyRequired) {
  2820. CcSetDirtyPinnedData( FirstBcb, NULL );
  2821. }
  2822. //
  2823. // Finally remove a pin count left over from CcAcquireByteRangeForWrite.
  2824. //
  2825. CcUnpinFileData( FirstBcb, TRUE, UNPIN );
  2826. }
  2827. FirstBcb = NextBcb;
  2828. } while (FileOffset->QuadPart != LastOffset.QuadPart);
  2829. DebugTrace(-1, me, "CcReleaseByteRangeFromWrite -> VOID\n", 0);
  2830. }
  2831. //
  2832. // Internal Support Routine
  2833. //
  2834. VOID
  2835. FASTCALL
  2836. CcWriteBehind (
  2837. IN PSHARED_CACHE_MAP SharedCacheMap,
  2838. IN PIO_STATUS_BLOCK IoStatus
  2839. )
  2840. /*++
  2841. Routine Description:
  2842. This routine may be called with Wait = FALSE to see if write behind
  2843. is required, or with Wait = TRUE to perform write behind as required.
  2844. The code is very similar to the the code that the Lazy Writer performs
  2845. for each SharedCacheMap. The main difference is in the call to
  2846. CcAcquireByteRangeForWrite. Write Behind does not care about time
  2847. stamps (passing ULONG to accept all time stamps), but it will never
  2848. dump the first (highest byte offset) buffer in the list if the last
  2849. byte of that buffer is not yet written. The Lazy Writer does exactly
  2850. the opposite, in the sense that it is totally time-driven, and will
  2851. even dump a partially modified buffer if it sits around long enough.
  2852. Arguments:
  2853. SharedCacheMap - Pointer to SharedCacheMap to be written
  2854. Return Value:
  2855. FALSE - if write behind is required, but the caller supplied
  2856. Wait = FALSE
  2857. TRUE - if write behind is complete or not required
  2858. --*/
  2859. {
  2860. KLOCK_QUEUE_HANDLE LockHandle;
  2861. ULONG ActivePage;
  2862. ULONG PageIsDirty;
  2863. PMBCB Mbcb;
  2864. NTSTATUS Status;
  2865. PVACB ActiveVacb = NULL;
  2866. DebugTrace(+1, me, "CcWriteBehind\n", 0 );
  2867. DebugTrace( 0, me, " SharedCacheMap = %08lx\n", SharedCacheMap );
  2868. //
  2869. // First we have to acquire the file for LazyWrite, to avoid
  2870. // deadlocking with writers to the file. We do this via the
  2871. // CallBack procedure specified to CcInitializeCacheMap.
  2872. //
  2873. if (!(*SharedCacheMap->Callbacks->AcquireForLazyWrite)
  2874. ( SharedCacheMap->LazyWriteContext, TRUE )) {
  2875. //
  2876. // The filesystem is hinting that it doesn't think that it can
  2877. // service the write without significant delay so we will defer
  2878. // and come back later. Simply drop the queued flag ... note that
  2879. // we do not modify CcPagesYetToWrite, in the hope that we can make
  2880. // up the difference in some other cache map on this pass.
  2881. //
  2882. CcAcquireMasterLock( &LockHandle.OldIrql );
  2883. ClearFlag(SharedCacheMap->Flags, WRITE_QUEUED);
  2884. CcReleaseMasterLock( LockHandle.OldIrql );
  2885. IoStatus->Status = STATUS_FILE_LOCK_CONFLICT;
  2886. return;
  2887. }
  2888. //
  2889. // See if there is a previous active page to clean up, but only
  2890. // do so now if it is the last dirty page or no users have the
  2891. // file open. We will free it below after dropping the spinlock.
  2892. //
  2893. KeAcquireInStackQueuedSpinLock( &SharedCacheMap->BcbSpinLock, &LockHandle );
  2894. CcAcquireMasterLockAtDpcLevel();
  2895. if ((SharedCacheMap->DirtyPages <= 1) || (SharedCacheMap->OpenCount == 0)) {
  2896. GetActiveVacbAtDpcLevel( SharedCacheMap, ActiveVacb, ActivePage, PageIsDirty );
  2897. }
  2898. //
  2899. // Increment open count so that our caller's views stay available
  2900. // for CcGetVacbMiss. We could be tying up all of the views, and
  2901. // still need to write file sizes.
  2902. //
  2903. CcIncrementOpenCount( SharedCacheMap, 'brWS' );
  2904. //
  2905. // If there is a mask bcb, then we need to establish a target for
  2906. // it to flush.
  2907. //
  2908. if ((Mbcb = SharedCacheMap->Mbcb) != 0) {
  2909. //
  2910. // Set a target of pages to write, assuming that any Active
  2911. // Vacb will increase the number.
  2912. //
  2913. Mbcb->PagesToWrite = Mbcb->DirtyPages + ((ActiveVacb != NULL) ? 1 : 0);
  2914. if (Mbcb->PagesToWrite > CcPagesYetToWrite) {
  2915. Mbcb->PagesToWrite = CcPagesYetToWrite;
  2916. }
  2917. }
  2918. CcReleaseMasterLockFromDpcLevel();
  2919. KeReleaseInStackQueuedSpinLock( &LockHandle );
  2920. //
  2921. // Now free the active Vacb, if we found one.
  2922. //
  2923. if (ActiveVacb != NULL) {
  2924. CcFreeActiveVacb( SharedCacheMap, ActiveVacb, ActivePage, PageIsDirty );
  2925. }
  2926. //
  2927. // Now perform the lazy writing for this file via a special call
  2928. // to CcFlushCache. He recognizes us by the &CcNoDelay input to
  2929. // FileOffset, which signifies a Lazy Write, but is subsequently
  2930. // ignored.
  2931. //
  2932. CcFlushCache( SharedCacheMap->FileObject->SectionObjectPointer,
  2933. &CcNoDelay,
  2934. 1,
  2935. IoStatus );
  2936. //
  2937. // No need for the Lazy Write resource now.
  2938. //
  2939. (*SharedCacheMap->Callbacks->ReleaseFromLazyWrite)
  2940. ( SharedCacheMap->LazyWriteContext );
  2941. //
  2942. // Check if we need to put up a popup.
  2943. //
  2944. if (!NT_SUCCESS(IoStatus->Status) && !RetryError(IoStatus->Status)) {
  2945. //
  2946. // We lost writebehind data. Bemoan our fate into the system event
  2947. // log and throw a popup with a meaningful name to the desktop.
  2948. //
  2949. POBJECT_NAME_INFORMATION FileNameInfo = NULL;
  2950. NTSTATUS Status;
  2951. //
  2952. // Increment the count of how many of these we've had. This counter
  2953. // is useful in attempting to discriminate some corruption cases under
  2954. // test.
  2955. //
  2956. CcLostDelayedWrites += 1;
  2957. Status = IoQueryFileDosDeviceName( SharedCacheMap->FileObject, &FileNameInfo );
  2958. if ( Status == STATUS_SUCCESS ) {
  2959. IoRaiseInformationalHardError( STATUS_LOST_WRITEBEHIND_DATA, &FileNameInfo->Name, NULL );
  2960. } else {
  2961. if ( SharedCacheMap->FileObject->FileName.Length &&
  2962. SharedCacheMap->FileObject->FileName.MaximumLength &&
  2963. SharedCacheMap->FileObject->FileName.Buffer ) {
  2964. IoRaiseInformationalHardError( STATUS_LOST_WRITEBEHIND_DATA, &SharedCacheMap->FileObject->FileName, NULL );
  2965. }
  2966. }
  2967. CcLogError( SharedCacheMap->FileObject,
  2968. ( Status == STATUS_SUCCESS ?
  2969. &FileNameInfo->Name :
  2970. &SharedCacheMap->FileObject->FileName ),
  2971. IO_LOST_DELAYED_WRITE,
  2972. IoStatus->Status,
  2973. IRP_MJ_WRITE );
  2974. if (FileNameInfo) {
  2975. ExFreePool(FileNameInfo);
  2976. }
  2977. //
  2978. // See if there is any deferred writes we can post.
  2979. //
  2980. } else if (!IsListEmpty(&CcDeferredWrites)) {
  2981. CcPostDeferredWrites();
  2982. }
  2983. //
  2984. // Now acquire BcbSpinLock again to check for ValidData updates.
  2985. //
  2986. KeAcquireInStackQueuedSpinLock( &SharedCacheMap->BcbSpinLock, &LockHandle );
  2987. //
  2988. // If the the current ValidDataGoal is greater (or equal) than ValidDataLength,
  2989. // then we must see if we have advanced beyond the current ValidDataLength.
  2990. //
  2991. // If we have NEVER written anything out from this shared cache map, then
  2992. // there is no need to check anything associtated with valid data length
  2993. // here. We will come by here again when, and if, anybody actually
  2994. // modifies the file and we lazy write some data.
  2995. //
  2996. Status = STATUS_SUCCESS;
  2997. if (FlagOn(SharedCacheMap->Flags, LAZY_WRITE_OCCURRED) &&
  2998. (SharedCacheMap->ValidDataGoal.QuadPart >= SharedCacheMap->ValidDataLength.QuadPart) &&
  2999. (SharedCacheMap->ValidDataLength.QuadPart != MAXLONGLONG) &&
  3000. (SharedCacheMap->FileSize.QuadPart != 0)) {
  3001. LARGE_INTEGER NewValidDataLength;
  3002. NewValidDataLength = CcGetFlushedValidData( SharedCacheMap->FileObject->SectionObjectPointer,
  3003. TRUE );
  3004. //
  3005. // If New ValidDataLength has been written, then we have to
  3006. // call the file system back to update it. We must temporarily
  3007. // drop our global list while we do this, which is safe to do since
  3008. // we have not cleared WRITE_QUEUED.
  3009. //
  3010. // Note we keep calling any time we wrote the last page of the file,
  3011. // to solve the "famous" AFS Server problem. The file system will
  3012. // truncate our valid data call to whatever is currently valid. But
  3013. // then if he writes a little more, we do not want to stop calling
  3014. // back.
  3015. //
  3016. if ( NewValidDataLength.QuadPart >= SharedCacheMap->ValidDataLength.QuadPart ) {
  3017. KeReleaseInStackQueuedSpinLock( &LockHandle );
  3018. //
  3019. // Call file system to set new valid data. We have no
  3020. // one to tell if this doesn't work.
  3021. //
  3022. Status = CcSetValidData( SharedCacheMap->FileObject,
  3023. &NewValidDataLength );
  3024. KeAcquireInStackQueuedSpinLock( &SharedCacheMap->BcbSpinLock, &LockHandle );
  3025. if (NT_SUCCESS(Status)) {
  3026. SharedCacheMap->ValidDataLength = NewValidDataLength;
  3027. #ifdef TOMM
  3028. } else if ((Status != STATUS_INSUFFICIENT_RESOURCES) && !RetryError(Status)) {
  3029. DbgPrint("Unexpected status from CcSetValidData: %08lx, FileObject: %08lx\n",
  3030. Status,
  3031. SharedCacheMap->FileObject);
  3032. DbgBreakPoint();
  3033. #endif TOMM
  3034. }
  3035. }
  3036. }
  3037. KeReleaseInStackQueuedSpinLock( &LockHandle );
  3038. //
  3039. // Show we are done.
  3040. //
  3041. CcAcquireMasterLock( &LockHandle.OldIrql );
  3042. CcDecrementOpenCount( SharedCacheMap, 'brWF' );
  3043. //
  3044. // Make an approximate guess about whether we will call CcDeleteSharedCacheMap or not
  3045. // to truncate the file.
  3046. //
  3047. // Also do not delete the SharedCacheMap if we got an error on the ValidDataLength
  3048. // callback. If we get a resource allocation failure or a retryable error (due to
  3049. // log file full?), we have no one to tell, so we must just loop back and try again.
  3050. // Of course all I/O errors are just too bad.
  3051. //
  3052. if ((SharedCacheMap->OpenCount == 0)
  3053. &&
  3054. (NT_SUCCESS(Status) || ((Status != STATUS_INSUFFICIENT_RESOURCES) && !RetryError(Status)))) {
  3055. CcReleaseMasterLock( LockHandle.OldIrql );
  3056. FsRtlAcquireFileExclusive( SharedCacheMap->FileObject );
  3057. CcAcquireMasterLock( &LockHandle.OldIrql );
  3058. //
  3059. // Now really see if we are to delete this SharedCacheMap. By having released
  3060. // first we avoid a deadlock with the file system when the FileObject is
  3061. // dereferenced. Note that CcDeleteSharedCacheMap requires that the
  3062. // CcMasterSpinLock already be acquired, and it releases it.
  3063. //
  3064. // Note that we must retest since we dropped and reacquired the master
  3065. // lock.
  3066. //
  3067. if ((SharedCacheMap->OpenCount == 0)
  3068. &&
  3069. ((SharedCacheMap->DirtyPages == 0) || ((SharedCacheMap->FileSize.QuadPart == 0) &&
  3070. !FlagOn(SharedCacheMap->Flags, PIN_ACCESS)))) {
  3071. //
  3072. // Make sure to drop the requeue flag in case the write hit the timeout at
  3073. // the same time it finished everything up.
  3074. //
  3075. CcDeleteSharedCacheMap( SharedCacheMap, LockHandle.OldIrql, TRUE );
  3076. IoStatus->Information = 0;
  3077. SharedCacheMap = NULL;
  3078. } else {
  3079. CcReleaseMasterLock( LockHandle.OldIrql );
  3080. FsRtlReleaseFile( SharedCacheMap->FileObject );
  3081. CcAcquireMasterLock( &LockHandle.OldIrql );
  3082. }
  3083. }
  3084. //
  3085. // In the normal case, we just clear the flag on the way out if
  3086. // we will not requeue the workitem.
  3087. //
  3088. if (SharedCacheMap != NULL) {
  3089. if (IoStatus->Information != CC_REQUEUE) {
  3090. ClearFlag(SharedCacheMap->Flags, WRITE_QUEUED);
  3091. }
  3092. CcReleaseMasterLock( LockHandle.OldIrql );
  3093. }
  3094. DebugTrace(-1, me, "CcWriteBehind->VOID\n", 0 );
  3095. return;
  3096. }
  3097. LARGE_INTEGER
  3098. CcGetFlushedValidData (
  3099. IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
  3100. IN BOOLEAN CcInternalCaller
  3101. )
  3102. /*++
  3103. Routine Description:
  3104. This routine may be called by a file system to find out how far the Cache Manager
  3105. has flushed in the stream. More accurately, this routine returns either the FileOffset
  3106. of the lowest dirty page currently in the file.
  3107. NOTE that even though the routine takes SectionObjectPointer, the caller must insure
  3108. that the stream is cached and stays cached for the duration of this routine, much like
  3109. for the copy routines, etc.
  3110. Arguments:
  3111. SectionObjectPointer - A pointer to the Section Object Pointers
  3112. structure in the nonpaged Fcb.
  3113. CcInternalCaller - must be TRUE if the caller is coming from Cc, FALSE otherwise.
  3114. TRUE imples the need for self-synchronization.
  3115. Return Value:
  3116. The derived number for flushed ValidData, or MAXLONGLONG in the quad part if
  3117. the Section is not cached. (Naturally the caller can guarantee that this case
  3118. does not occur, and internal callers do.)
  3119. --*/
  3120. {
  3121. PSHARED_CACHE_MAP SharedCacheMap;
  3122. KLOCK_QUEUE_HANDLE LockHandle;
  3123. LARGE_INTEGER NewValidDataLength;
  3124. //
  3125. // External callers may be unsynchronized with this shared cache map
  3126. // perhaps going away underneath this call. NTFS and his
  3127. // pair of streams for compression-on-the-wire is a good example of
  3128. // someone who may be synchronized in one stream but needs to peek at
  3129. // the other.
  3130. //
  3131. if (!CcInternalCaller) {
  3132. CcAcquireMasterLock( &LockHandle.OldIrql );
  3133. SharedCacheMap = SectionObjectPointer->SharedCacheMap;
  3134. if (SharedCacheMap == NULL) {
  3135. CcReleaseMasterLock( LockHandle.OldIrql );
  3136. NewValidDataLength.QuadPart = MAXLONGLONG;
  3137. return NewValidDataLength;
  3138. }
  3139. CcIncrementOpenCount( SharedCacheMap, 'dfGS' );
  3140. CcReleaseMasterLock( LockHandle.OldIrql );
  3141. KeAcquireInStackQueuedSpinLock( &SharedCacheMap->BcbSpinLock, &LockHandle );
  3142. } else {
  3143. SharedCacheMap = SectionObjectPointer->SharedCacheMap;
  3144. }
  3145. ASSERT( SharedCacheMap != NULL );
  3146. //
  3147. // If the file is entirely clean, then we wish to return
  3148. // the new ValidDataLength as equal to ValidDataGoal.
  3149. //
  3150. NewValidDataLength = SharedCacheMap->ValidDataGoal;
  3151. //
  3152. // If there may be dirty pages we will look at the last Bcb in the
  3153. // descending-order Bcb list, and see if it describes data beyond
  3154. // ValidDataGoal.
  3155. //
  3156. // It is important to note that since we use DirtyPages as a faux
  3157. // reference count over some short windows (+1, -1) the simple
  3158. // fact it is nonzero does *not* mean the file is dirty.
  3159. //
  3160. // (This test is logically too conservative. For example, the last Bcb
  3161. // may not even be dirty (in which case we should look at its
  3162. // predecessor), or we may have earlier written valid data to this
  3163. // byte range (which also means if we knew this we could look at
  3164. // the predessor). This simply means that the Lazy Writer may not
  3165. // successfully get ValidDataLength updated in a file being randomly
  3166. // accessed until the level of file access dies down, or at the latest
  3167. // until the file is closed. However, security will never be
  3168. // compromised.)
  3169. //
  3170. if (SharedCacheMap->DirtyPages) {
  3171. PBITMAP_RANGE BitmapRange;
  3172. PBCB LastBcb;
  3173. PMBCB Mbcb = SharedCacheMap->Mbcb;
  3174. if ((Mbcb != NULL) && (Mbcb->DirtyPages != 0)) {
  3175. BitmapRange = CcFindBitmapRangeToClean( Mbcb, 0 );
  3176. ASSERT(BitmapRange->FirstDirtyPage != MAXULONG);
  3177. NewValidDataLength.QuadPart = (BitmapRange->BasePage + BitmapRange->FirstDirtyPage)
  3178. << PAGE_SHIFT;
  3179. }
  3180. LastBcb = CONTAINING_RECORD( SharedCacheMap->BcbList.Flink,
  3181. BCB,
  3182. BcbLinks );
  3183. while (&LastBcb->BcbLinks != &SharedCacheMap->BcbList) {
  3184. if ((LastBcb->NodeTypeCode == CACHE_NTC_BCB) && LastBcb->Dirty) {
  3185. break;
  3186. }
  3187. LastBcb = CONTAINING_RECORD( LastBcb->BcbLinks.Flink,
  3188. BCB,
  3189. BcbLinks );
  3190. }
  3191. //
  3192. // Check the Base of the last entry.
  3193. //
  3194. if ((&LastBcb->BcbLinks != &SharedCacheMap->BcbList) &&
  3195. (LastBcb->FileOffset.QuadPart < NewValidDataLength.QuadPart )) {
  3196. NewValidDataLength = LastBcb->FileOffset;
  3197. }
  3198. }
  3199. if (!CcInternalCaller) {
  3200. //
  3201. // Remove our reference.
  3202. //
  3203. CcAcquireMasterLockAtDpcLevel();
  3204. CcDecrementOpenCount( SharedCacheMap, 'dfGF' );
  3205. if ((SharedCacheMap->OpenCount == 0) &&
  3206. !FlagOn(SharedCacheMap->Flags, WRITE_QUEUED) &&
  3207. (SharedCacheMap->DirtyPages == 0)) {
  3208. //
  3209. // Move to the dirty list.
  3210. //
  3211. RemoveEntryList( &SharedCacheMap->SharedCacheMapLinks );
  3212. InsertTailList( &CcDirtySharedCacheMapList.SharedCacheMapLinks,
  3213. &SharedCacheMap->SharedCacheMapLinks );
  3214. //
  3215. // Make sure the Lazy Writer will wake up, because we
  3216. // want him to delete this SharedCacheMap.
  3217. //
  3218. LazyWriter.OtherWork = TRUE;
  3219. if (!LazyWriter.ScanActive) {
  3220. CcScheduleLazyWriteScan( FALSE );
  3221. }
  3222. }
  3223. KeReleaseInStackQueuedSpinLockFromDpcLevel( &LockHandle );
  3224. CcReleaseMasterLock( LockHandle.OldIrql );
  3225. }
  3226. return NewValidDataLength;
  3227. }
  3228. VOID
  3229. CcFlushCache (
  3230. IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
  3231. IN PLARGE_INTEGER FileOffset OPTIONAL,
  3232. IN ULONG Length,
  3233. OUT PIO_STATUS_BLOCK IoStatus OPTIONAL
  3234. )
  3235. /*++
  3236. Routine Description:
  3237. This routine may be called to flush dirty data from the cache to the
  3238. cached file on disk. Any byte range within the file may be flushed,
  3239. or the entire file may be flushed by omitting the FileOffset parameter.
  3240. This routine does not take a Wait parameter; the caller should assume
  3241. that it will always block.
  3242. Arguments:
  3243. SectionObjectPointer - A pointer to the Section Object Pointers
  3244. structure in the nonpaged Fcb.
  3245. FileOffset - If this parameter is supplied (not NULL), then only the
  3246. byte range specified by FileOffset and Length are flushed.
  3247. If &CcNoDelay is specified, then this signifies the call
  3248. from the Lazy Writer, and the lazy write scan should resume
  3249. as normal from the last spot where it left off in the file.
  3250. Length - Defines the length of the byte range to flush, starting at
  3251. FileOffset. This parameter is ignored if FileOffset is
  3252. specified as NULL.
  3253. IoStatus - The I/O status resulting from the flush operation.
  3254. Return Value:
  3255. None.
  3256. --*/
  3257. {
  3258. LARGE_INTEGER NextFileOffset, TargetOffset;
  3259. ULONG NextLength;
  3260. PBCB FirstBcb;
  3261. KIRQL OldIrql;
  3262. PSHARED_CACHE_MAP SharedCacheMap;
  3263. IO_STATUS_BLOCK TrashStatus;
  3264. PVOID TempVa;
  3265. ULONG RemainingLength, TempLength;
  3266. NTSTATUS PopupStatus;
  3267. LOGICAL HotSpot;
  3268. ULONG BytesWritten = 0;
  3269. LOGICAL PopupRequired = FALSE;
  3270. LOGICAL VerifyRequired = FALSE;
  3271. LOGICAL IsLazyWriter = FALSE;
  3272. LOGICAL FreeActiveVacb = FALSE;
  3273. PVACB ActiveVacb = NULL;
  3274. NTSTATUS Status = STATUS_SUCCESS;
  3275. LARGE_INTEGER EndTick, CurrentTick;
  3276. DebugTrace(+1, me, "CcFlushCache:\n", 0 );
  3277. DebugTrace( 0, mm, " SectionObjectPointer = %08lx\n", SectionObjectPointer );
  3278. DebugTrace2(0, me, " FileOffset = %08lx, %08lx\n",
  3279. ARGUMENT_PRESENT(FileOffset) ? FileOffset->LowPart
  3280. : 0,
  3281. ARGUMENT_PRESENT(FileOffset) ? FileOffset->HighPart
  3282. : 0 );
  3283. DebugTrace( 0, me, " Length = %08lx\n", Length );
  3284. //
  3285. // If IoStatus passed a Null pointer, set up to through status away.
  3286. //
  3287. if (!ARGUMENT_PRESENT(IoStatus)) {
  3288. IoStatus = &TrashStatus;
  3289. }
  3290. IoStatus->Status = STATUS_SUCCESS;
  3291. IoStatus->Information = 0;
  3292. //
  3293. // See if this is the Lazy Writer. Since he wants to use this common
  3294. // routine, which is also a public routine callable by file systems,
  3295. // the Lazy Writer shows his call by specifying CcNoDelay as the file offset!
  3296. //
  3297. // Also, in case we do not write anything because we see only HotSpot(s),
  3298. // initialize the Status to indicate a retryable error, so CcWorkerThread
  3299. // knows we did not make any progress. Of course any actual flush will
  3300. // overwrite this code.
  3301. //
  3302. if (FileOffset == &CcNoDelay) {
  3303. IoStatus->Status = STATUS_VERIFY_REQUIRED;
  3304. IsLazyWriter = TRUE;
  3305. FileOffset = NULL;
  3306. }
  3307. CcAcquireMasterLock( &OldIrql );
  3308. SharedCacheMap = SectionObjectPointer->SharedCacheMap;
  3309. //
  3310. // Awareness is indicated by the lowbit of the FileOffset pointer.
  3311. // Non-awareness of a private write stream results in a no-op.
  3312. //
  3313. if ((SharedCacheMap != NULL) && FlagOn( SharedCacheMap->Flags, PRIVATE_WRITE )) {
  3314. if (((ULONG_PTR)FileOffset & 1) == 0) {
  3315. CcReleaseMasterLock( OldIrql );
  3316. return;
  3317. }
  3318. FileOffset = (PLARGE_INTEGER)((ULONG_PTR)FileOffset ^ 1);
  3319. }
  3320. //
  3321. // If there is nothing to do, return here.
  3322. //
  3323. if (ARGUMENT_PRESENT(FileOffset) && (Length == 0)) {
  3324. CcReleaseMasterLock( OldIrql );
  3325. DebugTrace(-1, me, "CcFlushCache -> VOID\n", 0 );
  3326. return;
  3327. }
  3328. //
  3329. // See if the file is cached.
  3330. //
  3331. if (SharedCacheMap != NULL) {
  3332. //
  3333. // Increment the open count to keep it from going away.
  3334. //
  3335. CcIncrementOpenCount( SharedCacheMap, 'fcCS' );
  3336. if ((SharedCacheMap->NeedToZero != NULL) || (SharedCacheMap->ActiveVacb != NULL)) {
  3337. ULONG FirstPage = 0;
  3338. ULONG LastPage = MAXULONG;
  3339. if (ARGUMENT_PRESENT(FileOffset)) {
  3340. FirstPage = (ULONG)(FileOffset->QuadPart >> PAGE_SHIFT);
  3341. LastPage = (ULONG)((FileOffset->QuadPart + Length - 1) >> PAGE_SHIFT);
  3342. }
  3343. //
  3344. // Make sure we do not flush the active page without zeroing any
  3345. // uninitialized data. Also, it is very important to free the active
  3346. // page if it is the one to be flushed, so that we get the dirty
  3347. // bit out to the Pfn.
  3348. //
  3349. if (((((LONGLONG)LastPage + 1) << PAGE_SHIFT) > SharedCacheMap->ValidDataGoal.QuadPart) ||
  3350. ((SharedCacheMap->NeedToZero != NULL) &&
  3351. (FirstPage <= SharedCacheMap->NeedToZeroPage) &&
  3352. (LastPage >= SharedCacheMap->NeedToZeroPage)) ||
  3353. ((SharedCacheMap->ActiveVacb != NULL) &&
  3354. (FirstPage <= SharedCacheMap->ActivePage) &&
  3355. (LastPage >= SharedCacheMap->ActivePage))) {
  3356. GetActiveVacbAtDpcLevel( SharedCacheMap, ActiveVacb, RemainingLength, TempLength );
  3357. FreeActiveVacb = TRUE;
  3358. }
  3359. }
  3360. }
  3361. CcReleaseMasterLock( OldIrql );
  3362. if (FreeActiveVacb) {
  3363. CcFreeActiveVacb( SharedCacheMap, ActiveVacb, RemainingLength, TempLength );
  3364. }
  3365. //
  3366. // If there is a user-mapped file, then we perform the "service" of
  3367. // flushing even data not written via the file system. Note that this
  3368. // is pretty important for folks provoking the flush/purge of a coherency
  3369. // operation.
  3370. //
  3371. // It is critical this happen before we examine our own hints. In the course
  3372. // of this flush it is possible valid data length will be advanced by the
  3373. // underlying filesystem, with CcZero'ing behind - which will cause us to
  3374. // make some dirty zeroes in the cache. Syscache bug! Note how coherency
  3375. // flushing works ...
  3376. //
  3377. if ((SharedCacheMap == NULL)
  3378. ||
  3379. FlagOn(((PFSRTL_COMMON_FCB_HEADER)(SharedCacheMap->FileObject->FsContext))->Flags,
  3380. FSRTL_FLAG_USER_MAPPED_FILE) && !IsLazyWriter) {
  3381. //
  3382. // Call MM to flush the section through our view.
  3383. //
  3384. DebugTrace( 0, mm, "MmFlushSection:\n", 0 );
  3385. DebugTrace( 0, mm, " SectionObjectPointer = %08lx\n", SectionObjectPointer );
  3386. DebugTrace2(0, me, " FileOffset = %08lx, %08lx\n",
  3387. ARGUMENT_PRESENT(FileOffset) ? FileOffset->LowPart
  3388. : 0,
  3389. ARGUMENT_PRESENT(FileOffset) ? FileOffset->HighPart
  3390. : 0 );
  3391. DebugTrace( 0, mm, " RegionSize = %08lx\n", Length );
  3392. Status = MmFlushSection( SectionObjectPointer,
  3393. FileOffset,
  3394. Length,
  3395. IoStatus,
  3396. TRUE );
  3397. if ((!NT_SUCCESS(IoStatus->Status)) && !RetryError(IoStatus->Status)) {
  3398. PopupRequired = TRUE;
  3399. PopupStatus = IoStatus->Status;
  3400. }
  3401. DebugTrace2(0, mm, " <IoStatus = %08lx, %08lx\n",
  3402. IoStatus->Status, IoStatus->Information );
  3403. }
  3404. //
  3405. // Scan for dirty pages if there is a shared cache map.
  3406. //
  3407. if (SharedCacheMap != NULL) {
  3408. //
  3409. // If FileOffset was not specified then set to flush entire region
  3410. // and set valid data length to the goal so that we will not get
  3411. // any more call backs.
  3412. //
  3413. if (!IsLazyWriter && !ARGUMENT_PRESENT(FileOffset)) {
  3414. SharedCacheMap->ValidDataLength = SharedCacheMap->ValidDataGoal;
  3415. }
  3416. //
  3417. // If this is an explicit flush, initialize our offset to scan for.
  3418. //
  3419. if (ARGUMENT_PRESENT(FileOffset)) {
  3420. TargetOffset = *FileOffset;
  3421. }
  3422. //
  3423. // Assume we want to pass the explicit flush flag in Length.
  3424. // But overwrite it if a length really was specified. On
  3425. // subsequent loops, NextLength will have some nonzero value.
  3426. //
  3427. NextLength = 1;
  3428. if (Length != 0) {
  3429. NextLength = Length;
  3430. }
  3431. //
  3432. // Now calculate the tick that will signal the expiration of a
  3433. // lazy writer tick interval.
  3434. //
  3435. if (IsLazyWriter) {
  3436. KeQueryTickCount( &EndTick );
  3437. EndTick.QuadPart += CcIdleDelayTick;
  3438. }
  3439. //
  3440. // Loop as long as we find buffers to flush for this
  3441. // SharedCacheMap, and we are not trying to delete the guy.
  3442. //
  3443. while (((SharedCacheMap->PagesToWrite != 0) || !IsLazyWriter)
  3444. &&
  3445. ((SharedCacheMap->FileSize.QuadPart != 0) ||
  3446. FlagOn(SharedCacheMap->Flags, PIN_ACCESS))
  3447. &&
  3448. !VerifyRequired
  3449. &&
  3450. CcAcquireByteRangeForWrite ( SharedCacheMap,
  3451. IsLazyWriter ? NULL : (ARGUMENT_PRESENT(FileOffset) ?
  3452. &TargetOffset : NULL),
  3453. IsLazyWriter ? 0: NextLength,
  3454. &NextFileOffset,
  3455. &NextLength,
  3456. &FirstBcb )) {
  3457. //
  3458. // Assume this range is not a hot spot.
  3459. //
  3460. HotSpot = FALSE;
  3461. //
  3462. // We defer calling Mm to set address range modified until here, to take
  3463. // overhead out of the main line path, and to reduce the number of TBIS
  3464. // on a multiprocessor.
  3465. //
  3466. RemainingLength = NextLength;
  3467. do {
  3468. //
  3469. // See if the next file offset is mapped. (If not, the dirty bit
  3470. // was propagated on the unmap.)
  3471. //
  3472. if ((TempVa = CcGetVirtualAddressIfMapped( SharedCacheMap,
  3473. NextFileOffset.QuadPart + NextLength - RemainingLength,
  3474. &ActiveVacb,
  3475. &TempLength)) != NULL) {
  3476. //
  3477. // Reduce TempLength to RemainingLength if necessary, and
  3478. // call MM.
  3479. //
  3480. if (TempLength > RemainingLength) {
  3481. TempLength = RemainingLength;
  3482. }
  3483. //
  3484. // Clear the Dirty bit (if set) in the PTE and set the
  3485. // Pfn modified. Assume if the Pte was dirty, that this may
  3486. // be a hot spot. Do not do hot spots for metadata, and unless
  3487. // they are within ValidDataLength as reported to the file system
  3488. // via CcSetValidData.
  3489. //
  3490. HotSpot = (BOOLEAN)(((MmSetAddressRangeModified(TempVa, TempLength) || HotSpot) &&
  3491. ((NextFileOffset.QuadPart + NextLength) <
  3492. (SharedCacheMap->ValidDataLength.QuadPart)) &&
  3493. ((SharedCacheMap->LazyWritePassCount & 0xF) != 0) &&
  3494. IsLazyWriter) &&
  3495. !FlagOn(SharedCacheMap->Flags, MODIFIED_WRITE_DISABLED));
  3496. CcFreeVirtualAddress( ActiveVacb );
  3497. } else {
  3498. //
  3499. // Reduce TempLength to RemainingLength if necessary.
  3500. //
  3501. if (TempLength > RemainingLength) {
  3502. TempLength = RemainingLength;
  3503. }
  3504. }
  3505. //
  3506. // Reduce RemainingLength by what we processed.
  3507. //
  3508. RemainingLength -= TempLength;
  3509. //
  3510. // Loop until done.
  3511. //
  3512. } while (RemainingLength != 0);
  3513. CcLazyWriteHotSpots += HotSpot;
  3514. //
  3515. // Now flush, now flush if we do not think it is a hot spot.
  3516. //
  3517. if (!HotSpot) {
  3518. MmFlushSection( SharedCacheMap->FileObject->SectionObjectPointer,
  3519. &NextFileOffset,
  3520. NextLength,
  3521. IoStatus,
  3522. !IsLazyWriter );
  3523. if (NT_SUCCESS(IoStatus->Status)) {
  3524. if (!FlagOn(SharedCacheMap->Flags, LAZY_WRITE_OCCURRED)) {
  3525. CcAcquireMasterLock( &OldIrql );
  3526. SetFlag(SharedCacheMap->Flags, LAZY_WRITE_OCCURRED);
  3527. CcReleaseMasterLock( OldIrql );
  3528. }
  3529. //
  3530. // Increment performance counters
  3531. //
  3532. if (IsLazyWriter) {
  3533. CcLazyWriteIos += 1;
  3534. CcLazyWritePages += (NextLength + PAGE_SIZE - 1) >> PAGE_SHIFT;
  3535. }
  3536. } else {
  3537. LARGE_INTEGER Offset = NextFileOffset;
  3538. ULONG RetryLength = NextLength;
  3539. DebugTrace2( 0, 0, "I/O Error on Cache Flush: %08lx, %08lx\n",
  3540. IoStatus->Status, IoStatus->Information );
  3541. if (RetryError(IoStatus->Status)) {
  3542. VerifyRequired = TRUE;
  3543. //
  3544. // Loop to write each page individually, starting with one
  3545. // more try on the page that got the error, in case that page
  3546. // or any page beyond it can be successfully written
  3547. // individually. Note that Offset and RetryLength are
  3548. // guaranteed to be in integral pages, but the Information
  3549. // field from the failed request is not.
  3550. //
  3551. // We ignore errors now, and give it one last shot, before
  3552. // setting the pages clean (see below).
  3553. //
  3554. } else {
  3555. do {
  3556. DebugTrace2( 0, 0, "Trying page at offset %08lx, %08lx\n",
  3557. Offset.LowPart, Offset.HighPart );
  3558. MmFlushSection ( SharedCacheMap->FileObject->SectionObjectPointer,
  3559. &Offset,
  3560. PAGE_SIZE,
  3561. IoStatus,
  3562. !IsLazyWriter );
  3563. DebugTrace2( 0, 0, "I/O status = %08lx, %08lx\n",
  3564. IoStatus->Status, IoStatus->Information );
  3565. if (NT_SUCCESS(IoStatus->Status)) {
  3566. CcAcquireMasterLock( &OldIrql );
  3567. SetFlag(SharedCacheMap->Flags, LAZY_WRITE_OCCURRED);
  3568. CcReleaseMasterLock( OldIrql );
  3569. }
  3570. if ((!NT_SUCCESS(IoStatus->Status)) && !RetryError(IoStatus->Status)) {
  3571. PopupRequired = TRUE;
  3572. PopupStatus = IoStatus->Status;
  3573. }
  3574. VerifyRequired = VerifyRequired || RetryError(IoStatus->Status);
  3575. Offset.QuadPart = Offset.QuadPart + (LONGLONG)PAGE_SIZE;
  3576. RetryLength -= PAGE_SIZE;
  3577. } while(RetryLength > 0);
  3578. }
  3579. }
  3580. }
  3581. //
  3582. // Now release the Bcb resources and set them clean. Note we do not check
  3583. // here for errors, and just returned in the I/O status. Errors on writes
  3584. // are rare to begin with. Nonetheless, our strategy is to rely on
  3585. // one or more of the following (depending on the file system) to prevent
  3586. // errors from getting to us.
  3587. //
  3588. // - Retries and/or other forms of error recovery in the disk driver
  3589. // - Mirroring driver
  3590. // - Hot fixing in the noncached path of the file system
  3591. //
  3592. // In the unexpected case that a write error does get through, we
  3593. // *currently* just set the Bcbs clean anyway, rather than let
  3594. // Bcbs and pages accumulate which cannot be written. Note we did
  3595. // a popup above to at least notify the guy.
  3596. //
  3597. // Set the pages dirty again if we either saw a HotSpot or got
  3598. // verify required.
  3599. //
  3600. CcReleaseByteRangeFromWrite ( SharedCacheMap,
  3601. &NextFileOffset,
  3602. NextLength,
  3603. FirstBcb,
  3604. (BOOLEAN)(HotSpot || VerifyRequired) );
  3605. //
  3606. // See if there is any deferred writes we should post.
  3607. //
  3608. BytesWritten += NextLength;
  3609. if ((BytesWritten >= 0x40000) && !IsListEmpty(&CcDeferredWrites)) {
  3610. CcPostDeferredWrites();
  3611. BytesWritten = 0;
  3612. }
  3613. //
  3614. // If we're the lazy writer and have spent more than the active tick
  3615. // length in this loop, break out for a requeue so we share the
  3616. // file resources.
  3617. //
  3618. if (IsLazyWriter) {
  3619. KeQueryTickCount( &CurrentTick );
  3620. if (CurrentTick.QuadPart > EndTick.QuadPart) {
  3621. IoStatus->Information = CC_REQUEUE;
  3622. break;
  3623. }
  3624. }
  3625. //
  3626. // Now for explicit flushes, we should advance our range.
  3627. //
  3628. if (ARGUMENT_PRESENT(FileOffset)) {
  3629. NextFileOffset.QuadPart += NextLength;
  3630. //
  3631. // Done yet?
  3632. //
  3633. if ((FileOffset->QuadPart + Length) <= NextFileOffset.QuadPart) {
  3634. break;
  3635. }
  3636. //
  3637. // Calculate new target range
  3638. //
  3639. NextLength = (ULONG)((FileOffset->QuadPart + Length) - NextFileOffset.QuadPart);
  3640. TargetOffset = NextFileOffset;
  3641. }
  3642. }
  3643. }
  3644. //
  3645. // See if there are any deferred writes we should post if
  3646. // we escaped the loop without checking after a series of
  3647. // flushes.
  3648. //
  3649. if (BytesWritten != 0 && !IsListEmpty(&CcDeferredWrites)) {
  3650. CcPostDeferredWrites();
  3651. }
  3652. //
  3653. // Now we can get rid of the open count, and clean up as required.
  3654. //
  3655. if (SharedCacheMap != NULL) {
  3656. //
  3657. // Serialize again to decrement the open count.
  3658. //
  3659. CcAcquireMasterLock( &OldIrql );
  3660. CcDecrementOpenCount( SharedCacheMap, 'fcCF' );
  3661. if ((SharedCacheMap->OpenCount == 0) &&
  3662. !FlagOn(SharedCacheMap->Flags, WRITE_QUEUED) &&
  3663. (SharedCacheMap->DirtyPages == 0)) {
  3664. //
  3665. // Move to the dirty list.
  3666. //
  3667. RemoveEntryList( &SharedCacheMap->SharedCacheMapLinks );
  3668. InsertTailList( &CcDirtySharedCacheMapList.SharedCacheMapLinks,
  3669. &SharedCacheMap->SharedCacheMapLinks );
  3670. //
  3671. // Make sure the Lazy Writer will wake up, because we
  3672. // want him to delete this SharedCacheMap.
  3673. //
  3674. LazyWriter.OtherWork = TRUE;
  3675. if (!LazyWriter.ScanActive) {
  3676. CcScheduleLazyWriteScan( FALSE );
  3677. }
  3678. }
  3679. CcReleaseMasterLock( OldIrql );
  3680. }
  3681. //
  3682. // Make sure and return the first error to our caller. In the
  3683. // case of the Lazy Writer, a popup will be issued.
  3684. //
  3685. if (PopupRequired) {
  3686. IoStatus->Status = PopupStatus;
  3687. }
  3688. DebugTrace(-1, me, "CcFlushCache -> VOID\n", 0 );
  3689. return;
  3690. }
  3691. PVOID
  3692. CcRemapBcb (
  3693. IN PVOID Bcb
  3694. )
  3695. /*++
  3696. Routine Description:
  3697. This routine may be called by a file system to map a Bcb an additional
  3698. time in order to preserve it through several calls that perform additional
  3699. maps and unpins.
  3700. Arguments:
  3701. Bcb - Supplies a pointer to a previously returned Bcb.
  3702. Return Value:
  3703. Bcb with read-only indicator.
  3704. --*/
  3705. {
  3706. KIRQL OldIrql;
  3707. PVACB Vacb;
  3708. //
  3709. // Remove read-only bit
  3710. //
  3711. Bcb = (PVOID) ((ULONG_PTR)Bcb & ~1);
  3712. if (((PBCB)Bcb)->NodeTypeCode == CACHE_NTC_OBCB) {
  3713. //
  3714. // If this is an overlapped BCB, use the first Vacb in the
  3715. // array
  3716. //
  3717. Vacb = ((POBCB)Bcb)->Bcbs[0]->Vacb;
  3718. } else if (((PBCB)Bcb)->NodeTypeCode == CACHE_NTC_BCB) {
  3719. //
  3720. // If this is a BCB, extract the Vcb from it
  3721. //
  3722. Vacb = ((PBCB)Bcb)->Vacb;
  3723. } else {
  3724. //
  3725. // Otherwise, there is no signature to match. Assume
  3726. // it is a Vacb.
  3727. //
  3728. Vacb = (PVACB) Bcb;
  3729. }
  3730. ASSERT((Vacb >= CcVacbs) && (Vacb < CcBeyondVacbs));
  3731. //
  3732. // Safely bump the active count
  3733. //
  3734. CcAcquireVacbLock( &OldIrql );
  3735. Vacb->Overlay.ActiveCount += 1;
  3736. CcReleaseVacbLock( OldIrql );
  3737. return (PVOID) ((ULONG_PTR)Vacb | 1);
  3738. }
  3739. VOID
  3740. CcRepinBcb (
  3741. IN PVOID Bcb
  3742. )
  3743. /*++
  3744. Routine Description:
  3745. This routine may be called by a file system to pin a Bcb an additional
  3746. time in order to reserve it for Write Through or error recovery.
  3747. Typically the file system would do this the first time that it sets a
  3748. pinned buffer dirty while processing a WriteThrough request, or any
  3749. time that it determines that a buffer will be required for WriteThrough.
  3750. The call to this routine must be followed by a call to CcUnpinRepinnedBcb.
  3751. CcUnpinRepinnedBcb should normally be called during request completion
  3752. after all other resources have been released. CcUnpinRepinnedBcb
  3753. synchronously writes the buffer (for WriteThrough requests) and performs
  3754. the matching unpin for this call.
  3755. Arguments:
  3756. Bcb - Supplies a pointer to a previously pinned Bcb
  3757. Return Value:
  3758. None.
  3759. --*/
  3760. {
  3761. KLOCK_QUEUE_HANDLE LockHandle;
  3762. KeAcquireInStackQueuedSpinLock( &((PBCB)Bcb)->SharedCacheMap->BcbSpinLock, &LockHandle );
  3763. ((PBCB)Bcb)->PinCount += 1;
  3764. KeReleaseInStackQueuedSpinLock( &LockHandle );
  3765. }
  3766. VOID
  3767. CcUnpinRepinnedBcb (
  3768. IN PVOID Bcb,
  3769. IN BOOLEAN WriteThrough,
  3770. OUT PIO_STATUS_BLOCK IoStatus
  3771. )
  3772. /*++
  3773. Routine Description:
  3774. This routine may be called to Write a previously pinned buffer
  3775. through to the file. It must have been preceded by a call to
  3776. CcRepinBcb. As this routine must acquire the Bcb
  3777. resource exclusive, the caller must be extremely careful to avoid
  3778. deadlocks. Ideally the caller owns no resources at all when it
  3779. calls this routine, or else the caller should guarantee that it
  3780. has nothing else pinned in this same file. (The latter rule is
  3781. the one used to avoid deadlocks in calls from CcCopyWrite and
  3782. CcMdlWrite.)
  3783. Arguments:
  3784. Bcb - Pointer to a Bcb which was previously specified in a call
  3785. to CcRepinBcb.
  3786. WriteThrough - TRUE if the Bcb should be written through.
  3787. IoStatus - Returns the I/O status for the operation.
  3788. Return Value:
  3789. None.
  3790. --*/
  3791. {
  3792. PSHARED_CACHE_MAP SharedCacheMap = ((PBCB)Bcb)->SharedCacheMap;
  3793. DebugTrace(+1, me, "CcUnpinRepinnedBcb\n", 0 );
  3794. DebugTrace( 0, me, " Bcb = %08lx\n", Bcb );
  3795. DebugTrace( 0, me, " WriteThrough = %02lx\n", WriteThrough );
  3796. //
  3797. // Set status to success for non write through case.
  3798. //
  3799. IoStatus->Status = STATUS_SUCCESS;
  3800. if (WriteThrough) {
  3801. //
  3802. // Acquire Bcb exclusive to eliminate possible modifiers of the buffer,
  3803. // since we are about to write its buffer.
  3804. //
  3805. if (FlagOn(SharedCacheMap->Flags, MODIFIED_WRITE_DISABLED)) {
  3806. ExAcquireResourceExclusiveLite( &((PBCB)Bcb)->Resource, TRUE );
  3807. }
  3808. //
  3809. // Now, there is a chance that the LazyWriter has already written
  3810. // it, since the resource was free. We will only write it if it
  3811. // is still dirty.
  3812. //
  3813. if (((PBCB)Bcb)->Dirty) {
  3814. //
  3815. // First we make sure that the dirty bit in the PFN database is set.
  3816. //
  3817. ASSERT( ((PBCB)Bcb)->BaseAddress != NULL );
  3818. MmSetAddressRangeModified( ((PBCB)Bcb)->BaseAddress,
  3819. ((PBCB)Bcb)->ByteLength );
  3820. //
  3821. // Now release the Bcb resource and set it clean. Note we do not check
  3822. // here for errors, and just return the I/O status. Errors on writes
  3823. // are rare to begin with. Nonetheless, our strategy is to rely on
  3824. // one or more of the following (depending on the file system) to prevent
  3825. // errors from getting to us.
  3826. //
  3827. // - Retries and/or other forms of error recovery in the disk driver
  3828. // - Mirroring driver
  3829. // - Hot fixing in the noncached path of the file system
  3830. //
  3831. // In the unexpected case that a write error does get through, we
  3832. // report it to our caller, but go ahead and set the Bcb clean. There
  3833. // seems to be no point in letting Bcbs (and pages in physical memory)
  3834. // accumulate which can never go away because we get an unrecoverable I/O
  3835. // error.
  3836. //
  3837. //
  3838. // We specify TRUE here for ReadOnly so that we will keep the
  3839. // resource during the flush.
  3840. //
  3841. CcUnpinFileData( (PBCB)Bcb, TRUE, SET_CLEAN );
  3842. //
  3843. // Write it out.
  3844. //
  3845. MmFlushSection( ((PBCB)Bcb)->SharedCacheMap->FileObject->SectionObjectPointer,
  3846. &((PBCB)Bcb)->FileOffset,
  3847. ((PBCB)Bcb)->ByteLength,
  3848. IoStatus,
  3849. TRUE );
  3850. //
  3851. // If we got verify required, we have to mark the buffer dirty again
  3852. // so we will try again later.
  3853. //
  3854. if (RetryError(IoStatus->Status)) {
  3855. CcSetDirtyPinnedData( (PBCB)Bcb, NULL );
  3856. }
  3857. //
  3858. // Now remove the final pin count now that we have set it clean.
  3859. //
  3860. CcUnpinFileData( (PBCB)Bcb, FALSE, UNPIN );
  3861. //
  3862. // See if there is any deferred writes we can post.
  3863. //
  3864. if (!IsListEmpty(&CcDeferredWrites)) {
  3865. CcPostDeferredWrites();
  3866. }
  3867. }
  3868. else {
  3869. //
  3870. // Lazy Writer got there first, just free the resource and unpin.
  3871. //
  3872. CcUnpinFileData( (PBCB)Bcb, FALSE, UNPIN );
  3873. }
  3874. DebugTrace2(0, me, " <IoStatus = %08lx, %08lx\n", IoStatus->Status,
  3875. IoStatus->Information );
  3876. }
  3877. //
  3878. // Non-WriteThrough case
  3879. //
  3880. else {
  3881. CcUnpinFileData( (PBCB)Bcb, TRUE, UNPIN );
  3882. //
  3883. // Set status to success for non write through case.
  3884. //
  3885. IoStatus->Status = STATUS_SUCCESS;
  3886. }
  3887. DebugTrace(-1, me, "CcUnpinRepinnedBcb -> VOID\n", 0 );
  3888. }
  3889. //
  3890. // Internal Support Routine
  3891. //
  3892. BOOLEAN
  3893. CcFindBcb (
  3894. IN PSHARED_CACHE_MAP SharedCacheMap,
  3895. IN PLARGE_INTEGER FileOffset,
  3896. IN OUT PLARGE_INTEGER BeyondLastByte,
  3897. OUT PBCB *Bcb
  3898. )
  3899. /*++
  3900. Routine Description:
  3901. This routine is called to find a Bcb describing the specified byte range
  3902. of a file. It returns TRUE if it could at least find a Bcb which describes
  3903. the beginning of the specified byte range, or else FALSE if the first
  3904. part of the byte range is not present. In the latter case, the requested
  3905. byte range (TrialLength) is truncated if there is currently a Bcb which
  3906. describes bytes beyond the beginning of the byte range.
  3907. The caller may see if the entire byte range is being returned by examining
  3908. the Bcb, and the caller (or caller's caller) may then make subsequent
  3909. calls if the data is not all returned.
  3910. The BcbSpinLock must be currently acquired.
  3911. Arguments:
  3912. SharedCacheMap - Supplies a pointer to the SharedCacheMap for the file
  3913. in which the byte range is desired.
  3914. FileOffset - Supplies the file offset for the beginning of the desired
  3915. byte range.
  3916. BeyondLastByte - Supplies the file offset of the ending of the desired
  3917. byte range + 1. Note that this offset will be truncated
  3918. on return if the Bcb was not found, but bytes beyond the
  3919. beginning of the Bcb are contained in another Bcb.
  3920. Bcb - returns a Bcb describing the beginning of the byte range if also
  3921. returning TRUE, or else the point in the Bcb list to insert after.
  3922. Return Value:
  3923. FALSE - if no Bcb describes the beginning of the desired byte range
  3924. TRUE - if a Bcb is being returned describing at least an initial
  3925. part of the byte range.
  3926. --*/
  3927. {
  3928. PLIST_ENTRY BcbList;
  3929. PBCB Bcbt;
  3930. BOOLEAN Found = FALSE;
  3931. DebugTrace(+1, me, "CcFindBcb:\n", 0 );
  3932. DebugTrace( 0, me, " SharedCacheMap = %08lx\n", SharedCacheMap );
  3933. DebugTrace2(0, me, " FileOffset = %08lx, %08lx\n", FileOffset->LowPart,
  3934. FileOffset->HighPart );
  3935. DebugTrace2(0, me, " TrialLength = %08lx, %08lx\n", TrialLength->LowPart,
  3936. TrialLength->HighPart );
  3937. //
  3938. // We want to terminate scans by testing the NodeTypeCode field from the
  3939. // BcbLinks, so we want to see the SharedCacheMap signature from the same
  3940. // offset.
  3941. //
  3942. ASSERT(FIELD_OFFSET(SHARED_CACHE_MAP, BcbList) == FIELD_OFFSET(BCB, BcbLinks));
  3943. //
  3944. // Similarly, when we hit one of the BcbListHeads in the array, small negative
  3945. // offsets are all structure pointers, so we are counting on the Bcb signature
  3946. // to have some non-Ulong address bits set.
  3947. //
  3948. ASSERT((CACHE_NTC_BCB & 3) != 0);
  3949. //
  3950. // Get address of Bcb listhead that is *after* the Bcb we are looking for,
  3951. // for backwards scan. It is important that we fail in the forward
  3952. // direction so that we are looking in the right segment of the Bcb list.
  3953. //
  3954. BcbList = GetBcbListHead( SharedCacheMap, FileOffset->QuadPart + SIZE_PER_BCB_LIST, TRUE );
  3955. //
  3956. // Search for an entry that overlaps the specified range, or until we hit
  3957. // a listhead.
  3958. //
  3959. Bcbt = CONTAINING_RECORD(BcbList->Flink, BCB, BcbLinks);
  3960. //
  3961. // First see if we really have to do Large arithmetic or not, and
  3962. // then use either a 32-bit loop or a 64-bit loop to search for
  3963. // the Bcb.
  3964. //
  3965. if (FileOffset->HighPart == 0 &&
  3966. Bcbt->NodeTypeCode == CACHE_NTC_BCB &&
  3967. Bcbt->BeyondLastByte.HighPart == 0) {
  3968. //
  3969. // 32-bit - loop until we get back to a listhead.
  3970. //
  3971. while (Bcbt->NodeTypeCode == CACHE_NTC_BCB) {
  3972. //
  3973. // Since the Bcb list is in descending order, we first check
  3974. // if we are completely beyond the current entry, and if so
  3975. // get out.
  3976. //
  3977. if (FileOffset->LowPart >= Bcbt->BeyondLastByte.LowPart) {
  3978. break;
  3979. }
  3980. //
  3981. // Next check if the first byte we are looking for is
  3982. // contained in the current Bcb. If so, we either have
  3983. // a partial hit and must truncate to the exact amount
  3984. // we have found, or we may have a complete hit. In
  3985. // either case we break with Found == TRUE.
  3986. //
  3987. if (FileOffset->LowPart >= Bcbt->FileOffset.LowPart) {
  3988. Found = TRUE;
  3989. break;
  3990. }
  3991. //
  3992. // Now we know we must loop back and keep looking, but we
  3993. // still must check for the case where the tail end of the
  3994. // bytes we are looking for are described by the current
  3995. // Bcb. If so we must truncate what we are looking for,
  3996. // because this routine is only supposed to return bytes
  3997. // from the start of the desired range.
  3998. //
  3999. if (BeyondLastByte->LowPart >= Bcbt->FileOffset.LowPart) {
  4000. BeyondLastByte->LowPart = Bcbt->FileOffset.LowPart;
  4001. }
  4002. //
  4003. // Advance to next entry in list (which is possibly back to
  4004. // the listhead) and loop back.
  4005. //
  4006. Bcbt = CONTAINING_RECORD( Bcbt->BcbLinks.Flink,
  4007. BCB,
  4008. BcbLinks );
  4009. }
  4010. } else {
  4011. //
  4012. // 64-bit - Loop until we get back to a listhead.
  4013. //
  4014. while (Bcbt->NodeTypeCode == CACHE_NTC_BCB) {
  4015. //
  4016. // Since the Bcb list is in descending order, we first check
  4017. // if we are completely beyond the current entry, and if so
  4018. // get out.
  4019. //
  4020. if (FileOffset->QuadPart >= Bcbt->BeyondLastByte.QuadPart) {
  4021. break;
  4022. }
  4023. //
  4024. // Next check if the first byte we are looking for is
  4025. // contained in the current Bcb. If so, we either have
  4026. // a partial hit and must truncate to the exact amount
  4027. // we have found, or we may have a complete hit. In
  4028. // either case we break with Found == TRUE.
  4029. //
  4030. if (FileOffset->QuadPart >= Bcbt->FileOffset.QuadPart) {
  4031. Found = TRUE;
  4032. break;
  4033. }
  4034. //
  4035. // Now we know we must loop back and keep looking, but we
  4036. // still must check for the case where the tail end of the
  4037. // bytes we are looking for are described by the current
  4038. // Bcb. If so we must truncate what we are looking for,
  4039. // because this routine is only supposed to return bytes
  4040. // from the start of the desired range.
  4041. //
  4042. if (BeyondLastByte->QuadPart >= Bcbt->FileOffset.QuadPart) {
  4043. BeyondLastByte->QuadPart = Bcbt->FileOffset.QuadPart;
  4044. }
  4045. //
  4046. // Advance to next entry in list (which is possibly back to
  4047. // the listhead) and loop back.
  4048. //
  4049. Bcbt = CONTAINING_RECORD( Bcbt->BcbLinks.Flink,
  4050. BCB,
  4051. BcbLinks );
  4052. }
  4053. }
  4054. *Bcb = Bcbt;
  4055. DebugTrace2(0, me, " <TrialLength = %08lx, %08lx\n", TrialLength->LowPart,
  4056. TrialLength->HighPart );
  4057. DebugTrace( 0, me, " <Bcb = %08lx\n", *Bcb );
  4058. DebugTrace(-1, me, "CcFindBcb -> %02lx\n", Found );
  4059. return Found;
  4060. }
  4061. //
  4062. // Internal Support Routine
  4063. //
  4064. PBCB
  4065. CcAllocateInitializeBcb (
  4066. IN OUT PSHARED_CACHE_MAP SharedCacheMap OPTIONAL,
  4067. IN OUT PBCB AfterBcb,
  4068. IN PLARGE_INTEGER FileOffset,
  4069. IN PLARGE_INTEGER TrialLength
  4070. )
  4071. /*++
  4072. Routine Description:
  4073. This routine allocates and initializes a Bcb to describe the specified
  4074. byte range, and inserts it into the Bcb List of the specified Shared
  4075. Cache Map. The Bcb List spin lock must currently be acquired.
  4076. BcbSpinLock must be acquired on entry.
  4077. Arguments:
  4078. SharedCacheMap - Supplies the SharedCacheMap for the new Bcb.
  4079. AfterBcb - Supplies where in the descending-order BcbList the new Bcb
  4080. should be inserted: either the ListHead (masquerading as
  4081. a Bcb) or a Bcb.
  4082. FileOffset - Supplies File Offset for the desired data.
  4083. TrialLength - Supplies length of desired data.
  4084. Return Value:
  4085. Address of the allocated and initialized Bcb
  4086. --*/
  4087. {
  4088. PBCB Bcb;
  4089. ULONG RoundedBcbSize = (sizeof(BCB) + 7) & ~7;
  4090. if ((Bcb = ExAllocatePoolWithTag( NonPagedPool, sizeof(BCB), 'cBcC')) == NULL) {
  4091. return NULL;
  4092. }
  4093. //
  4094. // Initialize the newly allocated Bcb. First zero it, then fill in
  4095. // nonzero fields.
  4096. //
  4097. RtlZeroMemory( Bcb, RoundedBcbSize );
  4098. //
  4099. // For Mbcb's, SharedCacheMap is NULL, and the rest of this initialization
  4100. // is not desired.
  4101. //
  4102. if (SharedCacheMap != NULL) {
  4103. Bcb->NodeTypeCode = CACHE_NTC_BCB;
  4104. Bcb->FileOffset = *FileOffset;
  4105. Bcb->ByteLength = TrialLength->LowPart;
  4106. Bcb->BeyondLastByte.QuadPart = FileOffset->QuadPart + TrialLength->QuadPart;
  4107. Bcb->PinCount += 1;
  4108. ExInitializeResourceLite( &Bcb->Resource );
  4109. Bcb->SharedCacheMap = SharedCacheMap;
  4110. //
  4111. // Since CcCalculateVacbLockCount has to be able to walk
  4112. // the BcbList with only the VacbSpinLock, we take that one
  4113. // out to change the list and set the count.
  4114. //
  4115. CcAcquireVacbLockAtDpcLevel();
  4116. InsertTailList( &AfterBcb->BcbLinks, &Bcb->BcbLinks );
  4117. ASSERT( (SharedCacheMap->SectionSize.QuadPart < VACB_SIZE_OF_FIRST_LEVEL) ||
  4118. (CcFindBcb(SharedCacheMap, FileOffset, &Bcb->BeyondLastByte, &AfterBcb) &&
  4119. (Bcb == AfterBcb)) );
  4120. //
  4121. // Now for large metadata streams we lock the Vacb level.
  4122. //
  4123. CcLockVacbLevel( SharedCacheMap, FileOffset->QuadPart );
  4124. CcReleaseVacbLockFromDpcLevel();
  4125. //
  4126. // If this resource was no write behind, let Ex know that the
  4127. // resource will never be acquired exclusive. Also disable
  4128. // boost (I know this is useless, but KenR said I had to do it).
  4129. //
  4130. if (SharedCacheMap &&
  4131. FlagOn(SharedCacheMap->Flags, DISABLE_WRITE_BEHIND)) {
  4132. #if DBG
  4133. SetFlag(Bcb->Resource.Flag, ResourceNeverExclusive);
  4134. #endif
  4135. ExDisableResourceBoost( &Bcb->Resource );
  4136. }
  4137. }
  4138. return Bcb;
  4139. }
  4140. //
  4141. // Internal support routine
  4142. //
  4143. VOID
  4144. FASTCALL
  4145. CcDeallocateBcb (
  4146. IN PBCB Bcb
  4147. )
  4148. /*++
  4149. Routine Description:
  4150. This routine deallocates a Bcb to the BcbZone. It must
  4151. already be removed from the BcbList.
  4152. Arguments:
  4153. Bcb - the Bcb to deallocate
  4154. Return Value:
  4155. None
  4156. --*/
  4157. {
  4158. //
  4159. // Deallocate Resource structures
  4160. //
  4161. if (Bcb->NodeTypeCode == CACHE_NTC_BCB) {
  4162. ExDeleteResourceLite( &Bcb->Resource );
  4163. }
  4164. ExFreePool(Bcb);
  4165. return;
  4166. }
  4167. //
  4168. // Internal Support Routine
  4169. //
  4170. BOOLEAN
  4171. CcMapAndRead(
  4172. IN PSHARED_CACHE_MAP SharedCacheMap,
  4173. IN PLARGE_INTEGER FileOffset,
  4174. IN ULONG Length,
  4175. IN ULONG ZeroFlags,
  4176. IN BOOLEAN Wait,
  4177. IN PVOID BaseAddress
  4178. )
  4179. /*++
  4180. Routine Description:
  4181. This routine may be called to insure that the specified data is mapped,
  4182. read into memory and locked. If TRUE is returned, then the
  4183. correct I/O status for the transfer is also returned, along with
  4184. a system-space address for the data.
  4185. Arguments:
  4186. SharedCacheMap - Supplies the address of the SharedCacheMap for the
  4187. data.
  4188. FileOffset - Supplies the file offset of the desired data.
  4189. Length - Supplies the total amount of data desired.
  4190. ZeroFlags - Defines which pages may be zeroed if not resident.
  4191. Wait - Supplies FALSE if the caller is not willing to block for the
  4192. data, or TRUE if the caller is willing to block.
  4193. BaseAddress - Supplies the system base address at which the data may
  4194. be accessed.
  4195. Return Value:
  4196. FALSE - if the caller supplied Wait = FALSE and the data could not
  4197. be returned without blocking.
  4198. TRUE - if the data is being returned.
  4199. Note: this routine may raise an exception due to a map or read failure,
  4200. however, this can only happen if Wait was specified as TRUE, since
  4201. mapping and reading will not be performed if the caller cannot wait.
  4202. --*/
  4203. {
  4204. ULONG ZeroCase;
  4205. ULONG SavedState;
  4206. BOOLEAN Result = FALSE;
  4207. PETHREAD Thread = PsGetCurrentThread();
  4208. UNREFERENCED_PARAMETER (SharedCacheMap);
  4209. UNREFERENCED_PARAMETER (FileOffset);
  4210. MmSavePageFaultReadAhead( Thread, &SavedState );
  4211. //
  4212. // try around everything for cleanup.
  4213. //
  4214. try {
  4215. ULONG PagesToGo;
  4216. //
  4217. // Now loop to touch all of the pages, calling MM to insure
  4218. // that if we fault, we take in exactly the number of pages
  4219. // we need.
  4220. //
  4221. PagesToGo = ADDRESS_AND_SIZE_TO_SPAN_PAGES( BaseAddress, Length );
  4222. //
  4223. // Loop to touch or zero the pages.
  4224. //
  4225. ZeroCase = ZERO_FIRST_PAGE;
  4226. while (PagesToGo) {
  4227. //
  4228. // If we cannot zero this page, or Mm failed to return
  4229. // a zeroed page, then just fault it in.
  4230. //
  4231. MmSetPageFaultReadAhead( Thread, (PagesToGo - 1) );
  4232. if (!FlagOn(ZeroFlags, ZeroCase) ||
  4233. !MmCheckCachedPageState(BaseAddress, TRUE)) {
  4234. //
  4235. // If we get here, it is almost certainly due to the fact
  4236. // that we can not take a zero page. MmCheckCachedPageState
  4237. // will so rarely return FALSE, that we will not worry
  4238. // about it. We will only check if the page is there if
  4239. // Wait is FALSE, so that we can do the right thing.
  4240. //
  4241. if (!MmCheckCachedPageState(BaseAddress, FALSE) && !Wait) {
  4242. try_return( Result = FALSE );
  4243. }
  4244. }
  4245. BaseAddress = (PCHAR)BaseAddress + PAGE_SIZE;
  4246. PagesToGo -= 1;
  4247. if (PagesToGo == 1) {
  4248. ZeroCase = ZERO_LAST_PAGE;
  4249. } else {
  4250. ZeroCase = ZERO_MIDDLE_PAGES;
  4251. }
  4252. }
  4253. try_return( Result = TRUE );
  4254. try_exit: NOTHING;
  4255. }
  4256. //
  4257. // Cleanup on the way out.
  4258. //
  4259. finally {
  4260. MmResetPageFaultReadAhead(Thread, SavedState);
  4261. }
  4262. return Result;
  4263. }
  4264. //
  4265. // Internal Support Routine
  4266. //
  4267. VOID
  4268. CcFreeActiveVacb (
  4269. IN PSHARED_CACHE_MAP SharedCacheMap,
  4270. IN PVACB ActiveVacb OPTIONAL,
  4271. IN ULONG ActivePage,
  4272. IN ULONG PageIsDirty
  4273. )
  4274. /*++
  4275. Routine Description:
  4276. This routine may be called to zero the end of a locked page or
  4277. free the ActiveVacb for a Shared Cache Map, if there is one.
  4278. Note that some callers are not synchronized with foreground
  4279. activity, and may therefore not have an ActiveVacb. Examples
  4280. of unsynchronized callers are CcZeroEndOfLastPage (which is
  4281. called by MM) and any flushing done by CcWriteBehind.
  4282. Arguments:
  4283. SharedCacheMap - SharedCacheMap to examine for page to be zeroed.
  4284. ActiveVacb - Vacb to free
  4285. ActivePage - Page that was used
  4286. PageIsDirty - ACTIVE_PAGE_IS_DIRTY if the active page is dirty
  4287. Return Value:
  4288. None
  4289. --*/
  4290. {
  4291. LARGE_INTEGER ActiveOffset;
  4292. PVOID ActiveAddress;
  4293. ULONG BytesLeftInPage;
  4294. KIRQL OldIrql;
  4295. //
  4296. // If the page was locked, then unlock it.
  4297. //
  4298. if (SharedCacheMap->NeedToZero != NULL) {
  4299. PVACB NeedToZeroVacb;
  4300. //
  4301. // Zero the rest of the page under spinlock control,
  4302. // and then clear the address field. This field makes
  4303. // zero->nonzero transitions only when the file is exclusive,
  4304. // but it can make nonzero->zero transitions any time the
  4305. // spinlock is not held.
  4306. //
  4307. ExAcquireFastLock( &SharedCacheMap->ActiveVacbSpinLock, &OldIrql );
  4308. //
  4309. // The address could already be gone.
  4310. //
  4311. ActiveAddress = SharedCacheMap->NeedToZero;
  4312. if (ActiveAddress != NULL) {
  4313. BytesLeftInPage = PAGE_SIZE - ((((ULONG)((ULONG_PTR)ActiveAddress) - 1) & (PAGE_SIZE - 1)) + 1);
  4314. RtlZeroBytes( ActiveAddress, BytesLeftInPage );
  4315. NeedToZeroVacb = SharedCacheMap->NeedToZeroVacb;
  4316. ASSERT( NeedToZeroVacb != NULL );
  4317. SharedCacheMap->NeedToZero = NULL;
  4318. }
  4319. ExReleaseFastLock( &SharedCacheMap->ActiveVacbSpinLock, OldIrql );
  4320. //
  4321. // Now call MM to unlock the address. Note we will never store the
  4322. // address at the start of the page, but we can sometimes store
  4323. // the start of the next page when we have exactly filled the page.
  4324. //
  4325. if (ActiveAddress != NULL) {
  4326. MmUnlockCachedPage( (PVOID)((PCHAR)ActiveAddress - 1) );
  4327. CcFreeVirtualAddress( NeedToZeroVacb );
  4328. }
  4329. }
  4330. //
  4331. // See if caller actually has an ActiveVacb
  4332. //
  4333. if (ActiveVacb != NULL) {
  4334. //
  4335. // See if the page is dirty
  4336. //
  4337. if (PageIsDirty) {
  4338. ActiveOffset.QuadPart = (LONGLONG)ActivePage << PAGE_SHIFT;
  4339. ActiveAddress = (PVOID)((PCHAR)ActiveVacb->BaseAddress +
  4340. (ActiveOffset.LowPart & (VACB_MAPPING_GRANULARITY - 1)));
  4341. //
  4342. // Tell the Lazy Writer to write the page.
  4343. //
  4344. CcSetDirtyInMask( SharedCacheMap, &ActiveOffset, PAGE_SIZE );
  4345. //
  4346. // Now we need to clear the flag and decrement some counts if there is
  4347. // no other active Vacb which snuck in.
  4348. //
  4349. CcAcquireMasterLock( &OldIrql );
  4350. ExAcquireSpinLockAtDpcLevel( &SharedCacheMap->ActiveVacbSpinLock );
  4351. if ((SharedCacheMap->ActiveVacb == NULL) &&
  4352. FlagOn(SharedCacheMap->Flags, ACTIVE_PAGE_IS_DIRTY)) {
  4353. ClearFlag(SharedCacheMap->Flags, ACTIVE_PAGE_IS_DIRTY);
  4354. CcDeductDirtyPages( SharedCacheMap, 1);
  4355. }
  4356. ExReleaseSpinLockFromDpcLevel( &SharedCacheMap->ActiveVacbSpinLock );
  4357. CcReleaseMasterLock( OldIrql );
  4358. }
  4359. //
  4360. // Now free the Vacb.
  4361. //
  4362. CcFreeVirtualAddress( ActiveVacb );
  4363. }
  4364. }
  4365. //
  4366. // Internal Support Routine
  4367. //
  4368. VOID
  4369. CcMapAndCopy(
  4370. IN PSHARED_CACHE_MAP SharedCacheMap,
  4371. IN PVOID UserBuffer,
  4372. IN PLARGE_INTEGER FileOffset,
  4373. IN ULONG Length,
  4374. IN ULONG ZeroFlags,
  4375. IN PFILE_OBJECT FileObject
  4376. )
  4377. /*++
  4378. Routine Description:
  4379. This routine may be called to copy the specified user data to the
  4380. cache via a special Mm routine which copies the data to uninitialized
  4381. pages and returns.
  4382. Arguments:
  4383. SharedCacheMap - Supplies the address of the SharedCacheMap for the
  4384. data.
  4385. UserBuffer - unsafe buffer supplying the user's data to be written
  4386. FileOffset - Supplies the file offset to be modified
  4387. Length - Supplies the total amount of data
  4388. ZeroFlags - Defines which pages may be zeroed if not resident.
  4389. WriteThrough - Supplies the file object being written to
  4390. Return Value:
  4391. None
  4392. --*/
  4393. {
  4394. ULONG ReceivedLength;
  4395. ULONG ZeroCase;
  4396. PVOID CacheBuffer;
  4397. PVOID SavedMappedBuffer;
  4398. ULONG SavedMappedLength;
  4399. ULONG ActivePage;
  4400. KIRQL OldIrql;
  4401. LARGE_INTEGER PFileOffset;
  4402. IO_STATUS_BLOCK IoStatus;
  4403. NTSTATUS Status;
  4404. ULONG SavedState;
  4405. LOGICAL MorePages;
  4406. BOOLEAN WriteThrough = BooleanFlagOn( FileObject->Flags, FO_WRITE_THROUGH );
  4407. ULONG SavedTotalLength = Length;
  4408. LARGE_INTEGER LocalOffset;
  4409. ULONG PageOffset = FileOffset->LowPart & (PAGE_SIZE - 1);
  4410. PVACB Vacb = NULL;
  4411. PETHREAD Thread = PsGetCurrentThread();
  4412. //
  4413. // Initialize SavePage to TRUE to skip the finally clause on zero-length
  4414. // writes.
  4415. //
  4416. BOOLEAN SavePage = TRUE;
  4417. //
  4418. // PREfix needs to see this explicitly, as opposed to a structure copy.
  4419. //
  4420. LocalOffset.QuadPart = FileOffset->QuadPart;
  4421. DebugTrace(+1, me, "CcMapAndCopy:\n", 0 );
  4422. DebugTrace( 0, me, " SharedCacheMap = %08lx\n", SharedCacheMap );
  4423. DebugTrace2(0, me, " FileOffset = %08lx, %08lx\n", FileOffset->LowPart,
  4424. FileOffset->HighPart );
  4425. DebugTrace( 0, me, " Length = %08lx\n", Length );
  4426. MmSavePageFaultReadAhead( Thread, &SavedState );
  4427. //
  4428. // BUGBUG: re-enable this path when we can also generate a ccsetvaliddata call
  4429. // in all cases to fix corruption issue see 615074)
  4430. //
  4431. #if 0
  4432. //
  4433. // See if we need to force write through. If the file object is of remote origin,
  4434. // it has been exempted from throttling. As a result, it is possible that too
  4435. // many pages will get dirty. In order to prevent this, we force write through
  4436. // on these file objects if we would have throttled them in the first place.
  4437. //
  4438. if (!WriteThrough && IoIsFileOriginRemote(FileObject)
  4439. &&
  4440. !CcCanIWrite( FileObject,
  4441. Length,
  4442. FALSE,
  4443. MAXUCHAR - 2 )) {
  4444. WriteThrough = TRUE;
  4445. }
  4446. #endif
  4447. //
  4448. // try around everything for cleanup.
  4449. //
  4450. try {
  4451. while (Length != 0) {
  4452. CacheBuffer = CcGetVirtualAddress( SharedCacheMap,
  4453. LocalOffset,
  4454. &Vacb,
  4455. &ReceivedLength );
  4456. //
  4457. // PREfix wants to know this cannot be NULL, otherwise it
  4458. // will complain.
  4459. //
  4460. ASSERT( CacheBuffer != NULL );
  4461. //
  4462. // If we got more than we need, make sure to only use
  4463. // the right amount.
  4464. //
  4465. if (ReceivedLength > Length) {
  4466. ReceivedLength = Length;
  4467. }
  4468. SavedMappedBuffer = CacheBuffer;
  4469. SavedMappedLength = ReceivedLength;
  4470. Length -= ReceivedLength;
  4471. //
  4472. // Now loop to touch all of the pages, calling MM to insure
  4473. // that if we fault, we take in exactly the number of pages
  4474. // we need.
  4475. //
  4476. CacheBuffer = (PVOID)((PCHAR)CacheBuffer - PageOffset);
  4477. ReceivedLength += PageOffset;
  4478. //
  4479. // Loop to touch or zero the pages.
  4480. //
  4481. ZeroCase = ZERO_FIRST_PAGE;
  4482. //
  4483. // Set up offset to page for use below.
  4484. //
  4485. PFileOffset = LocalOffset;
  4486. PFileOffset.LowPart -= PageOffset;
  4487. while (TRUE) {
  4488. //
  4489. // Calculate whether we wish to save an active page
  4490. // or not.
  4491. //
  4492. SavePage = (BOOLEAN) ((Length == 0) &&
  4493. (ReceivedLength < PAGE_SIZE) &&
  4494. (SavedTotalLength <= (PAGE_SIZE / 2)) &&
  4495. !WriteThrough);
  4496. MorePages = (ReceivedLength > PAGE_SIZE);
  4497. //
  4498. // Copy the data to the user buffer.
  4499. //
  4500. try {
  4501. //
  4502. // It is possible that there is a locked page
  4503. // hanging around, and so we need to nuke it here.
  4504. //
  4505. if (SharedCacheMap->NeedToZero != NULL) {
  4506. CcFreeActiveVacb( SharedCacheMap, NULL, 0, 0 );
  4507. }
  4508. Status = STATUS_SUCCESS;
  4509. if (FlagOn(ZeroFlags, ZeroCase)) {
  4510. Status = MmCopyToCachedPage( CacheBuffer,
  4511. UserBuffer,
  4512. PageOffset,
  4513. MorePages ?
  4514. (PAGE_SIZE - PageOffset) :
  4515. (ReceivedLength - PageOffset),
  4516. SavePage );
  4517. if (!NT_SUCCESS(Status)) {
  4518. ExRaiseStatus( FsRtlNormalizeNtstatus( Status,
  4519. STATUS_INVALID_USER_BUFFER ));
  4520. }
  4521. //
  4522. // Otherwise, we have to actually copy the data ourselves.
  4523. //
  4524. } else {
  4525. MmSetPageFaultReadAhead( Thread,
  4526. (MorePages && FlagOn(ZeroFlags, ZERO_LAST_PAGE)) ? 1 : 0);
  4527. RtlCopyBytes( (PVOID)((PCHAR)CacheBuffer + PageOffset),
  4528. UserBuffer,
  4529. MorePages ?
  4530. (PAGE_SIZE - PageOffset) :
  4531. (ReceivedLength - PageOffset) );
  4532. MmResetPageFaultReadAhead( Thread, SavedState );
  4533. }
  4534. } except( CcCopyReadExceptionFilter( GetExceptionInformation(),
  4535. &Status ) ) {
  4536. //
  4537. // If we got an access violation, then the user buffer went
  4538. // away. Otherwise we must have gotten an I/O error trying
  4539. // to bring the data in.
  4540. //
  4541. if (Status == STATUS_ACCESS_VIOLATION) {
  4542. ExRaiseStatus( STATUS_INVALID_USER_BUFFER );
  4543. }
  4544. else {
  4545. ExRaiseStatus( FsRtlNormalizeNtstatus( Status,
  4546. STATUS_UNEXPECTED_IO_ERROR ));
  4547. }
  4548. }
  4549. //
  4550. // Now get out quickly if it is a small write and we want
  4551. // to save the page.
  4552. //
  4553. if (SavePage) {
  4554. ActivePage = (ULONG)( Vacb->Overlay.FileOffset.QuadPart >> PAGE_SHIFT ) +
  4555. (ULONG)(((PCHAR)CacheBuffer - (PCHAR)Vacb->BaseAddress) >>
  4556. PAGE_SHIFT);
  4557. PFileOffset.LowPart += ReceivedLength;
  4558. //
  4559. // If the cache page was not locked, then clear the address
  4560. // to zero from.
  4561. //
  4562. if (Status == STATUS_CACHE_PAGE_LOCKED) {
  4563. //
  4564. // We need to guarantee this Vacb for zeroing and calling
  4565. // MmUnlockCachedPage, so we increment the active count here
  4566. // and remember it for CcFreeActiveVacb.
  4567. //
  4568. CcAcquireVacbLock( &OldIrql );
  4569. Vacb->Overlay.ActiveCount += 1;
  4570. ExAcquireSpinLockAtDpcLevel( &SharedCacheMap->ActiveVacbSpinLock );
  4571. ASSERT(SharedCacheMap->NeedToZero == NULL);
  4572. SharedCacheMap->NeedToZero = (PVOID)((PCHAR)CacheBuffer +
  4573. (PFileOffset.LowPart & (PAGE_SIZE - 1)));
  4574. SharedCacheMap->NeedToZeroPage = ActivePage;
  4575. SharedCacheMap->NeedToZeroVacb = Vacb;
  4576. ExReleaseSpinLockFromDpcLevel( &SharedCacheMap->ActiveVacbSpinLock );
  4577. CcReleaseVacbLock( OldIrql );
  4578. }
  4579. SetActiveVacb( SharedCacheMap,
  4580. OldIrql,
  4581. Vacb,
  4582. ActivePage,
  4583. ACTIVE_PAGE_IS_DIRTY );
  4584. try_return( NOTHING );
  4585. }
  4586. //
  4587. // If it looks like we may save a page and exit on the next loop,
  4588. // then we must make sure to mark the current page dirty. Note
  4589. // that Cc[Fast]CopyWrite will finish the last part of any page
  4590. // before allowing us to free the Active Vacb above, therefore
  4591. // this case only occurs for a small random write.
  4592. //
  4593. if ((SavedTotalLength <= (PAGE_SIZE / 2)) && !WriteThrough) {
  4594. CcSetDirtyInMask( SharedCacheMap, &PFileOffset, ReceivedLength );
  4595. }
  4596. UserBuffer = (PVOID)((PCHAR)UserBuffer + (PAGE_SIZE - PageOffset));
  4597. PageOffset = 0;
  4598. //
  4599. // If there is more than a page to go (including what we just
  4600. // copied), then adjust our buffer pointer and counts, and
  4601. // determine if we are to the last page yet.
  4602. //
  4603. if (MorePages) {
  4604. CacheBuffer = (PCHAR)CacheBuffer + PAGE_SIZE;
  4605. ReceivedLength -= PAGE_SIZE;
  4606. //
  4607. // Update our offset to the page. Note that 32-bit
  4608. // add is ok since we cannot cross a Vacb boundary
  4609. // and we reinitialize this offset before entering
  4610. // this loop again.
  4611. //
  4612. PFileOffset.LowPart += PAGE_SIZE;
  4613. if (ReceivedLength > PAGE_SIZE) {
  4614. ZeroCase = ZERO_MIDDLE_PAGES;
  4615. } else {
  4616. ZeroCase = ZERO_LAST_PAGE;
  4617. }
  4618. } else {
  4619. break;
  4620. }
  4621. }
  4622. //
  4623. // If there is still more to write (ie. we are going to step
  4624. // onto the next vacb) AND we just dirtied more than 64K, then
  4625. // do a vicarious MmFlushSection here. This prevents us from
  4626. // creating unlimited dirty pages while holding the file
  4627. // resource exclusive. We also do not need to set the pages
  4628. // dirty in the mask in this case.
  4629. //
  4630. if (Length > CcMaxDirtyWrite) {
  4631. MmSetAddressRangeModified( SavedMappedBuffer, SavedMappedLength );
  4632. MmFlushSection( SharedCacheMap->FileObject->SectionObjectPointer,
  4633. &LocalOffset,
  4634. SavedMappedLength,
  4635. &IoStatus,
  4636. TRUE );
  4637. if (!NT_SUCCESS(IoStatus.Status)) {
  4638. ExRaiseStatus( FsRtlNormalizeNtstatus( IoStatus.Status,
  4639. STATUS_UNEXPECTED_IO_ERROR ));
  4640. }
  4641. //
  4642. // For write through files, call Mm to propagate the dirty bits
  4643. // here while we have the view mapped, so we know the flush will
  4644. // work below. Again - do not set dirty in the mask.
  4645. //
  4646. } else if (WriteThrough) {
  4647. MmSetAddressRangeModified( SavedMappedBuffer, SavedMappedLength );
  4648. //
  4649. // For the normal case, just set the pages dirty for the Lazy Writer
  4650. // now.
  4651. //
  4652. } else {
  4653. CcSetDirtyInMask( SharedCacheMap, &LocalOffset, SavedMappedLength );
  4654. }
  4655. CcFreeVirtualAddress( Vacb );
  4656. Vacb = NULL;
  4657. //
  4658. // If we have to loop back to get at least a page, it will be ok to
  4659. // zero the first page. If we are not getting at least a page, we
  4660. // must make sure we clear the ZeroFlags if we cannot zero the last
  4661. // page.
  4662. //
  4663. if (Length >= PAGE_SIZE) {
  4664. ZeroFlags |= ZERO_FIRST_PAGE;
  4665. } else if ((ZeroFlags & ZERO_LAST_PAGE) == 0) {
  4666. ZeroFlags = 0;
  4667. }
  4668. //
  4669. // Note that if ReceivedLength (and therefore SavedMappedLength)
  4670. // was truncated to the transfer size then the new LocalOffset
  4671. // computed below is not correct. This is not an issue since
  4672. // in that case (Length == 0) and we would never get here.
  4673. //
  4674. LocalOffset.QuadPart = LocalOffset.QuadPart + (LONGLONG)SavedMappedLength;
  4675. }
  4676. try_exit: NOTHING;
  4677. }
  4678. //
  4679. // Cleanup on the way out.
  4680. //
  4681. finally {
  4682. MmResetPageFaultReadAhead( Thread, SavedState );
  4683. //
  4684. // We have no work to do if we have squirreled away the Vacb.
  4685. //
  4686. if (!SavePage || AbnormalTermination()) {
  4687. //
  4688. // Make sure we do not leave anything mapped or dirty in the PTE
  4689. // on the way out.
  4690. //
  4691. if (Vacb != NULL) {
  4692. CcFreeVirtualAddress( Vacb );
  4693. }
  4694. //
  4695. // Either flush the whole range because of write through, or
  4696. // mark it dirty for the lazy writer.
  4697. //
  4698. if (WriteThrough) {
  4699. MmFlushSection ( SharedCacheMap->FileObject->SectionObjectPointer,
  4700. FileOffset,
  4701. SavedTotalLength,
  4702. &IoStatus,
  4703. TRUE );
  4704. if (!NT_SUCCESS(IoStatus.Status)) {
  4705. ExRaiseStatus( FsRtlNormalizeNtstatus( IoStatus.Status,
  4706. STATUS_UNEXPECTED_IO_ERROR ));
  4707. }
  4708. //
  4709. // Advance ValidDataGoal
  4710. //
  4711. LocalOffset.QuadPart = FileOffset->QuadPart + (LONGLONG)SavedTotalLength;
  4712. if (LocalOffset.QuadPart > SharedCacheMap->ValidDataGoal.QuadPart) {
  4713. SharedCacheMap->ValidDataGoal = LocalOffset;
  4714. }
  4715. }
  4716. }
  4717. }
  4718. DebugTrace(-1, me, "CcMapAndCopy -> %02lx\n", Result );
  4719. return;
  4720. }
  4721. BOOLEAN
  4722. CcLogError(
  4723. IN PFILE_OBJECT FileObject,
  4724. IN PUNICODE_STRING FileName,
  4725. IN NTSTATUS Error,
  4726. IN NTSTATUS DeviceError,
  4727. IN UCHAR IrpMajorCode
  4728. )
  4729. /*++
  4730. Routine Description:
  4731. This routine writes an eventlog entry to the eventlog.
  4732. Arguments:
  4733. FileObject - The fileobject in whose context the error occured.
  4734. FileName - The filename to use in logging the error (usually the DOS-side name)
  4735. Error - The error to log in the eventlog record
  4736. DeviceError - The actual error that occured in the device - will be logged
  4737. as user data
  4738. Return Value:
  4739. True if successful, false if internal memory allocation failed
  4740. --*/
  4741. {
  4742. UCHAR ErrorPacketLength;
  4743. UCHAR BasePacketLength;
  4744. ULONG StringLength;
  4745. PIO_ERROR_LOG_PACKET ErrorLogEntry = NULL;
  4746. BOOLEAN Result = FALSE;
  4747. PWCHAR String;
  4748. PAGED_CODE();
  4749. //
  4750. // Get our error packet, holding the string and status code. Note we log against the
  4751. // true filesystem if this is available.
  4752. //
  4753. // The sizing of the packet is a bit slimy since the dumpdata is already grown by a
  4754. // ULONG onto the end of the packet. Since NTSTATUS is ULONG, well, we just work in
  4755. // place.
  4756. //
  4757. BasePacketLength = sizeof(IO_ERROR_LOG_PACKET);
  4758. if ((BasePacketLength + FileName->Length + sizeof(WCHAR)) <= ERROR_LOG_MAXIMUM_SIZE) {
  4759. ErrorPacketLength = (UCHAR)(BasePacketLength + FileName->Length + sizeof(WCHAR));
  4760. } else {
  4761. ErrorPacketLength = ERROR_LOG_MAXIMUM_SIZE;
  4762. }
  4763. ErrorLogEntry = (PIO_ERROR_LOG_PACKET) IoAllocateErrorLogEntry( (FileObject->Vpb ?
  4764. FileObject->Vpb->DeviceObject :
  4765. FileObject->DeviceObject),
  4766. ErrorPacketLength );
  4767. if (ErrorLogEntry) {
  4768. //
  4769. // Fill in the nonzero members of the packet.
  4770. //
  4771. ErrorLogEntry->MajorFunctionCode = IrpMajorCode;
  4772. ErrorLogEntry->ErrorCode = Error;
  4773. ErrorLogEntry->FinalStatus = DeviceError;
  4774. ErrorLogEntry->DumpDataSize = sizeof(NTSTATUS);
  4775. RtlCopyMemory( &ErrorLogEntry->DumpData, &DeviceError, sizeof(NTSTATUS) );
  4776. //
  4777. // The filename string is appended to the end of the error log entry. We may
  4778. // have to smash the middle to fit it in the limited space.
  4779. //
  4780. StringLength = ErrorPacketLength - BasePacketLength - sizeof(WCHAR);
  4781. ASSERT(!(StringLength % sizeof(WCHAR)));
  4782. String = (PWCHAR) ((PUCHAR)ErrorLogEntry + BasePacketLength);
  4783. ErrorLogEntry->NumberOfStrings = 1;
  4784. ErrorLogEntry->StringOffset = BasePacketLength;
  4785. //
  4786. // If the name does not fit in the packet, divide the name equally to the
  4787. // prefix and suffix, with an ellipsis " .. " (4 wide characters) to indicate
  4788. // the loss.
  4789. //
  4790. if (StringLength < FileName->Length) {
  4791. //
  4792. // Remember, prefix + " .. " + suffix is the length. Calculate by figuring
  4793. // the prefix and then get the suffix by whacking the ellipsis and prefix off
  4794. // the total.
  4795. //
  4796. ULONG NamePrefixSegmentLength = ((StringLength/sizeof(WCHAR))/2 - 2)*sizeof(WCHAR);
  4797. ULONG NameSuffixSegmentLength = StringLength - 4*sizeof(WCHAR) - NamePrefixSegmentLength;
  4798. ASSERT(!(NamePrefixSegmentLength % sizeof(WCHAR)));
  4799. ASSERT(!(NameSuffixSegmentLength % sizeof(WCHAR)));
  4800. RtlCopyMemory( String,
  4801. FileName->Buffer,
  4802. NamePrefixSegmentLength );
  4803. String = (PWCHAR)((PCHAR)String + NamePrefixSegmentLength);
  4804. RtlCopyMemory( String,
  4805. L" .. ",
  4806. 4*sizeof(WCHAR) );
  4807. String += 4;
  4808. RtlCopyMemory( String,
  4809. (PUCHAR)FileName->Buffer +
  4810. FileName->Length - NameSuffixSegmentLength,
  4811. NameSuffixSegmentLength );
  4812. String = (PWCHAR)((PCHAR)String + NameSuffixSegmentLength);
  4813. } else {
  4814. RtlCopyMemory( String,
  4815. FileName->Buffer,
  4816. FileName->Length );
  4817. String += FileName->Length/sizeof(WCHAR);
  4818. }
  4819. //
  4820. // Null terminate the string and send the packet.
  4821. //
  4822. *String = L'\0';
  4823. IoWriteErrorLogEntry( ErrorLogEntry );
  4824. Result = TRUE;
  4825. }
  4826. return Result;
  4827. }
  4828. LOGICAL
  4829. CcHasInactiveViews (
  4830. VOID
  4831. )
  4832. /*++
  4833. Routine Description:
  4834. This routine is called by Memory Management only to query if the system
  4835. cache has any inactive views. If so, Memory Management may issue a
  4836. subsequent call to CcUnmapInactiveViews to discard these views in an
  4837. attempt to reclaim the prototype PTE pool (and other resources tied to
  4838. the section).
  4839. Arguments:
  4840. None.
  4841. Return Value:
  4842. TRUE if Cc has any views it can discard, FALSE if not.
  4843. Environment:
  4844. Arbitrary thread context, generally APC_LEVEL or DISPATCH_LEVEL. Various
  4845. mutexes and/or spinlocks may be held by the caller.
  4846. --*/
  4847. {
  4848. return FALSE; // BUGBUG - add code to flesh out.
  4849. }
  4850. LOGICAL
  4851. CcUnmapInactiveViews (
  4852. IN ULONG NumberOfViewsToUnmap
  4853. )
  4854. /*++
  4855. Routine Description:
  4856. This routine is called by Memory Management to request that the cache
  4857. manager unmap a number of inactive views. This call is generally made
  4858. because the system is low on pool (paged or nonpaged).
  4859. Discarding these views is done in an attempt to reclaim the prototype
  4860. PTE pool (and other resources tied to the section).
  4861. Arguments:
  4862. NumberOfViewsToUnmap - Supplies the desired number of views to unmap.
  4863. Return Value:
  4864. TRUE if Cc discarded *ANY* views, FALSE if not.
  4865. Environment:
  4866. Dereference segment thread context at PASSIVE_LEVEL.
  4867. --*/
  4868. {
  4869. UNREFERENCED_PARAMETER (NumberOfViewsToUnmap);
  4870. return FALSE; // BUGBUG - add code to flesh out.
  4871. }
  4872. #ifdef CCDBG
  4873. VOID
  4874. CcDump (
  4875. IN PVOID Ptr
  4876. )
  4877. {
  4878. PVOID Junk = Ptr;
  4879. }
  4880. #endif