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.

1325 lines
35 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. pinsup.c
  5. Abstract:
  6. This module implements the pointer-based Pin support routines for the
  7. Cache subsystem.
  8. Author:
  9. Tom Miller [TomM] 4-June-1990
  10. Revision History:
  11. --*/
  12. #include "cc.h"
  13. //
  14. // Define our debug constant
  15. //
  16. #define me 0x00000008
  17. #if LIST_DBG
  18. #define SetCallersAddress(BCB) { \
  19. RtlGetCallersAddress( &(BCB)->CallerAddress, \
  20. &(BCB)->CallersCallerAddress ); \
  21. }
  22. #endif
  23. //
  24. // Internal routines
  25. //
  26. POBCB
  27. CcAllocateObcb (
  28. IN PLARGE_INTEGER FileOffset,
  29. IN ULONG Length,
  30. IN PBCB FirstBcb
  31. );
  32. #ifdef ALLOC_PRAGMA
  33. #if !LIST_DBG
  34. #pragma alloc_text(PAGE,CcMapData)
  35. #pragma alloc_text(PAGE,CcPinMappedData)
  36. #pragma alloc_text(PAGE,CcPinRead)
  37. #pragma alloc_text(PAGE,CcPreparePinWrite)
  38. #endif
  39. #pragma alloc_text(PAGE,CcUnpinData)
  40. #pragma alloc_text(PAGE,CcSetBcbOwnerPointer)
  41. #pragma alloc_text(PAGE,CcUnpinDataForThread)
  42. #pragma alloc_text(PAGE,CcAllocateObcb)
  43. #endif
  44. BOOLEAN
  45. CcMapData (
  46. IN PFILE_OBJECT FileObject,
  47. IN PLARGE_INTEGER FileOffset,
  48. IN ULONG Length,
  49. IN ULONG Flags,
  50. OUT PVOID *Bcb,
  51. OUT PVOID *Buffer
  52. )
  53. /*++
  54. Routine Description:
  55. This routine attempts to map the specified file data in the cache.
  56. A pointer is returned to the desired data in the cache.
  57. If the caller does not want to block on this call, then
  58. Wait should be supplied as FALSE. If Wait was supplied as FALSE and
  59. it is currently impossible to supply the requested data without
  60. blocking, then this routine will return FALSE. However, if the
  61. data is immediately accessible in the cache and no blocking is
  62. required, this routine returns TRUE with a pointer to the data.
  63. Note that a call to this routine with Wait supplied as TRUE is
  64. considerably faster than a call with Wait supplies as FALSE, because
  65. in the Wait TRUE case we only have to make sure the data is mapped
  66. in order to return.
  67. It is illegal to modify data that is only mapped, and can in fact lead
  68. to serious problems. It is impossible to check for this in all cases,
  69. however CcSetDirtyPinnedData may implement some Assertions to check for
  70. this. If the caller wishes to modify data that it has only mapped, then
  71. it must *first* call CcPinMappedData.
  72. In any case, the caller MUST subsequently call CcUnpinData.
  73. Naturally if CcPinRead or CcPreparePinWrite were called multiple
  74. times for the same data, CcUnpinData must be called the same number
  75. of times.
  76. The returned Buffer pointer is valid until the data is unpinned, at
  77. which point it is invalid to use the pointer further. This buffer pointer
  78. will remain valid if CcPinMappedData is called.
  79. Note that under some circumstances (like Wait supplied as FALSE or more
  80. than a page is requested), this routine may actually pin the data, however
  81. it is not necessary, and in fact not correct, for the caller to be concerned
  82. about this.
  83. Arguments:
  84. FileObject - Pointer to the file object for a file which was
  85. opened with NO_INTERMEDIATE_BUFFERING clear, i.e., for
  86. which CcInitializeCacheMap was called by the file system.
  87. FileOffset - Byte offset in file for desired data.
  88. Length - Length of desired data in bytes.
  89. Wait - FALSE if caller may not block, TRUE otherwise (see description
  90. above)
  91. Bcb - On the first call this returns a pointer to a Bcb
  92. parameter which must be supplied as input on all subsequent
  93. calls, for this buffer
  94. Buffer - Returns pointer to desired data, valid until the buffer is
  95. unpinned or freed. This pointer will remain valid if CcPinMappedData
  96. is called.
  97. Return Value:
  98. FALSE - if Wait was supplied as FALSE and the data was not delivered
  99. TRUE - if the data is being delivered
  100. --*/
  101. {
  102. PSHARED_CACHE_MAP SharedCacheMap;
  103. LARGE_INTEGER BeyondLastByte;
  104. ULONG ReceivedLength;
  105. ULONG SavedState;
  106. volatile UCHAR ch;
  107. PVOID TempBcb;
  108. ULONG PageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES((ULongToPtr(FileOffset->LowPart)), Length);
  109. PETHREAD Thread = PsGetCurrentThread();
  110. DebugTrace(+1, me, "CcMapData\n", 0 );
  111. MmSavePageFaultReadAhead( Thread, &SavedState );
  112. //
  113. // Increment performance counters
  114. //
  115. if (FlagOn(Flags, MAP_WAIT)) {
  116. CcMapDataWait += 1;
  117. //
  118. // Initialize the indirect pointer to our miss counter.
  119. //
  120. CcMissCounter = &CcMapDataWaitMiss;
  121. } else {
  122. CcMapDataNoWait += 1;
  123. }
  124. //
  125. // Get pointer to SharedCacheMap.
  126. //
  127. SharedCacheMap = *(PSHARED_CACHE_MAP *)((PCHAR)FileObject->SectionObjectPointer
  128. + sizeof(PVOID));
  129. //
  130. // Call local routine to Map or Access the file data. If we cannot map
  131. // the data because of a Wait condition, return FALSE.
  132. //
  133. if (FlagOn(Flags, MAP_WAIT)) {
  134. *Buffer = CcGetVirtualAddress( SharedCacheMap,
  135. *FileOffset,
  136. (PVACB *)&TempBcb,
  137. &ReceivedLength );
  138. ASSERT( ReceivedLength >= Length );
  139. } else if (!CcPinFileData( FileObject,
  140. FileOffset,
  141. Length,
  142. TRUE,
  143. FALSE,
  144. Flags,
  145. (PBCB *)&TempBcb,
  146. Buffer,
  147. &BeyondLastByte )) {
  148. DebugTrace(-1, me, "CcMapData -> FALSE\n", 0 );
  149. CcMapDataNoWaitMiss += 1;
  150. return FALSE;
  151. } else {
  152. ASSERT( (BeyondLastByte.QuadPart - FileOffset->QuadPart) >= Length );
  153. #if LIST_DBG
  154. {
  155. KIRQL OldIrql;
  156. PBCB BcbTemp = (PBCB)*Bcb;
  157. OldIrql = KeAcquireQueuedSpinLock( LockQueueBcbLock );
  158. if (BcbTemp->CcBcbLinks.Flink == NULL) {
  159. InsertTailList( &CcBcbList, &BcbTemp->CcBcbLinks );
  160. CcBcbCount += 1;
  161. KeReleaseQueuedSpinLock( LockQueueBcbLock, OldIrql );
  162. SetCallersAddress( BcbTemp );
  163. } else {
  164. KeReleaseQueuedSpinLock( LockQueueBcbLock, OldIrql );
  165. }
  166. }
  167. #endif
  168. }
  169. //
  170. // Caller specifically requested he doesn't want data to be faulted in.
  171. //
  172. if (!FlagOn( Flags, MAP_NO_READ )) {
  173. //
  174. // Now let's just sit here and take the miss(es) like a man (and count them).
  175. //
  176. try {
  177. //
  178. // Loop to touch each page
  179. //
  180. BeyondLastByte.LowPart = 0;
  181. while (PageCount != 0) {
  182. MmSetPageFaultReadAhead( Thread, PageCount - 1 );
  183. ch = *((volatile UCHAR *)(*Buffer) + BeyondLastByte.LowPart);
  184. BeyondLastByte.LowPart += PAGE_SIZE;
  185. PageCount -= 1;
  186. }
  187. } finally {
  188. MmResetPageFaultReadAhead( Thread, SavedState );
  189. if (AbnormalTermination() && (TempBcb != NULL)) {
  190. CcUnpinFileData( (PBCB)TempBcb, TRUE, UNPIN );
  191. }
  192. }
  193. }
  194. CcMissCounter = &CcThrowAway;
  195. //
  196. // Increment the pointer as a reminder that it is read only, and
  197. // return it. We pend this until now to avoid raising with a valid
  198. // Bcb into caller's contexts.
  199. //
  200. *(PCHAR *)&TempBcb += 1;
  201. *Bcb = TempBcb;
  202. DebugTrace(-1, me, "CcMapData -> TRUE\n", 0 );
  203. return TRUE;
  204. }
  205. BOOLEAN
  206. CcPinMappedData (
  207. IN PFILE_OBJECT FileObject,
  208. IN PLARGE_INTEGER FileOffset,
  209. IN ULONG Length,
  210. IN ULONG Flags,
  211. IN OUT PVOID *Bcb
  212. )
  213. /*++
  214. Routine Description:
  215. This routine attempts to pin data that was previously only mapped.
  216. If the routine determines that in fact it was necessary to actually
  217. pin the data when CcMapData was called, then this routine does not
  218. have to do anything.
  219. If the caller does not want to block on this call, then
  220. Wait should be supplied as FALSE. If Wait was supplied as FALSE and
  221. it is currently impossible to supply the requested data without
  222. blocking, then this routine will return FALSE. However, if the
  223. data is immediately accessible in the cache and no blocking is
  224. required, this routine returns TRUE with a pointer to the data.
  225. If the data is not returned in the first call, the caller
  226. may request the data later with Wait = TRUE. It is not required
  227. that the caller request the data later.
  228. If the caller subsequently modifies the data, it should call
  229. CcSetDirtyPinnedData.
  230. In any case, the caller MUST subsequently call CcUnpinData.
  231. Naturally if CcPinRead or CcPreparePinWrite were called multiple
  232. times for the same data, CcUnpinData must be called the same number
  233. of times.
  234. Note there are no performance counters in this routine, as the misses
  235. will almost always occur on the map above, and there will seldom be a
  236. miss on this conversion.
  237. Arguments:
  238. FileObject - Pointer to the file object for a file which was
  239. opened with NO_INTERMEDIATE_BUFFERING clear, i.e., for
  240. which CcInitializeCacheMap was called by the file system.
  241. FileOffset - Byte offset in file for desired data.
  242. Length - Length of desired data in bytes.
  243. Flags - (PIN_WAIT, PIN_EXCLUSIVE, PIN_NO_READ, etc. as defined in cache.h)
  244. If the caller specifies PIN_NO_READ and PIN_EXCLUSIVE, then he must
  245. guarantee that no one else will be attempting to map the view, if he
  246. wants to guarantee that the Bcb is not mapped (view may be purged).
  247. If the caller specifies PIN_NO_READ without PIN_EXCLUSIVE, the data
  248. may or may not be mapped in the return Bcb.
  249. Bcb - On the first call this returns a pointer to a Bcb
  250. parameter which must be supplied as input on all subsequent
  251. calls, for this buffer
  252. Return Value:
  253. FALSE - if Wait was not set and the data was not delivered
  254. TRUE - if the data is being delivered
  255. --*/
  256. {
  257. PVOID Buffer;
  258. LARGE_INTEGER BeyondLastByte;
  259. PSHARED_CACHE_MAP SharedCacheMap;
  260. LARGE_INTEGER LocalFileOffset = *FileOffset;
  261. POBCB MyBcb = NULL;
  262. PBCB *CurrentBcbPtr = (PBCB *)&MyBcb;
  263. BOOLEAN Result = FALSE;
  264. DebugTrace(+1, me, "CcPinMappedData\n", 0 );
  265. //
  266. // If the Bcb is no longer ReadOnly, then just return.
  267. //
  268. if ((*(PULONG)Bcb & 1) == 0) {
  269. return TRUE;
  270. }
  271. //
  272. // Remove the Read Only flag
  273. //
  274. *(PCHAR *)Bcb -= 1;
  275. //
  276. // Get pointer to SharedCacheMap.
  277. //
  278. SharedCacheMap = *(PSHARED_CACHE_MAP *)((PCHAR)FileObject->SectionObjectPointer
  279. + sizeof(PVOID));
  280. //
  281. // We only count the calls to this routine, since they are almost guaranteed
  282. // to be hits.
  283. //
  284. CcPinMappedDataCount += 1;
  285. //
  286. // Guarantee we will put the flag back if required.
  287. //
  288. try {
  289. if (((PBCB)*Bcb)->NodeTypeCode != CACHE_NTC_BCB) {
  290. //
  291. // Form loop to handle occasional overlapped Bcb case.
  292. //
  293. do {
  294. //
  295. // If we have already been through the loop, then adjust
  296. // our file offset and length from the last time.
  297. //
  298. if (MyBcb != NULL) {
  299. //
  300. // If this is the second time through the loop, then it is time
  301. // to handle the overlap case and allocate an OBCB.
  302. //
  303. if (CurrentBcbPtr == (PBCB *)&MyBcb) {
  304. MyBcb = CcAllocateObcb( FileOffset, Length, (PBCB)MyBcb );
  305. //
  306. // Set CurrentBcbPtr to point at the first entry in
  307. // the vector (which is already filled in), before
  308. // advancing it below.
  309. //
  310. CurrentBcbPtr = &MyBcb->Bcbs[0];
  311. }
  312. Length -= (ULONG)(BeyondLastByte.QuadPart - LocalFileOffset.QuadPart);
  313. LocalFileOffset.QuadPart = BeyondLastByte.QuadPart;
  314. CurrentBcbPtr += 1;
  315. }
  316. //
  317. // Call local routine to Map or Access the file data. If we cannot map
  318. // the data because of a Wait condition, return FALSE.
  319. //
  320. if (!CcPinFileData( FileObject,
  321. &LocalFileOffset,
  322. Length,
  323. (BOOLEAN)!FlagOn(SharedCacheMap->Flags, MODIFIED_WRITE_DISABLED),
  324. FALSE,
  325. Flags,
  326. CurrentBcbPtr,
  327. &Buffer,
  328. &BeyondLastByte )) {
  329. try_return( Result = FALSE );
  330. }
  331. //
  332. // Continue looping if we did not get everything.
  333. //
  334. } while((BeyondLastByte.QuadPart - LocalFileOffset.QuadPart) < Length);
  335. //
  336. // Free the Vacb before going on.
  337. //
  338. CcFreeVirtualAddress( (PVACB)*Bcb );
  339. *Bcb = MyBcb;
  340. //
  341. // Debug routines used to insert and remove Bcbs from the global list
  342. //
  343. #if LIST_DBG
  344. {
  345. KIRQL OldIrql;
  346. PBCB BcbTemp = (PBCB)*Bcb;
  347. OldIrql = KeAcquireQueuedSpinLock( LockQueueBcbLock );
  348. if (BcbTemp->CcBcbLinks.Flink == NULL) {
  349. InsertTailList( &CcBcbList, &BcbTemp->CcBcbLinks );
  350. CcBcbCount += 1;
  351. KeReleaseQueuedSpinLock( LockQueueBcbLock, OldIrql );
  352. SetCallersAddress( BcbTemp );
  353. } else {
  354. KeReleaseQueuedSpinLock( LockQueueBcbLock, OldIrql );
  355. }
  356. }
  357. #endif
  358. }
  359. //
  360. // If he really has a Bcb, all we have to do is acquire it shared since he is
  361. // no longer ReadOnly.
  362. //
  363. else {
  364. if (!ExAcquireSharedStarveExclusive( &((PBCB)*Bcb)->Resource, BooleanFlagOn(Flags, PIN_WAIT))) {
  365. try_return( Result = FALSE );
  366. }
  367. }
  368. Result = TRUE;
  369. try_exit: NOTHING;
  370. }
  371. finally {
  372. if (!Result) {
  373. //
  374. // Put the Read Only flag back
  375. //
  376. *(PCHAR *)Bcb += 1;
  377. //
  378. // We may have gotten partway through
  379. //
  380. if (MyBcb != NULL) {
  381. CcUnpinData( MyBcb );
  382. }
  383. }
  384. DebugTrace(-1, me, "CcPinMappedData -> %02lx\n", Result );
  385. }
  386. return Result;
  387. }
  388. BOOLEAN
  389. CcPinRead (
  390. IN PFILE_OBJECT FileObject,
  391. IN PLARGE_INTEGER FileOffset,
  392. IN ULONG Length,
  393. IN ULONG Flags,
  394. OUT PVOID *Bcb,
  395. OUT PVOID *Buffer
  396. )
  397. /*++
  398. Routine Description:
  399. This routine attempts to pin the specified file data in the cache.
  400. A pointer is returned to the desired data in the cache. This routine
  401. is intended for File System support and is not intended to be called
  402. from Dpc level.
  403. If the caller does not want to block on this call, then
  404. Wait should be supplied as FALSE. If Wait was supplied as FALSE and
  405. it is currently impossible to supply the requested data without
  406. blocking, then this routine will return FALSE. However, if the
  407. data is immediately accessible in the cache and no blocking is
  408. required, this routine returns TRUE with a pointer to the data.
  409. If the data is not returned in the first call, the caller
  410. may request the data later with Wait = TRUE. It is not required
  411. that the caller request the data later.
  412. If the caller subsequently modifies the data, it should call
  413. CcSetDirtyPinnedData.
  414. In any case, the caller MUST subsequently call CcUnpinData.
  415. Naturally if CcPinRead or CcPreparePinWrite were called multiple
  416. times for the same data, CcUnpinData must be called the same number
  417. of times.
  418. The returned Buffer pointer is valid until the data is unpinned, at
  419. which point it is invalid to use the pointer further.
  420. Arguments:
  421. FileObject - Pointer to the file object for a file which was
  422. opened with NO_INTERMEDIATE_BUFFERING clear, i.e., for
  423. which CcInitializeCacheMap was called by the file system.
  424. FileOffset - Byte offset in file for desired data.
  425. Length - Length of desired data in bytes.
  426. Flags - (PIN_WAIT, PIN_EXCLUSIVE, PIN_NO_READ, etc. as defined in cache.h)
  427. If the caller specifies PIN_NO_READ and PIN_EXCLUSIVE, then he must
  428. guarantee that no one else will be attempting to map the view, if he
  429. wants to guarantee that the Bcb is not mapped (view may be purged).
  430. If the caller specifies PIN_NO_READ without PIN_EXCLUSIVE, the data
  431. may or may not be mapped in the return Bcb.
  432. Bcb - On the first call this returns a pointer to a Bcb
  433. parameter which must be supplied as input on all subsequent
  434. calls, for this buffer
  435. Buffer - Returns pointer to desired data, valid until the buffer is
  436. unpinned or freed.
  437. Return Value:
  438. FALSE - if Wait was not set and the data was not delivered
  439. TRUE - if the data is being delivered
  440. --*/
  441. {
  442. PSHARED_CACHE_MAP SharedCacheMap;
  443. PVOID LocalBuffer;
  444. LARGE_INTEGER BeyondLastByte;
  445. LARGE_INTEGER LocalFileOffset = *FileOffset;
  446. POBCB MyBcb = NULL;
  447. PBCB *CurrentBcbPtr = (PBCB *)&MyBcb;
  448. BOOLEAN Result = FALSE;
  449. DebugTrace(+1, me, "CcPinRead\n", 0 );
  450. //
  451. // Increment performance counters
  452. //
  453. if (FlagOn(Flags, PIN_WAIT)) {
  454. CcPinReadWait += 1;
  455. //
  456. // Initialize the indirect pointer to our miss counter.
  457. //
  458. CcMissCounter = &CcPinReadWaitMiss;
  459. } else {
  460. CcPinReadNoWait += 1;
  461. }
  462. //
  463. // Get pointer to SharedCacheMap.
  464. //
  465. SharedCacheMap = *(PSHARED_CACHE_MAP *)((PCHAR)FileObject->SectionObjectPointer
  466. + sizeof(PVOID));
  467. try {
  468. //
  469. // Form loop to handle occasional overlapped Bcb case.
  470. //
  471. do {
  472. //
  473. // If we have already been through the loop, then adjust
  474. // our file offset and length from the last time.
  475. //
  476. if (MyBcb != NULL) {
  477. //
  478. // If this is the second time through the loop, then it is time
  479. // to handle the overlap case and allocate an OBCB.
  480. //
  481. if (CurrentBcbPtr == (PBCB *)&MyBcb) {
  482. MyBcb = CcAllocateObcb( FileOffset, Length, (PBCB)MyBcb );
  483. //
  484. // Set CurrentBcbPtr to point at the first entry in
  485. // the vector (which is already filled in), before
  486. // advancing it below.
  487. //
  488. CurrentBcbPtr = &MyBcb->Bcbs[0];
  489. //
  490. // Also on second time through, return starting Buffer
  491. //
  492. *Buffer = LocalBuffer;
  493. }
  494. Length -= (ULONG)(BeyondLastByte.QuadPart - LocalFileOffset.QuadPart);
  495. LocalFileOffset.QuadPart = BeyondLastByte.QuadPart;
  496. CurrentBcbPtr += 1;
  497. }
  498. //
  499. // Call local routine to Map or Access the file data. If we cannot map
  500. // the data because of a Wait condition, return FALSE.
  501. //
  502. if (!CcPinFileData( FileObject,
  503. &LocalFileOffset,
  504. Length,
  505. (BOOLEAN)!FlagOn(SharedCacheMap->Flags, MODIFIED_WRITE_DISABLED),
  506. FALSE,
  507. Flags,
  508. CurrentBcbPtr,
  509. &LocalBuffer,
  510. &BeyondLastByte )) {
  511. CcPinReadNoWaitMiss += 1;
  512. try_return( Result = FALSE );
  513. }
  514. //
  515. // Continue looping if we did not get everything.
  516. //
  517. } while((BeyondLastByte.QuadPart - LocalFileOffset.QuadPart) < Length);
  518. *Bcb = MyBcb;
  519. //
  520. // Debug routines used to insert and remove Bcbs from the global list
  521. //
  522. #if LIST_DBG
  523. {
  524. KIRQL OldIrql;
  525. PBCB BcbTemp = (PBCB)*Bcb;
  526. OldIrql = KeAcquireQueuedSpinLock( LockQueueBcbLock );
  527. if (BcbTemp->CcBcbLinks.Flink == NULL) {
  528. InsertTailList( &CcBcbList, &BcbTemp->CcBcbLinks );
  529. CcBcbCount += 1;
  530. KeReleaseQueuedSpinLock( LockQueueBcbLock, OldIrql );
  531. SetCallersAddress( BcbTemp );
  532. } else {
  533. KeReleaseQueuedSpinLock( LockQueueBcbLock, OldIrql );
  534. }
  535. }
  536. #endif
  537. //
  538. // In the normal (nonoverlapping) case we return the
  539. // correct buffer address here.
  540. //
  541. if (CurrentBcbPtr == (PBCB *)&MyBcb) {
  542. *Buffer = LocalBuffer;
  543. }
  544. Result = TRUE;
  545. try_exit: NOTHING;
  546. }
  547. finally {
  548. CcMissCounter = &CcThrowAway;
  549. if (!Result) {
  550. //
  551. // We may have gotten partway through
  552. //
  553. if (MyBcb != NULL) {
  554. CcUnpinData( MyBcb );
  555. }
  556. }
  557. DebugTrace(-1, me, "CcPinRead -> %02lx\n", Result );
  558. }
  559. return Result;
  560. }
  561. BOOLEAN
  562. CcPreparePinWrite (
  563. IN PFILE_OBJECT FileObject,
  564. IN PLARGE_INTEGER FileOffset,
  565. IN ULONG Length,
  566. IN BOOLEAN Zero,
  567. IN ULONG Flags,
  568. OUT PVOID *Bcb,
  569. OUT PVOID *Buffer
  570. )
  571. /*++
  572. Routine Description:
  573. This routine attempts to lock the specified file data in the cache
  574. and return a pointer to it along with the correct
  575. I/O status. Pages to be completely overwritten may be satisfied
  576. with emtpy pages.
  577. If not all of the pages can be prepared, and Wait was supplied as
  578. FALSE, then this routine will return FALSE, and its outputs will
  579. be meaningless. The caller may request the data later with
  580. Wait = TRUE. However, it is not required that the caller request
  581. the data later.
  582. If Wait is supplied as TRUE, and all of the pages can be prepared
  583. without blocking, this call will return TRUE immediately. Otherwise,
  584. this call will block until all of the pages can be prepared, and
  585. then return TRUE.
  586. When this call returns with TRUE, the caller may immediately begin
  587. to transfer data into the buffers via the Buffer pointer. The
  588. buffer will already be marked dirty.
  589. The caller MUST subsequently call CcUnpinData.
  590. Naturally if CcPinRead or CcPreparePinWrite were called multiple
  591. times for the same data, CcUnpinData must be called the same number
  592. of times.
  593. The returned Buffer pointer is valid until the data is unpinned, at
  594. which point it is invalid to use the pointer further.
  595. Arguments:
  596. FileObject - Pointer to the file object for a file which was
  597. opened with NO_INTERMEDIATE_BUFFERING clear, i.e., for
  598. which CcInitializeCacheMap was called by the file system.
  599. FileOffset - Byte offset in file for desired data.
  600. Length - Length of desired data in bytes.
  601. Zero - If supplied as TRUE, the buffer will be zeroed on return.
  602. Flags - (PIN_WAIT, PIN_EXCLUSIVE, PIN_NO_READ, etc. as defined in cache.h)
  603. If the caller specifies PIN_NO_READ and PIN_EXCLUSIVE, then he must
  604. guarantee that no one else will be attempting to map the view, if he
  605. wants to guarantee that the Bcb is not mapped (view may be purged).
  606. If the caller specifies PIN_NO_READ without PIN_EXCLUSIVE, the data
  607. may or may not be mapped in the return Bcb.
  608. Bcb - This returns a pointer to a Bcb parameter which must be
  609. supplied as input to CcPinWriteComplete.
  610. Buffer - Returns pointer to desired data, valid until the buffer is
  611. unpinned or freed.
  612. Return Value:
  613. FALSE - if Wait was not set and the data was not delivered
  614. TRUE - if the pages are being delivered
  615. --*/
  616. {
  617. PSHARED_CACHE_MAP SharedCacheMap;
  618. PVOID LocalBuffer;
  619. LARGE_INTEGER BeyondLastByte;
  620. LARGE_INTEGER LocalFileOffset = *FileOffset;
  621. POBCB MyBcb = NULL;
  622. PBCB *CurrentBcbPtr = (PBCB *)&MyBcb;
  623. ULONG OriginalLength = Length;
  624. BOOLEAN Result = FALSE;
  625. DebugTrace(+1, me, "CcPreparePinWrite\n", 0 );
  626. //
  627. // Get pointer to SharedCacheMap.
  628. //
  629. SharedCacheMap = *(PSHARED_CACHE_MAP *)((PCHAR)FileObject->SectionObjectPointer
  630. + sizeof(PVOID));
  631. try {
  632. //
  633. // Form loop to handle occasional overlapped Bcb case.
  634. //
  635. do {
  636. //
  637. // If we have already been through the loop, then adjust
  638. // our file offset and length from the last time.
  639. //
  640. if (MyBcb != NULL) {
  641. //
  642. // If this is the second time through the loop, then it is time
  643. // to handle the overlap case and allocate an OBCB.
  644. //
  645. if (CurrentBcbPtr == (PBCB *)&MyBcb) {
  646. MyBcb = CcAllocateObcb( FileOffset, Length, (PBCB)MyBcb );
  647. //
  648. // Set CurrentBcbPtr to point at the first entry in
  649. // the vector (which is already filled in), before
  650. // advancing it below.
  651. //
  652. CurrentBcbPtr = &MyBcb->Bcbs[0];
  653. //
  654. // Also on second time through, return starting Buffer
  655. //
  656. *Buffer = LocalBuffer;
  657. }
  658. Length -= (ULONG)(BeyondLastByte.QuadPart - LocalFileOffset.QuadPart);
  659. LocalFileOffset.QuadPart = BeyondLastByte.QuadPart;
  660. CurrentBcbPtr += 1;
  661. }
  662. //
  663. // Call local routine to Map or Access the file data. If we cannot map
  664. // the data because of a Wait condition, return FALSE.
  665. //
  666. if (!CcPinFileData( FileObject,
  667. &LocalFileOffset,
  668. Length,
  669. FALSE,
  670. TRUE,
  671. Flags,
  672. CurrentBcbPtr,
  673. &LocalBuffer,
  674. &BeyondLastByte )) {
  675. try_return( Result = FALSE );
  676. }
  677. //
  678. // Continue looping if we did not get everything.
  679. //
  680. } while((BeyondLastByte.QuadPart - LocalFileOffset.QuadPart) < Length);
  681. //
  682. // Debug routines used to insert and remove Bcbs from the global list
  683. //
  684. #if LIST_DBG
  685. {
  686. KIRQL OldIrql;
  687. PBCB BcbTemp = (PBCB)*Bcb;
  688. OldIrql = KeAcquireQueuedSpinLock( LockQueueBcbLock );
  689. if (BcbTemp->CcBcbLinks.Flink == NULL) {
  690. InsertTailList( &CcBcbList, &BcbTemp->CcBcbLinks );
  691. CcBcbCount += 1;
  692. KeReleaseQueuedSpinLock( LockQueueBcbLock, OldIrql );
  693. SetCallersAddress( BcbTemp );
  694. } else {
  695. KeReleaseQueuedSpinLock( LockQueueBcbLock, OldIrql );
  696. }
  697. }
  698. #endif
  699. //
  700. // In the normal (nonoverlapping) case we return the
  701. // correct buffer address here.
  702. //
  703. if (CurrentBcbPtr == (PBCB *)&MyBcb) {
  704. *Buffer = LocalBuffer;
  705. }
  706. if (Zero) {
  707. RtlZeroMemory( *Buffer, OriginalLength );
  708. }
  709. CcSetDirtyPinnedData( MyBcb, NULL );
  710. //
  711. // Fill in the return argument.
  712. //
  713. *Bcb = MyBcb;
  714. Result = TRUE;
  715. try_exit: NOTHING;
  716. }
  717. finally {
  718. CcMissCounter = &CcThrowAway;
  719. if (!Result) {
  720. //
  721. // We may have gotten partway through
  722. //
  723. if (MyBcb != NULL) {
  724. CcUnpinData( MyBcb );
  725. }
  726. }
  727. DebugTrace(-1, me, "CcPreparePinWrite -> %02lx\n", Result );
  728. }
  729. return Result;
  730. }
  731. VOID
  732. CcUnpinData (
  733. IN PVOID Bcb
  734. )
  735. /*++
  736. Routine Description:
  737. This routine must be called at IPL0, some time after calling CcPinRead
  738. or CcPreparePinWrite. It performs any cleanup that is necessary.
  739. Arguments:
  740. Bcb - Bcb parameter returned from the last call to CcPinRead.
  741. Return Value:
  742. None.
  743. --*/
  744. {
  745. DebugTrace(+1, me, "CcUnpinData:\n", 0 );
  746. DebugTrace( 0, me, " >Bcb = %08lx\n", Bcb );
  747. //
  748. // Test for ReadOnly and unpin accordingly.
  749. //
  750. if (((ULONG_PTR)Bcb & 1) != 0) {
  751. //
  752. // Remove the Read Only flag
  753. //
  754. Bcb = (PVOID) ((ULONG_PTR)Bcb & ~1);
  755. CcUnpinFileData( (PBCB)Bcb, TRUE, UNPIN );
  756. } else {
  757. //
  758. // Handle the overlapped Bcb case.
  759. //
  760. if (((POBCB)Bcb)->NodeTypeCode == CACHE_NTC_OBCB) {
  761. PBCB *BcbPtrPtr = &((POBCB)Bcb)->Bcbs[0];
  762. //
  763. // Loop to free all Bcbs with recursive calls
  764. // (rather than dealing with RO for this uncommon case).
  765. //
  766. while (*BcbPtrPtr != NULL) {
  767. CcUnpinData(*(BcbPtrPtr++));
  768. }
  769. //
  770. // Then free the pool for the Obcb
  771. //
  772. ExFreePool( Bcb );
  773. //
  774. // Otherwise, it is a normal Bcb
  775. //
  776. } else {
  777. CcUnpinFileData( (PBCB)Bcb, FALSE, UNPIN );
  778. }
  779. }
  780. DebugTrace(-1, me, "CcUnPinData -> VOID\n", 0 );
  781. }
  782. VOID
  783. CcSetBcbOwnerPointer (
  784. IN PVOID Bcb,
  785. IN PVOID OwnerPointer
  786. )
  787. /*++
  788. Routine Description:
  789. This routine may be called to set the resource owner for the Bcb resource,
  790. for cases where another thread will do the unpin *and* the current thread
  791. may exit.
  792. Arguments:
  793. Bcb - Bcb parameter returned from the last call to CcPinRead.
  794. OwnerPointer - A valid resource owner pointer, which means a pointer to
  795. an allocated system address, with the low-order two bits
  796. set. The address may not be deallocated until after the
  797. unpin call.
  798. Return Value:
  799. None.
  800. --*/
  801. {
  802. ASSERT(((ULONG_PTR)Bcb & 1) == 0);
  803. //
  804. // Handle the overlapped Bcb case.
  805. //
  806. if (((POBCB)Bcb)->NodeTypeCode == CACHE_NTC_OBCB) {
  807. PBCB *BcbPtrPtr = &((POBCB)Bcb)->Bcbs[0];
  808. //
  809. // Loop to set owner for all Bcbs.
  810. //
  811. while (*BcbPtrPtr != NULL) {
  812. ExSetResourceOwnerPointer( &(*BcbPtrPtr)->Resource, OwnerPointer );
  813. BcbPtrPtr++;
  814. }
  815. //
  816. // Otherwise, it is a normal Bcb
  817. //
  818. } else {
  819. //
  820. // Handle normal case.
  821. //
  822. ExSetResourceOwnerPointer( &((PBCB)Bcb)->Resource, OwnerPointer );
  823. }
  824. }
  825. VOID
  826. CcUnpinDataForThread (
  827. IN PVOID Bcb,
  828. IN ERESOURCE_THREAD ResourceThreadId
  829. )
  830. /*++
  831. Routine Description:
  832. This routine must be called at IPL0, some time after calling CcPinRead
  833. or CcPreparePinWrite. It performs any cleanup that is necessary,
  834. releasing the Bcb resource for the given thread.
  835. Arguments:
  836. Bcb - Bcb parameter returned from the last call to CcPinRead.
  837. Return Value:
  838. None.
  839. --*/
  840. {
  841. DebugTrace(+1, me, "CcUnpinDataForThread:\n", 0 );
  842. DebugTrace( 0, me, " >Bcb = %08lx\n", Bcb );
  843. DebugTrace( 0, me, " >ResoureceThreadId = %08lx\n", ResoureceThreadId );
  844. //
  845. // Test for ReadOnly and unpin accordingly.
  846. //
  847. if (((ULONG_PTR)Bcb & 1) != 0) {
  848. //
  849. // Remove the Read Only flag
  850. //
  851. Bcb = (PVOID) ((ULONG_PTR)Bcb & ~1);
  852. CcUnpinFileData( (PBCB)Bcb, TRUE, UNPIN );
  853. } else {
  854. //
  855. // Handle the overlapped Bcb case.
  856. //
  857. if (((POBCB)Bcb)->NodeTypeCode == CACHE_NTC_OBCB) {
  858. PBCB *BcbPtrPtr = &((POBCB)Bcb)->Bcbs[0];
  859. //
  860. // Loop to free all Bcbs with recursive calls
  861. // (rather than dealing with RO for this uncommon case).
  862. //
  863. while (*BcbPtrPtr != NULL) {
  864. CcUnpinDataForThread( *(BcbPtrPtr++), ResourceThreadId );
  865. }
  866. //
  867. // Then free the pool for the Obcb
  868. //
  869. ExFreePool( Bcb );
  870. //
  871. // Otherwise, it is a normal Bcb
  872. //
  873. } else {
  874. //
  875. // If not readonly, we can release the resource for the thread first,
  876. // and then call CcUnpinFileData. Release resource first in case
  877. // Bcb gets deallocated.
  878. //
  879. ExReleaseResourceForThreadLite( &((PBCB)Bcb)->Resource, ResourceThreadId );
  880. CcUnpinFileData( (PBCB)Bcb, TRUE, UNPIN );
  881. }
  882. }
  883. DebugTrace(-1, me, "CcUnpinDataForThread -> VOID\n", 0 );
  884. }
  885. POBCB
  886. CcAllocateObcb (
  887. IN PLARGE_INTEGER FileOffset,
  888. IN ULONG Length,
  889. IN PBCB FirstBcb
  890. )
  891. /*++
  892. Routine Description:
  893. This routine is called by the various pinning routines to allocate and
  894. initialize an overlap Bcb.
  895. Arguments:
  896. FileOffset - Starting file offset for the Obcb (An Obcb starts with a
  897. public structure, which someone could use)
  898. Length - Length of the range covered by the Obcb
  899. FirstBcb - First Bcb already created, which only covers the start of
  900. the desired range (low order bit may be set to indicate ReadOnly)
  901. Return Value:
  902. Pointer to the allocated Obcb
  903. --*/
  904. {
  905. ULONG LengthToAllocate;
  906. POBCB Obcb;
  907. PBCB Bcb = (PBCB)((ULONG_PTR)FirstBcb & ~1);
  908. //
  909. // Allocate according to the worst case, assuming that we
  910. // will need as many additional Bcbs as there are pages
  911. // remaining. Also throw in one more pointer to guarantee
  912. // users of the OBCB can always terminate on NULL.
  913. //
  914. // We remove fron consideration the range described by the
  915. // first Bcb (note that the range of the Obcb is not strictly
  916. // starting at the first Bcb) and add in locations for the first
  917. // bcb and the null.
  918. //
  919. LengthToAllocate = FIELD_OFFSET(OBCB, Bcbs) + (2 * sizeof(PBCB)) +
  920. ((Length -
  921. (Bcb->ByteLength -
  922. (FileOffset->HighPart?
  923. (ULONG)(FileOffset->QuadPart - Bcb->FileOffset.QuadPart) :
  924. FileOffset->LowPart - Bcb->FileOffset.LowPart)) +
  925. PAGE_SIZE - 1) / PAGE_SIZE) * sizeof(PBCB);
  926. Obcb = ExAllocatePoolWithTag( NonPagedPool, LengthToAllocate, 'bOcC' );
  927. if (Obcb == NULL) {
  928. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  929. }
  930. RtlZeroMemory( Obcb, LengthToAllocate );
  931. Obcb->NodeTypeCode = CACHE_NTC_OBCB;
  932. Obcb->NodeByteSize = (USHORT)LengthToAllocate;
  933. Obcb->ByteLength = Length;
  934. Obcb->FileOffset = *FileOffset;
  935. Obcb->Bcbs[0] = FirstBcb;
  936. return Obcb;
  937. }