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.

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