Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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