Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

823 lines
18 KiB

  1. /*++
  2. Copyright (c) 1994 Microsoft Corporation
  3. Module Name:
  4. Cache.c
  5. Abstract:
  6. This module implements internal caching support routines. It does
  7. not interact with the cache manager.
  8. Author:
  9. Manny Weiser [MannyW] 05-Jan-1994
  10. Revision History:
  11. --*/
  12. #include "Procs.h"
  13. //
  14. // The local debug trace level
  15. //
  16. BOOLEAN
  17. SpaceForWriteBehind(
  18. PNONPAGED_FCB NpFcb,
  19. ULONG FileOffset,
  20. ULONG BytesToWrite
  21. );
  22. BOOLEAN
  23. OkToReadAhead(
  24. PFCB Fcb,
  25. IN ULONG FileOffset,
  26. IN UCHAR IoType
  27. );
  28. #define Dbg (DEBUG_TRACE_CACHE)
  29. //
  30. // Local procedure prototypes
  31. //
  32. #ifdef ALLOC_PRAGMA
  33. #pragma alloc_text( PAGE, CacheRead )
  34. #pragma alloc_text( PAGE, SpaceForWriteBehind )
  35. #pragma alloc_text( PAGE, CacheWrite )
  36. #pragma alloc_text( PAGE, OkToReadAhead )
  37. #pragma alloc_text( PAGE, CalculateReadAheadSize )
  38. #pragma alloc_text( PAGE, FlushCache )
  39. #pragma alloc_text( PAGE, AcquireFcbAndFlushCache )
  40. #endif
  41. ULONG
  42. CacheRead(
  43. IN PNONPAGED_FCB NpFcb,
  44. IN ULONG FileOffset,
  45. IN ULONG BytesToRead,
  46. IN PVOID UserBuffer
  47. , IN BOOLEAN WholeBufferOnly
  48. )
  49. /*++
  50. Routine Description:
  51. This routine attempts to satisfy a user read from cache. It returns
  52. the number of bytes actually copied from cache.
  53. Arguments:
  54. NpFcb - A pointer the the nonpaged FCB of the file being read.
  55. FileOffset - The file offset to read.
  56. BytesToRead - The number of bytes to read.
  57. UserBuffer - A pointer to the users target buffer.
  58. WholeBufferOnly - Do a cache read only if we can satisfy the entire
  59. read request.
  60. Return Value:
  61. The number of bytes copied to the user buffer.
  62. --*/
  63. {
  64. ULONG BytesToCopy;
  65. PAGED_CODE();
  66. if (DisableReadCache) return 0 ;
  67. DebugTrace(0, Dbg, "CacheRead...\n", 0 );
  68. DebugTrace( 0, Dbg, "FileOffset = %d\n", FileOffset );
  69. DebugTrace( 0, Dbg, "ByteCount = %d\n", BytesToRead );
  70. NwAcquireSharedFcb( NpFcb, TRUE );
  71. //
  72. // If this is a read ahead and it contains some data that the user
  73. // could be interested in, copy the interesting data.
  74. //
  75. if ( NpFcb->CacheType == ReadAhead &&
  76. NpFcb->CacheDataSize != 0 &&
  77. FileOffset >= NpFcb->CacheFileOffset &&
  78. FileOffset <= NpFcb->CacheFileOffset + NpFcb->CacheDataSize ) {
  79. if ( NpFcb->CacheBuffer ) {
  80. //
  81. // Make sure we have a CacheBuffer.
  82. //
  83. BytesToCopy =
  84. MIN ( BytesToRead,
  85. NpFcb->CacheFileOffset +
  86. NpFcb->CacheDataSize - FileOffset );
  87. if ( WholeBufferOnly && BytesToCopy != BytesToRead ) {
  88. NwReleaseFcb( NpFcb );
  89. return( 0 );
  90. }
  91. RtlCopyMemory(
  92. UserBuffer,
  93. NpFcb->CacheBuffer + ( FileOffset - NpFcb->CacheFileOffset ),
  94. BytesToCopy );
  95. DebugTrace(0, Dbg, "CacheRead -> %d\n", BytesToCopy );
  96. } else {
  97. ASSERT(FALSE); // we should never get here
  98. DebugTrace(0, Dbg, "CacheRead -> %08lx\n", 0 );
  99. BytesToCopy = 0;
  100. }
  101. } else {
  102. DebugTrace(0, Dbg, "CacheRead -> %08lx\n", 0 );
  103. BytesToCopy = 0;
  104. }
  105. NwReleaseFcb( NpFcb );
  106. return( BytesToCopy );
  107. }
  108. BOOLEAN
  109. SpaceForWriteBehind(
  110. PNONPAGED_FCB NpFcb,
  111. ULONG FileOffset,
  112. ULONG BytesToWrite
  113. )
  114. /*++
  115. Routine Description:
  116. This routine determines if it is ok to write behind this data to
  117. this FCB.
  118. Arguments:
  119. NpFcb - A pointer the the NONPAGED_FCB of the file being written.
  120. FileOffset - The file offset to write.
  121. BytesToWrite - The number of bytes to write.
  122. Return Value:
  123. The number of bytes copied to the user buffer.
  124. --*/
  125. {
  126. PAGED_CODE();
  127. if ( NpFcb->CacheDataSize == 0 ) {
  128. NpFcb->CacheFileOffset = FileOffset;
  129. }
  130. if ( NpFcb->CacheDataSize == 0 && BytesToWrite >= NpFcb->CacheSize ) {
  131. return( FALSE );
  132. }
  133. if ( FileOffset - NpFcb->CacheFileOffset + BytesToWrite >
  134. NpFcb->CacheSize ) {
  135. return( FALSE );
  136. }
  137. return( TRUE );
  138. }
  139. BOOLEAN
  140. CacheWrite(
  141. IN PIRP_CONTEXT IrpContext OPTIONAL,
  142. IN PNONPAGED_FCB NpFcb,
  143. IN ULONG FileOffset,
  144. IN ULONG BytesToWrite,
  145. IN PVOID UserBuffer
  146. )
  147. /*++
  148. Routine Description:
  149. This routine attempts to satisfy a user write to cache. The write
  150. succeeds if it is sequential and fits in the cache buffer.
  151. Arguments:
  152. IrpContext - A pointer to request parameters.
  153. NpFcb - A pointer the the NONPAGED_FCB of the file being read.
  154. FileOffset - The file offset to write.
  155. BytesToWrite - The number of bytes to write.
  156. UserBuffer - A pointer to the users source buffer.
  157. Return Value:
  158. The number of bytes copied to the user buffer.
  159. --*/
  160. {
  161. ULONG CacheSize;
  162. NTSTATUS status;
  163. PAGED_CODE();
  164. if (DisableWriteCache) return FALSE ;
  165. DebugTrace( +1, Dbg, "CacheWrite...\n", 0 );
  166. DebugTrace( 0, Dbg, "FileOffset = %d\n", FileOffset );
  167. DebugTrace( 0, Dbg, "ByteCount = %d\n", BytesToWrite );
  168. //
  169. // Grab the FCB resource so that we can check the
  170. // share access. (Bug 68546)
  171. //
  172. NwAcquireSharedFcb( NpFcb, TRUE );
  173. if ( NpFcb->Fcb->ShareAccess.SharedWrite ||
  174. NpFcb->Fcb->ShareAccess.SharedRead ) {
  175. DebugTrace( 0, Dbg, "File is not open in exclusive mode\n", 0 );
  176. DebugTrace( -1, Dbg, "CacheWrite -> FALSE\n", 0 );
  177. NwReleaseFcb( NpFcb );
  178. return( FALSE );
  179. }
  180. NwReleaseFcb( NpFcb );
  181. //
  182. // Note, If we decide to send data to the server we must be at the front
  183. // of the queue before we grab the Fcb exclusive.
  184. //
  185. TryAgain:
  186. NwAcquireExclusiveFcb( NpFcb, TRUE );
  187. //
  188. // Allocate a cache buffer if we don't already have one.
  189. //
  190. if ( NpFcb->CacheBuffer == NULL ) {
  191. if ( IrpContext == NULL ) {
  192. DebugTrace( 0, Dbg, "No cache buffer\n", 0 );
  193. DebugTrace( -1, Dbg, "CacheWrite -> FALSE\n", 0 );
  194. NwReleaseFcb( NpFcb );
  195. return( FALSE );
  196. }
  197. NpFcb->CacheType = WriteBehind;
  198. if (( IrpContext->pNpScb->SendBurstModeEnabled ) ||
  199. ( IrpContext->pNpScb->ReceiveBurstModeEnabled )) {
  200. CacheSize = IrpContext->pNpScb->MaxReceiveSize;
  201. } else {
  202. CacheSize = IrpContext->pNpScb->BufferSize;
  203. }
  204. try {
  205. NpFcb->CacheBuffer = ALLOCATE_POOL_EX( NonPagedPool, CacheSize );
  206. NpFcb->CacheSize = CacheSize;
  207. NpFcb->CacheMdl = ALLOCATE_MDL( NpFcb->CacheBuffer, CacheSize, FALSE, FALSE, NULL );
  208. if ( NpFcb->CacheMdl == NULL ) {
  209. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  210. }
  211. MmBuildMdlForNonPagedPool( NpFcb->CacheMdl );
  212. } except ( EXCEPTION_EXECUTE_HANDLER ) {
  213. if ( NpFcb->CacheBuffer != NULL) {
  214. FREE_POOL( NpFcb->CacheBuffer );
  215. NpFcb->CacheBuffer = NULL;
  216. NpFcb->CacheSize = 0;
  217. }
  218. DebugTrace( 0, Dbg, "Allocate failed\n", 0 );
  219. DebugTrace( -1, Dbg, "CacheWrite -> FALSE\n", 0 );
  220. NpFcb->CacheDataSize = 0;
  221. NwReleaseFcb( NpFcb );
  222. return( FALSE );
  223. }
  224. NpFcb->CacheFileOffset = 0;
  225. NpFcb->CacheDataSize = 0;
  226. } else if ( NpFcb->CacheType != WriteBehind ) {
  227. DebugTrace( -1, Dbg, "CacheWrite not writebehind -> FALSE\n", 0 );
  228. NwReleaseFcb( NpFcb );
  229. return( FALSE );
  230. }
  231. //
  232. // If the data is non sequential and non overlapping, flush the
  233. // existing cache.
  234. //
  235. if ( NpFcb->CacheDataSize != 0 &&
  236. ( FileOffset < NpFcb->CacheFileOffset ||
  237. FileOffset > NpFcb->CacheFileOffset + NpFcb->CacheDataSize ) ) {
  238. //
  239. // Release and then AcquireFcbAndFlush() will get us to the front
  240. // of the queue before re-acquiring. This avoids potential deadlocks.
  241. //
  242. NwReleaseFcb( NpFcb );
  243. if ( IrpContext != NULL ) {
  244. DebugTrace( 0, Dbg, "Data is not sequential, flushing data\n", 0 );
  245. status = AcquireFcbAndFlushCache( IrpContext, NpFcb );
  246. if ( !NT_SUCCESS( status ) ) {
  247. ExRaiseStatus( status );
  248. }
  249. }
  250. DebugTrace( -1, Dbg, "CacheWrite -> FALSE\n", 0 );
  251. return( FALSE );
  252. }
  253. //
  254. // The data is sequential, see if it fits.
  255. //
  256. if ( SpaceForWriteBehind( NpFcb, FileOffset, BytesToWrite ) ) {
  257. try {
  258. RtlCopyMemory(
  259. NpFcb->CacheBuffer + ( FileOffset - NpFcb->CacheFileOffset ),
  260. UserBuffer,
  261. BytesToWrite );
  262. } except ( EXCEPTION_EXECUTE_HANDLER ) {
  263. DebugTrace( 0, Dbg, "Bad user mode buffer in CacheWrite.\n", 0 );
  264. DebugTrace(-1, Dbg, "CacheWrite -> FALSE\n", 0 );
  265. NwReleaseFcb( NpFcb );
  266. return ( FALSE );
  267. }
  268. if ( NpFcb->CacheDataSize <
  269. (FileOffset - NpFcb->CacheFileOffset + BytesToWrite) ) {
  270. NpFcb->CacheDataSize =
  271. FileOffset - NpFcb->CacheFileOffset + BytesToWrite;
  272. }
  273. DebugTrace(-1, Dbg, "CacheWrite -> TRUE\n", 0 );
  274. NwReleaseFcb( NpFcb );
  275. return( TRUE );
  276. } else if ( IrpContext != NULL ) {
  277. //
  278. // The data didn't fit in the cache. If the cache is empty
  279. // then its time to return because it never will fit and we
  280. // have no stale data. This can happen if this request or
  281. // another being processed in parallel flush the cache and
  282. // TryAgain.
  283. //
  284. if ( NpFcb->CacheDataSize == 0 ) {
  285. DebugTrace(-1, Dbg, "CacheWrite -> FALSE\n", 0 );
  286. NwReleaseFcb( NpFcb );
  287. return( FALSE );
  288. }
  289. //
  290. // The data didn't fit in the cache, flush the cache
  291. //
  292. DebugTrace( 0, Dbg, "Cache is full, flushing data\n", 0 );
  293. //
  294. // We must be at the front of the Queue before writing.
  295. //
  296. NwReleaseFcb( NpFcb );
  297. status = AcquireFcbAndFlushCache( IrpContext, NpFcb );
  298. if ( !NT_SUCCESS( status ) ) {
  299. ExRaiseStatus( status );
  300. }
  301. //
  302. // Now see if it fits in the cache. We need to repeat all
  303. // the tests again because two requests can flush the cache at the
  304. // same time and the other one of them could have nearly filled it again.
  305. //
  306. goto TryAgain;
  307. } else {
  308. DebugTrace(-1, Dbg, "CacheWrite full -> FALSE\n", 0 );
  309. NwReleaseFcb( NpFcb );
  310. return( FALSE );
  311. }
  312. }
  313. BOOLEAN
  314. OkToReadAhead(
  315. PFCB Fcb,
  316. IN ULONG FileOffset,
  317. IN UCHAR IoType
  318. )
  319. /*++
  320. Routine Description:
  321. This routine determines whether the attempted i/o is sequential (so that
  322. we can use the cache).
  323. Arguments:
  324. Fcb - A pointer the the Fcb of the file being read.
  325. FileOffset - The file offset to read.
  326. Return Value:
  327. TRUE - The operation is sequential.
  328. FALSE - The operation is not sequential.
  329. --*/
  330. {
  331. PAGED_CODE();
  332. if ( Fcb->NonPagedFcb->CacheType == IoType &&
  333. !Fcb->ShareAccess.SharedWrite &&
  334. FileOffset == Fcb->LastReadOffset + Fcb->LastReadSize ) {
  335. DebugTrace(0, Dbg, "Io is sequential\n", 0 );
  336. return( TRUE );
  337. } else {
  338. DebugTrace(0, Dbg, "Io is not sequential\n", 0 );
  339. return( FALSE );
  340. }
  341. }
  342. ULONG
  343. CalculateReadAheadSize(
  344. IN PIRP_CONTEXT IrpContext,
  345. IN PNONPAGED_FCB NpFcb,
  346. IN ULONG CacheReadSize,
  347. IN ULONG FileOffset,
  348. IN ULONG ByteCount
  349. )
  350. /*++
  351. Routine Description:
  352. This routine determines the amount of data that can be read ahead,
  353. and sets up for the read.
  354. Note: Fcb must be acquired exclusive before calling.
  355. Arguments:
  356. NpFcb - A pointer the the nonpaged FCB of the file being read.
  357. FileOffset - The file offset to read.
  358. Return Value:
  359. The amount of data to read.
  360. --*/
  361. {
  362. ULONG ReadSize;
  363. ULONG CacheSize;
  364. PAGED_CODE();
  365. DebugTrace(+1, Dbg, "CalculateReadAheadSize\n", 0 );
  366. if (( IrpContext->pNpScb->SendBurstModeEnabled ) ||
  367. ( IrpContext->pNpScb->ReceiveBurstModeEnabled )) {
  368. CacheSize = IrpContext->pNpScb->MaxReceiveSize;
  369. } else {
  370. CacheSize = IrpContext->pNpScb->BufferSize;
  371. }
  372. //
  373. // The caller of this routine owns the FCB exclusive, so
  374. // we don't have to worry about the NpFcb fields like
  375. // ShareAccess.
  376. //
  377. if ( OkToReadAhead( NpFcb->Fcb, FileOffset - CacheReadSize, ReadAhead ) &&
  378. ByteCount < CacheSize ) {
  379. ReadSize = CacheSize;
  380. } else {
  381. //
  382. // Do not read ahead.
  383. //
  384. DebugTrace( 0, Dbg, "No read ahead\n", 0 );
  385. DebugTrace(-1, Dbg, "CalculateReadAheadSize -> %d\n", ByteCount );
  386. return ( ByteCount );
  387. }
  388. //
  389. // Allocate pool for the segment of the read
  390. //
  391. if ( NpFcb->CacheBuffer == NULL ) {
  392. try {
  393. NpFcb->CacheBuffer = ALLOCATE_POOL_EX( NonPagedPool, ReadSize );
  394. NpFcb->CacheSize = ReadSize;
  395. NpFcb->CacheMdl = ALLOCATE_MDL( NpFcb->CacheBuffer, ReadSize, FALSE, FALSE, NULL );
  396. if ( NpFcb->CacheMdl == NULL ) {
  397. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  398. }
  399. MmBuildMdlForNonPagedPool( NpFcb->CacheMdl );
  400. } except ( EXCEPTION_EXECUTE_HANDLER ) {
  401. if ( NpFcb->CacheBuffer != NULL) {
  402. FREE_POOL( NpFcb->CacheBuffer );
  403. NpFcb->CacheBuffer = NULL;
  404. }
  405. NpFcb->CacheSize = 0;
  406. NpFcb->CacheDataSize = 0;
  407. DebugTrace( 0, Dbg, "Failed to allocated buffer\n", 0 );
  408. DebugTrace(-1, Dbg, "CalculateReadAheadSize -> %d\n", ByteCount );
  409. return( ByteCount );
  410. }
  411. } else {
  412. ReadSize = MIN ( NpFcb->CacheSize, ReadSize );
  413. }
  414. DebugTrace(-1, Dbg, "CalculateReadAheadSize -> %d\n", ReadSize );
  415. return( ReadSize );
  416. }
  417. NTSTATUS
  418. FlushCache(
  419. PIRP_CONTEXT IrpContext,
  420. PNONPAGED_FCB NpFcb
  421. )
  422. /*++
  423. Routine Description:
  424. This routine flushes the cache buffer for the NpFcb. The caller must
  425. have acquired the FCB exclusive prior to making this call!
  426. Arguments:
  427. IrpContext - A pointer to request parameters.
  428. NpFcb - A pointer the the nonpaged FCB of the file to flush.
  429. Return Value:
  430. The amount of data to read.
  431. --*/
  432. {
  433. NTSTATUS status = STATUS_SUCCESS;
  434. PAGED_CODE();
  435. if ( NpFcb->CacheDataSize != 0 && NpFcb->CacheType == WriteBehind ) {
  436. LARGE_INTEGER ByteOffset;
  437. ByteOffset.QuadPart = NpFcb->CacheFileOffset;
  438. status = DoWrite(
  439. IrpContext,
  440. ByteOffset,
  441. NpFcb->CacheDataSize,
  442. NpFcb->CacheBuffer,
  443. NpFcb->CacheMdl );
  444. //
  445. // DoWrite leaves us at the head of the queue. The caller
  446. // is responsible for dequeueing the irp context appropriately.
  447. //
  448. if ( NT_SUCCESS( status ) ) {
  449. NpFcb->CacheDataSize = 0;
  450. }
  451. }
  452. return( status );
  453. }
  454. NTSTATUS
  455. AcquireFcbAndFlushCache(
  456. PIRP_CONTEXT IrpContext,
  457. PNONPAGED_FCB NpFcb
  458. )
  459. /*++
  460. Routine Description:
  461. This routine acquires the FCB exclusive and flushes the cache
  462. buffer for the acquired NpFcb.
  463. Arguments:
  464. IrpContext - A pointer to request parameters.
  465. NpFcb - A pointer the the nonpaged FCB of the file to flush.
  466. Return Value:
  467. The amount of data to read.
  468. --*/
  469. {
  470. NTSTATUS status = STATUS_SUCCESS;
  471. PAGED_CODE();
  472. NwAppendToQueueAndWait( IrpContext );
  473. NwAcquireExclusiveFcb( NpFcb, TRUE );
  474. status = FlushCache( IrpContext, NpFcb );
  475. //
  476. // Release the FCB and remove ourselves from the queue.
  477. // Frequently the caller will want to grab a resource so
  478. // we need to be off the queue then.
  479. //
  480. NwReleaseFcb( NpFcb );
  481. NwDequeueIrpContext( IrpContext, FALSE );
  482. return( status );
  483. }
  484. VOID
  485. FlushAllBuffers(
  486. PIRP_CONTEXT pIrpContext
  487. )
  488. /*+++
  489. Pretty self descriptive - flush all the buffers. The caller should
  490. not own any locks and should not be on an SCB queue.
  491. ---*/
  492. {
  493. PLIST_ENTRY pVcbListEntry;
  494. PLIST_ENTRY pFcbListEntry;
  495. PVCB pVcb;
  496. PFCB pFcb;
  497. PNONPAGED_SCB pOriginalNpScb;
  498. PNONPAGED_SCB pNpScb;
  499. PNONPAGED_FCB pNpFcb;
  500. DebugTrace( 0, Dbg, "FlushAllBuffers...\n", 0 );
  501. ASSERT( !BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE ) );
  502. pOriginalNpScb = pIrpContext->pNpScb;
  503. //
  504. // Grab the RCB so that we can touch the global VCB list.
  505. //
  506. NwAcquireExclusiveRcb( &NwRcb, TRUE );
  507. for ( pVcbListEntry = GlobalVcbList.Flink;
  508. pVcbListEntry != &GlobalVcbList;
  509. pVcbListEntry = pVcbListEntry->Flink ) {
  510. pVcb = CONTAINING_RECORD( pVcbListEntry, VCB, GlobalVcbListEntry );
  511. pNpScb = pVcb->Scb->pNpScb;
  512. pIrpContext->pNpScb = pNpScb;
  513. pIrpContext->pNpScb->pScb;
  514. //
  515. // Reference this SCB and VCB so they don't go away.
  516. //
  517. NwReferenceScb( pNpScb );
  518. NwReferenceVcb( pVcb );
  519. //
  520. // Release the RCB so we can get to the head of
  521. // the queue safely...
  522. //
  523. NwReleaseRcb( &NwRcb );
  524. NwAppendToQueueAndWait( pIrpContext );
  525. //
  526. // Reacquire the RCB so we can walk the FCB list safely.
  527. //
  528. NwAcquireExclusiveRcb( &NwRcb, TRUE );
  529. //
  530. // Flush all the FCBs for this VCB.
  531. //
  532. for ( pFcbListEntry = pVcb->FcbList.Flink;
  533. pFcbListEntry != &(pVcb->FcbList) ;
  534. pFcbListEntry = pFcbListEntry->Flink ) {
  535. pFcb = CONTAINING_RECORD( pFcbListEntry, FCB, FcbListEntry );
  536. pNpFcb = pFcb->NonPagedFcb;
  537. NwAcquireExclusiveFcb( pNpFcb, TRUE );
  538. FlushCache( pIrpContext, pNpFcb );
  539. NwReleaseFcb( pNpFcb );
  540. }
  541. NwDereferenceVcb( pVcb, pIrpContext, TRUE );
  542. NwReleaseRcb( &NwRcb );
  543. NwDequeueIrpContext( pIrpContext, FALSE );
  544. NwAcquireExclusiveRcb( &NwRcb, TRUE );
  545. NwDereferenceScb( pNpScb );
  546. }
  547. //
  548. // Release and restore.
  549. //
  550. NwReleaseRcb( &NwRcb );
  551. if ( pOriginalNpScb ) {
  552. pIrpContext->pNpScb = pOriginalNpScb;
  553. pIrpContext->pScb = pOriginalNpScb->pScb;
  554. } else {
  555. pIrpContext->pNpScb = NULL;
  556. pIrpContext->pScb = NULL;
  557. }
  558. return;
  559. }