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.

1514 lines
40 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. buildmdl.c
  5. Abstract:
  6. This module contains the Mm support routines for the cache manager to
  7. prefetching groups of pages from secondary storage using logical file
  8. offets instead of virtual addresses. This saves the cache manager from
  9. having to map pages unnecessarily.
  10. The caller builds a list of various file objects and logical block offsets,
  11. passing them to MmPrefetchPagesIntoLockedMdl. The code here then examines
  12. the internal pages, reading in those that are not already valid or in
  13. transition. These pages are read with a single read, using a dummy page
  14. to bridge gaps of pages that were valid or transition prior to the I/O
  15. being issued.
  16. Upon conclusion of the I/O, control is returned to the calling thread.
  17. All pages are referenced counted as though they were probed and locked,
  18. regardless of whether they are currently valid or transition.
  19. Author:
  20. Landy Wang (landyw) 12-Feb-2001
  21. Revision History:
  22. --*/
  23. #include "mi.h"
  24. #if DBG
  25. ULONG MiCcDebug;
  26. #define MI_CC_FORCE_PREFETCH 0x1 // Trim all user pages to force prefetch
  27. #define MI_CC_DELAY 0x2 // Delay hoping to trigger collisions
  28. #endif
  29. typedef struct _MI_READ_INFO {
  30. PCONTROL_AREA ControlArea;
  31. PFILE_OBJECT FileObject;
  32. LARGE_INTEGER FileOffset;
  33. PMMINPAGE_SUPPORT InPageSupport;
  34. PMDL IoMdl;
  35. PMDL ApiMdl;
  36. PMMPFN DummyPagePfn;
  37. PSUBSECTION FirstReferencedSubsection;
  38. PSUBSECTION LastReferencedSubsection;
  39. SIZE_T LengthInBytes;
  40. } MI_READ_INFO, *PMI_READ_INFO;
  41. VOID
  42. MiCcReleasePrefetchResources (
  43. IN PMI_READ_INFO MiReadInfo,
  44. IN NTSTATUS Status
  45. );
  46. NTSTATUS
  47. MiCcPrepareReadInfo (
  48. IN PMI_READ_INFO MiReadInfo
  49. );
  50. NTSTATUS
  51. MiCcPutPagesInTransition (
  52. IN PMI_READ_INFO MiReadInfo
  53. );
  54. NTSTATUS
  55. MiCcCompletePrefetchIos (
  56. PMI_READ_INFO MiReadInfo
  57. );
  58. VOID
  59. MiRemoveUserPages (
  60. VOID
  61. );
  62. VOID
  63. MiPfFreeDummyPage (
  64. IN PMMPFN DummyPagePfn
  65. );
  66. #ifdef ALLOC_PRAGMA
  67. #pragma alloc_text (PAGE, MmPrefetchPagesIntoLockedMdl)
  68. #pragma alloc_text (PAGE, MiCcPrepareReadInfo)
  69. #pragma alloc_text (PAGE, MiCcReleasePrefetchResources)
  70. #endif
  71. NTSTATUS
  72. MmPrefetchPagesIntoLockedMdl (
  73. IN PFILE_OBJECT FileObject,
  74. IN PLARGE_INTEGER FileOffset,
  75. IN SIZE_T Length,
  76. OUT PMDL *MdlOut
  77. )
  78. /*++
  79. Routine Description:
  80. This routine fills an MDL with pages described by the file object's
  81. offset and length.
  82. This routine is for cache manager usage only.
  83. Arguments:
  84. FileObject - Supplies a 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 - Supplies the byte offset in the file for the desired data.
  88. Length - Supplies the length of the desired data in bytes.
  89. MdlOut - On output it returns a pointer to an Mdl describing
  90. the desired data.
  91. Return Value:
  92. NTSTATUS.
  93. Environment:
  94. Kernel mode. PASSIVE_LEVEL.
  95. --*/
  96. {
  97. MI_READ_INFO MiReadInfo;
  98. NTSTATUS status;
  99. KIRQL OldIrql;
  100. LOGICAL ApcNeeded;
  101. PETHREAD CurrentThread;
  102. ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
  103. RtlZeroMemory (&MiReadInfo, sizeof(MiReadInfo));
  104. MiReadInfo.FileObject = FileObject;
  105. MiReadInfo.FileOffset = *FileOffset;
  106. MiReadInfo.LengthInBytes = Length;
  107. //
  108. // Prepare for the impending read : allocate MDLs, inpage blocks,
  109. // reference count subsections, etc.
  110. //
  111. status = MiCcPrepareReadInfo (&MiReadInfo);
  112. if (!NT_SUCCESS (status)) {
  113. MiCcReleasePrefetchResources (&MiReadInfo, status);
  114. return status;
  115. }
  116. ASSERT (MiReadInfo.InPageSupport != NULL);
  117. //
  118. // APCs must be disabled once we put a page in transition. Otherwise
  119. // a thread suspend will stop us from issuing the I/O - this will hang
  120. // any other threads that need the same page.
  121. //
  122. CurrentThread = PsGetCurrentThread();
  123. ApcNeeded = FALSE;
  124. ASSERT ((PKTHREAD)CurrentThread == KeGetCurrentThread ());
  125. KeEnterCriticalRegionThread ((PKTHREAD)CurrentThread);
  126. //
  127. // The nested fault count protects this thread from deadlocks where a
  128. // special kernel APC fires and references the same user page(s) we are
  129. // putting in transition.
  130. //
  131. KeRaiseIrql (APC_LEVEL, &OldIrql);
  132. ASSERT (CurrentThread->NestedFaultCount == 0);
  133. CurrentThread->NestedFaultCount += 1;
  134. KeLowerIrql (OldIrql);
  135. //
  136. // Allocate physical memory, lock down all the pages and issue any
  137. // I/O that may be needed. When MiCcPutPagesInTransition returns
  138. // STATUS_SUCCESS or STATUS_ISSUE_PAGING_IO, it guarantees that the
  139. // ApiMdl contains reference-counted (locked-down) pages.
  140. //
  141. status = MiCcPutPagesInTransition (&MiReadInfo);
  142. if (NT_SUCCESS (status)) {
  143. //
  144. // No I/O was issued because all the pages were already resident and
  145. // have now been locked down.
  146. //
  147. ASSERT (MiReadInfo.ApiMdl != NULL);
  148. }
  149. else if (status == STATUS_ISSUE_PAGING_IO) {
  150. //
  151. // Wait for the I/O to complete. Note APCs must remain disabled.
  152. //
  153. ASSERT (MiReadInfo.InPageSupport != NULL);
  154. status = MiCcCompletePrefetchIos (&MiReadInfo);
  155. }
  156. else {
  157. //
  158. // Some error occurred (like insufficient memory, etc) so fail
  159. // the request by falling through.
  160. //
  161. }
  162. //
  163. // Release acquired resources like pool, subsections, etc.
  164. //
  165. MiCcReleasePrefetchResources (&MiReadInfo, status);
  166. //
  167. // Only now that the I/O have been completed (not just issued) can
  168. // APCs be re-enabled. This prevents a user-issued suspend APC from
  169. // keeping a shared page in transition forever.
  170. //
  171. KeRaiseIrql (APC_LEVEL, &OldIrql);
  172. ASSERT (CurrentThread->NestedFaultCount == 1);
  173. CurrentThread->NestedFaultCount -= 1;
  174. if (CurrentThread->ApcNeeded == 1) {
  175. ApcNeeded = TRUE;
  176. CurrentThread->ApcNeeded = 0;
  177. }
  178. KeLowerIrql (OldIrql);
  179. KeLeaveCriticalRegionThread ((PKTHREAD)CurrentThread);
  180. ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
  181. ASSERT (CurrentThread->NestedFaultCount == 0);
  182. ASSERT (CurrentThread->ApcNeeded == 0);
  183. if (ApcNeeded == TRUE) {
  184. KeRaiseIrql (APC_LEVEL, &OldIrql);
  185. IoRetryIrpCompletions ();
  186. KeLowerIrql (OldIrql);
  187. }
  188. *MdlOut = MiReadInfo.ApiMdl;
  189. return status;
  190. }
  191. VOID
  192. MiCcReleasePrefetchResources (
  193. IN PMI_READ_INFO MiReadInfo,
  194. IN NTSTATUS Status
  195. )
  196. /*++
  197. Routine Description:
  198. This routine releases all resources consumed to handle a system cache
  199. logical offset based prefetch.
  200. Environment:
  201. Kernel mode, PASSIVE_LEVEL.
  202. --*/
  203. {
  204. PSUBSECTION FirstReferencedSubsection;
  205. PSUBSECTION LastReferencedSubsection;
  206. //
  207. // Release all subsection prototype PTE references.
  208. //
  209. FirstReferencedSubsection = MiReadInfo->FirstReferencedSubsection;
  210. LastReferencedSubsection = MiReadInfo->LastReferencedSubsection;
  211. while (FirstReferencedSubsection != LastReferencedSubsection) {
  212. MiRemoveViewsFromSectionWithPfn ((PMSUBSECTION) FirstReferencedSubsection,
  213. FirstReferencedSubsection->PtesInSubsection);
  214. FirstReferencedSubsection = FirstReferencedSubsection->NextSubsection;
  215. }
  216. if (MiReadInfo->IoMdl != NULL) {
  217. ExFreePool (MiReadInfo->IoMdl);
  218. }
  219. //
  220. // Note successful returns yield the ApiMdl so don't free it here.
  221. //
  222. if (!NT_SUCCESS (Status)) {
  223. if (MiReadInfo->ApiMdl != NULL) {
  224. ExFreePool (MiReadInfo->ApiMdl);
  225. }
  226. }
  227. if (MiReadInfo->InPageSupport != NULL) {
  228. #if DBG
  229. MiReadInfo->InPageSupport->ListEntry.Next = NULL;
  230. #endif
  231. MiFreeInPageSupportBlock (MiReadInfo->InPageSupport);
  232. }
  233. //
  234. // Put DummyPage back on the free list.
  235. //
  236. if (MiReadInfo->DummyPagePfn != NULL) {
  237. MiPfFreeDummyPage (MiReadInfo->DummyPagePfn);
  238. }
  239. }
  240. NTSTATUS
  241. MiCcPrepareReadInfo (
  242. IN PMI_READ_INFO MiReadInfo
  243. )
  244. /*++
  245. Routine Description:
  246. This routine constructs MDLs that describe the pages in the argument
  247. read-list. The caller will then issue the I/O on return.
  248. Arguments:
  249. MiReadInfo - Supplies a pointer to the read-list.
  250. Return Value:
  251. Various NTSTATUS codes.
  252. Environment:
  253. Kernel mode, PASSIVE_LEVEL.
  254. --*/
  255. {
  256. ULONG PteOffset;
  257. NTSTATUS Status;
  258. PMMPTE ProtoPte;
  259. PMMPTE LastProto;
  260. PMMPTE *ProtoPteArray;
  261. PCONTROL_AREA ControlArea;
  262. PSUBSECTION Subsection;
  263. PMMINPAGE_SUPPORT InPageSupport;
  264. PMDL Mdl;
  265. PMDL IoMdl;
  266. PMDL ApiMdl;
  267. ULONG i;
  268. PFN_NUMBER NumberOfPages;
  269. ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
  270. NumberOfPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES (MiReadInfo->FileOffset.LowPart, MiReadInfo->LengthInBytes);
  271. //
  272. // Translate the section object into the relevant control area.
  273. //
  274. ControlArea = (PCONTROL_AREA)MiReadInfo->FileObject->SectionObjectPointer->DataSectionObject;
  275. //
  276. // Initialize the internal Mi readlist.
  277. //
  278. MiReadInfo->ControlArea = ControlArea;
  279. //
  280. // Allocate and initialize an inpage support block for this run.
  281. //
  282. InPageSupport = MiGetInPageSupportBlock (FALSE, PREFETCH_PROCESS);
  283. if (InPageSupport == NULL) {
  284. return STATUS_INSUFFICIENT_RESOURCES;
  285. }
  286. MiReadInfo->InPageSupport = InPageSupport;
  287. //
  288. // Allocate and initialize an MDL to return to our caller. The actual
  289. // frame numbers are filled in when all the pages are reference counted.
  290. //
  291. ApiMdl = MmCreateMdl (NULL, NULL, NumberOfPages << PAGE_SHIFT);
  292. if (ApiMdl == NULL) {
  293. return STATUS_INSUFFICIENT_RESOURCES;
  294. }
  295. ApiMdl->MdlFlags |= MDL_PAGES_LOCKED;
  296. MiReadInfo->ApiMdl = ApiMdl;
  297. //
  298. // Allocate and initialize an MDL to use for the actual transfer (if any).
  299. //
  300. IoMdl = MmCreateMdl (NULL, NULL, NumberOfPages << PAGE_SHIFT);
  301. if (IoMdl == NULL) {
  302. return STATUS_INSUFFICIENT_RESOURCES;
  303. }
  304. MiReadInfo->IoMdl = IoMdl;
  305. Mdl = IoMdl;
  306. //
  307. // If the section is backed by a ROM, then there's no need to prefetch
  308. // anything as it would waste RAM.
  309. //
  310. if (ControlArea->u.Flags.Rom == 1) {
  311. ASSERT (XIPConfigured == TRUE);
  312. #if 0
  313. //
  314. // Calculate the offset to read into the file.
  315. // offset = base + ((thispte - basepte) << PAGE_SHIFT)
  316. //
  317. StartingOffset.QuadPart = MiReadInfo.FileOffset;
  318. TempOffset = MiEndingOffset(Subsection);
  319. ASSERT (StartingOffset.QuadPart < TempOffset.QuadPart);
  320. PageFrameIndex = (PFN_NUMBER) (StartingOffset.QuadPart >> PAGE_SHIFT);
  321. PageFrameIndex += ((PLARGE_CONTROL_AREA)ControlArea)->StartingFrame;
  322. //
  323. // Increment the PFN reference count in the control area for
  324. // the subsection (the PFN lock is required to modify this field).
  325. //
  326. ControlArea->NumberOfPfnReferences += 1;
  327. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  328. ASSERT (Pfn1->u3.e1.Rom == 1);
  329. if (Pfn1->u3.e1.PageLocation != 0) {
  330. ASSERT (Pfn1->u3.e1.PageLocation == StandbyPageList);
  331. MiUnlinkPageFromList (Pfn1);
  332. //
  333. // Update the PFN database - the reference count must be
  334. // incremented as the share count is going to go from zero to 1.
  335. //
  336. ASSERT (Pfn1->u2.ShareCount == 0);
  337. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  338. Pfn1->u3.e2.ReferenceCount += 1;
  339. Pfn1->u2.ShareCount += 1;
  340. Pfn1->u3.e1.PageLocation = ActiveAndValid;
  341. ASSERT (Pfn1->PteAddress == PointerPte);
  342. ASSERT (Pfn1->u1.Event == NULL);
  343. //
  344. // Determine the page frame number of the page table page which
  345. // contains this PTE.
  346. //
  347. PteFramePointer = MiGetPteAddress (PointerPte);
  348. if (PteFramePointer->u.Hard.Valid == 0) {
  349. #if (_MI_PAGING_LEVELS < 3)
  350. if (!NT_SUCCESS(MiCheckPdeForPagedPool (PointerPte))) {
  351. #endif
  352. KeBugCheckEx (MEMORY_MANAGEMENT,
  353. 0x61940,
  354. (ULONG_PTR)PointerPte,
  355. (ULONG_PTR)PteFramePointer->u.Long,
  356. 0);
  357. #if (_MI_PAGING_LEVELS < 3)
  358. }
  359. #endif
  360. }
  361. PteFramePage = MI_GET_PAGE_FRAME_FROM_PTE (PteFramePointer);
  362. ASSERT (Pfn1->PteFrame == PteFramePage);
  363. //
  364. // Increment the share count for the page table page containing
  365. // this PTE as the PTE is going to be made valid.
  366. //
  367. ASSERT (PteFramePage != 0);
  368. Pfn2 = MI_PFN_ELEMENT (PteFramePage);
  369. Pfn2->u2.ShareCount += 1;
  370. }
  371. else {
  372. ASSERT (Pfn1->u3.e1.InPageError == 0);
  373. ASSERT (Pfn1->u3.e1.PrototypePte == 1);
  374. ASSERT (Pfn1->u1.Event == NULL);
  375. MiInitializePfn (PageFrameIndex, PointerPte, 0);
  376. }
  377. //
  378. // Put the prototype PTE into the valid state.
  379. // LWFIX: don't make proto valid
  380. //
  381. MI_MAKE_VALID_PTE (TempPte,
  382. PageFrameIndex,
  383. PointerPte->u.Soft.Protection,
  384. PointerPte);
  385. MI_WRITE_VALID_PTE (PointerPte, TempPte);
  386. #endif
  387. // LWFIX: not success, but what ? and who frees pool
  388. return STATUS_SUCCESS;
  389. }
  390. //
  391. // Make sure the section is really prefetchable - physical and
  392. // pagefile-backed sections are not.
  393. //
  394. if ((ControlArea->u.Flags.PhysicalMemory) ||
  395. (ControlArea->u.Flags.Image == 1) ||
  396. (ControlArea->FilePointer == NULL)) {
  397. return STATUS_INVALID_PARAMETER_1;
  398. }
  399. //
  400. // Start the read at the proper file offset.
  401. //
  402. InPageSupport->ReadOffset = MiReadInfo->FileOffset;
  403. ASSERT (BYTE_OFFSET (InPageSupport->ReadOffset.LowPart) == 0);
  404. InPageSupport->FilePointer = MiReadInfo->FileObject;
  405. //
  406. // Stash a pointer to the start of the prototype PTE array (the values
  407. // in the array are not contiguous as they may cross subsections)
  408. // in the inpage block so we can walk it quickly later when the pages
  409. // are put into transition.
  410. //
  411. ProtoPteArray = (PMMPTE *)(Mdl + 1);
  412. InPageSupport->BasePte = (PMMPTE) ProtoPteArray;
  413. //
  414. // Data (but not image) reads use the whole page and the filesystems
  415. // zero fill any remainder beyond valid data length so we don't
  416. // bother to handle this here. It is important to specify the
  417. // entire page where possible so the filesystem won't post this
  418. // which will hurt perf. LWFIX: must use CcZero to make this true.
  419. //
  420. ASSERT (((ULONG_PTR)Mdl & (sizeof(QUAD) - 1)) == 0);
  421. InPageSupport->u1.e1.PrefetchMdlHighBits = ((ULONG_PTR)Mdl >> 3);
  422. //
  423. // Initialize the prototype PTE pointers.
  424. //
  425. ASSERT (ControlArea->u.Flags.GlobalOnlyPerSession == 0);
  426. if (ControlArea->u.Flags.Rom == 0) {
  427. Subsection = (PSUBSECTION)(ControlArea + 1);
  428. }
  429. else {
  430. Subsection = (PSUBSECTION)((PLARGE_CONTROL_AREA)ControlArea + 1);
  431. }
  432. #if DBG
  433. if (MiCcDebug & MI_CC_FORCE_PREFETCH) {
  434. MiRemoveUserPages ();
  435. }
  436. #endif
  437. //
  438. // Calculate the first prototype PTE address.
  439. //
  440. PteOffset = (ULONG)(MiReadInfo->FileOffset.QuadPart >> PAGE_SHIFT);
  441. //
  442. // Make sure the PTEs are not in the extended part of the segment.
  443. //
  444. while (PteOffset >= Subsection->PtesInSubsection) {
  445. PteOffset -= Subsection->PtesInSubsection;
  446. Subsection = Subsection->NextSubsection;
  447. }
  448. Status = MiAddViewsForSectionWithPfn ((PMSUBSECTION) Subsection,
  449. Subsection->PtesInSubsection);
  450. if (!NT_SUCCESS (Status)) {
  451. return Status;
  452. }
  453. MiReadInfo->FirstReferencedSubsection = Subsection;
  454. MiReadInfo->LastReferencedSubsection = Subsection;
  455. ProtoPte = &Subsection->SubsectionBase[PteOffset];
  456. LastProto = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
  457. for (i = 0; i < NumberOfPages; i += 1) {
  458. //
  459. // Calculate which PTE maps the given logical block offset.
  460. //
  461. // Always look forwards (as an optimization) in the subsection chain.
  462. //
  463. // A quick check is made first to avoid recalculations and loops where
  464. // possible.
  465. //
  466. if (ProtoPte >= LastProto) {
  467. //
  468. // Handle extended subsections. Increment the view count for
  469. // every subsection spanned by this request, creating prototype
  470. // PTEs if needed.
  471. //
  472. ASSERT (i != 0);
  473. Subsection = Subsection->NextSubsection;
  474. Status = MiAddViewsForSectionWithPfn ((PMSUBSECTION) Subsection,
  475. Subsection->PtesInSubsection);
  476. if (!NT_SUCCESS (Status)) {
  477. return Status;
  478. }
  479. MiReadInfo->LastReferencedSubsection = Subsection;
  480. ProtoPte = Subsection->SubsectionBase;
  481. LastProto = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
  482. }
  483. *ProtoPteArray = ProtoPte;
  484. ProtoPteArray += 1;
  485. ProtoPte += 1;
  486. }
  487. return STATUS_SUCCESS;
  488. }
  489. NTSTATUS
  490. MiCcPutPagesInTransition (
  491. IN PMI_READ_INFO MiReadInfo
  492. )
  493. /*++
  494. Routine Description:
  495. This routine allocates physical memory for the specified read-list and
  496. puts all the pages in transition (so collided faults from other threads
  497. for these same pages remain coherent). I/O for any pages not already
  498. resident are issued here. The caller must wait for their completion.
  499. Arguments:
  500. MiReadInfo - Supplies a pointer to the read-list.
  501. Return Value:
  502. STATUS_SUCCESS - all the pages were already resident, reference counts
  503. have been applied and no I/O needs to be waited for.
  504. STATUS_ISSUE_PAGING_IO - the I/O has been issued and the caller must wait.
  505. Various other failure status values indicate the operation failed.
  506. Environment:
  507. Kernel mode. PASSIVE_LEVEL.
  508. --*/
  509. {
  510. NTSTATUS status;
  511. PMMPTE LocalPrototypePte;
  512. PVOID StartingVa;
  513. PFN_NUMBER MdlPages;
  514. KIRQL OldIrql;
  515. MMPTE PteContents;
  516. PFN_NUMBER PageFrameIndex;
  517. PFN_NUMBER ResidentAvailableCharge;
  518. PPFN_NUMBER IoPage;
  519. PPFN_NUMBER ApiPage;
  520. PPFN_NUMBER Page;
  521. PPFN_NUMBER DestinationPage;
  522. ULONG PageColor;
  523. PMMPTE PointerPte;
  524. PMMPTE *ProtoPteArray;
  525. PMMPTE *EndProtoPteArray;
  526. PFN_NUMBER DummyPage;
  527. PMDL Mdl;
  528. PMDL FreeMdl;
  529. PMMPFN PfnProto;
  530. PMMPFN Pfn1;
  531. PMMPFN DummyPfn1;
  532. ULONG i;
  533. PFN_NUMBER DummyTrim;
  534. ULONG NumberOfPagesNeedingIo;
  535. MMPTE TempPte;
  536. PMMPTE PointerPde;
  537. PEPROCESS CurrentProcess;
  538. PMMINPAGE_SUPPORT InPageSupport;
  539. ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
  540. MiReadInfo->DummyPagePfn = NULL;
  541. FreeMdl = NULL;
  542. CurrentProcess = PsGetCurrentProcess();
  543. PfnProto = NULL;
  544. PointerPde = NULL;
  545. InPageSupport = MiReadInfo->InPageSupport;
  546. Mdl = MI_EXTRACT_PREFETCH_MDL (InPageSupport);
  547. ASSERT (Mdl == MiReadInfo->IoMdl);
  548. IoPage = (PPFN_NUMBER)(Mdl + 1);
  549. ApiPage = (PPFN_NUMBER)(MiReadInfo->ApiMdl + 1);
  550. StartingVa = (PVOID)((PCHAR)Mdl->StartVa + Mdl->ByteOffset);
  551. MdlPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES (StartingVa,
  552. Mdl->ByteCount);
  553. if (MdlPages + 1 > MAXUSHORT) {
  554. //
  555. // The PFN ReferenceCount for the dummy page could wrap, refuse the
  556. // request.
  557. //
  558. return STATUS_INSUFFICIENT_RESOURCES;
  559. }
  560. NumberOfPagesNeedingIo = 0;
  561. ProtoPteArray = (PMMPTE *)InPageSupport->BasePte;
  562. EndProtoPteArray = ProtoPteArray + MdlPages;
  563. ASSERT (*ProtoPteArray != NULL);
  564. LOCK_PFN (OldIrql);
  565. //
  566. // Ensure sufficient pages exist for the transfer plus the dummy page.
  567. //
  568. if ((MmAvailablePages <= MdlPages) ||
  569. (MI_NONPAGABLE_MEMORY_AVAILABLE() <= (SPFN_NUMBER)MdlPages)) {
  570. UNLOCK_PFN (OldIrql);
  571. return STATUS_INSUFFICIENT_RESOURCES;
  572. }
  573. //
  574. // Charge resident available immediately as the PFN lock may get released
  575. // and reacquired below before all the pages have been locked down.
  576. // Note the dummy page is immediately charged separately.
  577. //
  578. MmResidentAvailablePages -= MdlPages;
  579. MM_BUMP_COUNTER(46, MdlPages);
  580. ResidentAvailableCharge = MdlPages;
  581. //
  582. // Allocate a dummy page to map discarded pages that aren't skipped.
  583. //
  584. DummyPage = MiRemoveAnyPage (0);
  585. Pfn1 = MI_PFN_ELEMENT (DummyPage);
  586. ASSERT (Pfn1->u2.ShareCount == 0);
  587. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  588. MiInitializePfnForOtherProcess (DummyPage, MI_PF_DUMMY_PAGE_PTE, 0);
  589. //
  590. // Always bias the reference count by 1 and charge for this locked page
  591. // up front so the myriad increments and decrements don't get slowed
  592. // down with needless checking.
  593. //
  594. MI_ADD_LOCKED_PAGE_CHARGE(Pfn1, 42);
  595. Pfn1->u3.e2.ReferenceCount += 1;
  596. Pfn1->u3.e1.ReadInProgress = 1;
  597. Pfn1->u3.e1.PrototypePte = 0;
  598. MiReadInfo->DummyPagePfn = Pfn1;
  599. DummyPfn1 = Pfn1;
  600. DummyPfn1->u3.e2.ReferenceCount =
  601. (USHORT)(DummyPfn1->u3.e2.ReferenceCount + MdlPages);
  602. //
  603. // Properly initialize the inpage support block fields we overloaded.
  604. //
  605. InPageSupport->BasePte = *ProtoPteArray;
  606. //
  607. // Build the proper InPageSupport and MDL to describe this run.
  608. //
  609. for (; ProtoPteArray < EndProtoPteArray; ProtoPteArray += 1, IoPage += 1, ApiPage += 1) {
  610. //
  611. // Fill the MDL entry for this RLE.
  612. //
  613. PointerPte = *ProtoPteArray;
  614. ASSERT (PointerPte != NULL);
  615. //
  616. // The PointerPte better be inside a prototype PTE allocation
  617. // so that subsequent page trims update the correct PTEs.
  618. //
  619. ASSERT (((PointerPte >= (PMMPTE)MmPagedPoolStart) &&
  620. (PointerPte <= (PMMPTE)MmPagedPoolEnd)) ||
  621. ((PointerPte >= (PMMPTE)MmSpecialPoolStart) && (PointerPte <= (PMMPTE)MmSpecialPoolEnd)));
  622. //
  623. // Check the state of this prototype PTE now that the PFN lock is held.
  624. // If the page is not resident, the PTE must be put in transition with
  625. // read in progress before the PFN lock is released.
  626. //
  627. //
  628. // Lock page containing prototype PTEs in memory by
  629. // incrementing the reference count for the page.
  630. // Unlock any page locked earlier containing prototype PTEs if
  631. // the containing page is not the same for both.
  632. //
  633. if (PfnProto != NULL) {
  634. if (PointerPde != MiGetPteAddress (PointerPte)) {
  635. ASSERT (PfnProto->u3.e2.ReferenceCount > 1);
  636. MI_REMOVE_LOCKED_PAGE_CHARGE_AND_DECREF(PfnProto, 43);
  637. PfnProto = NULL;
  638. }
  639. }
  640. if (PfnProto == NULL) {
  641. ASSERT (!MI_IS_PHYSICAL_ADDRESS (PointerPte));
  642. PointerPde = MiGetPteAddress (PointerPte);
  643. if (PointerPde->u.Hard.Valid == 0) {
  644. MiMakeSystemAddressValidPfn (PointerPte);
  645. }
  646. PfnProto = MI_PFN_ELEMENT (PointerPde->u.Hard.PageFrameNumber);
  647. MI_ADD_LOCKED_PAGE_CHARGE(PfnProto, 44);
  648. PfnProto->u3.e2.ReferenceCount += 1;
  649. ASSERT (PfnProto->u3.e2.ReferenceCount > 1);
  650. }
  651. recheck:
  652. PteContents = *PointerPte;
  653. // LWFIX: are zero or dzero ptes possible here ?
  654. ASSERT (PteContents.u.Long != ZeroKernelPte.u.Long);
  655. if (PteContents.u.Hard.Valid == 1) {
  656. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&PteContents);
  657. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  658. ASSERT (Pfn1->u3.e1.PrototypePte == 1);
  659. MI_ADD_LOCKED_PAGE_CHARGE(Pfn1, 45);
  660. Pfn1->u3.e2.ReferenceCount += 1;
  661. *ApiPage = PageFrameIndex;
  662. *IoPage = DummyPage;
  663. continue;
  664. }
  665. if ((PteContents.u.Soft.Prototype == 0) &&
  666. (PteContents.u.Soft.Transition == 1)) {
  667. //
  668. // The page is in transition. If there is an inpage still in
  669. // progress, wait for it to complete. Reference the PFN and
  670. // then march on.
  671. //
  672. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents);
  673. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  674. ASSERT (Pfn1->u3.e1.PrototypePte == 1);
  675. if (Pfn1->u4.InPageError) {
  676. //
  677. // There was an in-page read error and there are other
  678. // threads colliding for this page, delay to let the
  679. // other threads complete and then retry.
  680. //
  681. UNLOCK_PFN (OldIrql);
  682. KeDelayExecutionThread (KernelMode, FALSE, (PLARGE_INTEGER)&MmHalfSecond);
  683. LOCK_PFN (OldIrql);
  684. goto recheck;
  685. }
  686. if (Pfn1->u3.e1.ReadInProgress) {
  687. // LWFIX - start with temp\aw.c
  688. }
  689. //
  690. // PTE refers to a normal transition PTE.
  691. //
  692. ASSERT ((SPFN_NUMBER)MmAvailablePages >= 0);
  693. if (MmAvailablePages == 0) {
  694. //
  695. // This can only happen if the system is utilizing a hardware
  696. // compression cache. This ensures that only a safe amount
  697. // of the compressed virtual cache is directly mapped so that
  698. // if the hardware gets into trouble, we can bail it out.
  699. //
  700. UNLOCK_PFN (OldIrql);
  701. KeDelayExecutionThread (KernelMode, FALSE, (PLARGE_INTEGER)&MmHalfSecond);
  702. LOCK_PFN (OldIrql);
  703. goto recheck;
  704. }
  705. //
  706. // The PFN reference count will be 1 already here if the
  707. // modified writer has begun a write of this page. Otherwise
  708. // it's ordinarily 0.
  709. //
  710. MI_ADD_LOCKED_PAGE_CHARGE_FOR_MODIFIED_PAGE (Pfn1, 46);
  711. Pfn1->u3.e2.ReferenceCount += 1;
  712. *IoPage = DummyPage;
  713. *ApiPage = PageFrameIndex;
  714. continue;
  715. }
  716. ASSERT (PteContents.u.Soft.Prototype == 1);
  717. if (MiEnsureAvailablePageOrWait (NULL, NULL)) {
  718. //
  719. // Had to wait so recheck all state.
  720. //
  721. goto recheck;
  722. }
  723. NumberOfPagesNeedingIo += 1;
  724. //
  725. // Allocate a physical page.
  726. //
  727. PageColor = MI_PAGE_COLOR_VA_PROCESS (
  728. MiGetVirtualAddressMappedByPte (PointerPte),
  729. &CurrentProcess->NextPageColor);
  730. PageFrameIndex = MiRemoveAnyPage (PageColor);
  731. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  732. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  733. ASSERT (Pfn1->u2.ShareCount == 0);
  734. ASSERT (PointerPte->u.Hard.Valid == 0);
  735. //
  736. // Initialize read-in-progress PFN.
  737. //
  738. MiInitializePfn (PageFrameIndex, PointerPte, 0);
  739. //
  740. // These pieces of MiInitializePfn initialization are overridden
  741. // here as these pages are only going into prototype
  742. // transition and not into any page tables.
  743. //
  744. MI_ADD_LOCKED_PAGE_CHARGE(Pfn1, 47);
  745. Pfn1->u2.ShareCount -= 1;
  746. Pfn1->u3.e1.PageLocation = ZeroedPageList;
  747. //
  748. // Initialize the I/O specific fields.
  749. //
  750. Pfn1->u1.Event = &InPageSupport->Event;
  751. Pfn1->u3.e1.ReadInProgress = 1;
  752. ASSERT (Pfn1->u4.InPageError == 0);
  753. Pfn1->u3.e1.PrototypePte = 1;
  754. //
  755. // Increment the PFN reference count in the control area for
  756. // the subsection.
  757. //
  758. MiReadInfo->ControlArea->NumberOfPfnReferences += 1;
  759. //
  760. // Put the prototype PTE into the transition state.
  761. //
  762. MI_MAKE_TRANSITION_PTE (TempPte,
  763. PageFrameIndex,
  764. PointerPte->u.Soft.Protection,
  765. PointerPte);
  766. MI_WRITE_INVALID_PTE (PointerPte, TempPte);
  767. *IoPage = PageFrameIndex;
  768. *ApiPage = PageFrameIndex;
  769. }
  770. //
  771. // Return the upfront resident available charge as the individual charges
  772. // have all been made at this point.
  773. //
  774. MmResidentAvailablePages += ResidentAvailableCharge;
  775. MM_BUMP_COUNTER(47, MdlPages);
  776. //
  777. // If all the pages were resident, dereference the dummy page references
  778. // now and notify our caller that I/O is not necessary.
  779. //
  780. if (NumberOfPagesNeedingIo == 0) {
  781. ASSERT (DummyPfn1->u3.e2.ReferenceCount > MdlPages);
  782. DummyPfn1->u3.e2.ReferenceCount =
  783. (USHORT)(DummyPfn1->u3.e2.ReferenceCount - MdlPages);
  784. //
  785. // Unlock page containing prototype PTEs.
  786. //
  787. if (PfnProto != NULL) {
  788. ASSERT (PfnProto->u3.e2.ReferenceCount > 1);
  789. MI_REMOVE_LOCKED_PAGE_CHARGE_AND_DECREF(PfnProto, 48);
  790. }
  791. UNLOCK_PFN (OldIrql);
  792. return STATUS_SUCCESS;
  793. }
  794. //
  795. // Carefully trim leading dummy pages.
  796. //
  797. Page = (PPFN_NUMBER)(Mdl + 1);
  798. DummyTrim = 0;
  799. for (i = 0; i < MdlPages - 1; i += 1) {
  800. if (*Page == DummyPage) {
  801. DummyTrim += 1;
  802. Page += 1;
  803. }
  804. else {
  805. break;
  806. }
  807. }
  808. if (DummyTrim != 0) {
  809. Mdl->Size = (USHORT)(Mdl->Size - (DummyTrim * sizeof(PFN_NUMBER)));
  810. Mdl->ByteCount -= (ULONG)(DummyTrim * PAGE_SIZE);
  811. ASSERT (Mdl->ByteCount != 0);
  812. InPageSupport->ReadOffset.QuadPart += (DummyTrim * PAGE_SIZE);
  813. DummyPfn1->u3.e2.ReferenceCount =
  814. (USHORT)(DummyPfn1->u3.e2.ReferenceCount - DummyTrim);
  815. //
  816. // Shuffle down the PFNs in the MDL.
  817. // Recalculate BasePte to adjust for the shuffle.
  818. //
  819. Pfn1 = MI_PFN_ELEMENT (*Page);
  820. ASSERT (Pfn1->PteAddress->u.Hard.Valid == 0);
  821. ASSERT ((Pfn1->PteAddress->u.Soft.Prototype == 0) &&
  822. (Pfn1->PteAddress->u.Soft.Transition == 1));
  823. InPageSupport->BasePte = Pfn1->PteAddress;
  824. DestinationPage = (PPFN_NUMBER)(Mdl + 1);
  825. do {
  826. *DestinationPage = *Page;
  827. DestinationPage += 1;
  828. Page += 1;
  829. i += 1;
  830. } while (i < MdlPages);
  831. MdlPages -= DummyTrim;
  832. }
  833. //
  834. // Carefully trim trailing dummy pages.
  835. //
  836. ASSERT (MdlPages != 0);
  837. Page = (PPFN_NUMBER)(Mdl + 1) + MdlPages - 1;
  838. if (*Page == DummyPage) {
  839. ASSERT (MdlPages >= 2);
  840. //
  841. // Trim the last page specially as it may be a partial page.
  842. //
  843. Mdl->Size -= sizeof(PFN_NUMBER);
  844. if (BYTE_OFFSET(Mdl->ByteCount) != 0) {
  845. Mdl->ByteCount &= ~(PAGE_SIZE - 1);
  846. }
  847. else {
  848. Mdl->ByteCount -= PAGE_SIZE;
  849. }
  850. ASSERT (Mdl->ByteCount != 0);
  851. DummyPfn1->u3.e2.ReferenceCount -= 1;
  852. //
  853. // Now trim any other trailing pages.
  854. //
  855. Page -= 1;
  856. DummyTrim = 0;
  857. while (Page != ((PPFN_NUMBER)(Mdl + 1))) {
  858. if (*Page != DummyPage) {
  859. break;
  860. }
  861. DummyTrim += 1;
  862. Page -= 1;
  863. }
  864. if (DummyTrim != 0) {
  865. ASSERT (Mdl->Size > (USHORT)(DummyTrim * sizeof(PFN_NUMBER)));
  866. Mdl->Size = (USHORT)(Mdl->Size - (DummyTrim * sizeof(PFN_NUMBER)));
  867. Mdl->ByteCount -= (ULONG)(DummyTrim * PAGE_SIZE);
  868. DummyPfn1->u3.e2.ReferenceCount =
  869. (USHORT)(DummyPfn1->u3.e2.ReferenceCount - DummyTrim);
  870. }
  871. ASSERT (MdlPages > DummyTrim + 1);
  872. MdlPages -= (DummyTrim + 1);
  873. #if DBG
  874. StartingVa = (PVOID)((PCHAR)Mdl->StartVa + Mdl->ByteOffset);
  875. ASSERT (MdlPages == ADDRESS_AND_SIZE_TO_SPAN_PAGES(StartingVa,
  876. Mdl->ByteCount));
  877. #endif
  878. }
  879. //
  880. // If the MDL is not already embedded in the inpage block, see if its
  881. // final size qualifies it - if so, embed it now.
  882. //
  883. if ((Mdl != &InPageSupport->Mdl) &&
  884. (Mdl->ByteCount <= (MM_MAXIMUM_READ_CLUSTER_SIZE + 1) * PAGE_SIZE)){
  885. #if DBG
  886. RtlFillMemoryUlong (&InPageSupport->Page[0],
  887. (MM_MAXIMUM_READ_CLUSTER_SIZE+1) * sizeof (PFN_NUMBER),
  888. 0xf1f1f1f1);
  889. #endif
  890. RtlCopyMemory (&InPageSupport->Mdl, Mdl, Mdl->Size);
  891. FreeMdl = Mdl;
  892. Mdl = &InPageSupport->Mdl;
  893. ASSERT (((ULONG_PTR)Mdl & (sizeof(QUAD) - 1)) == 0);
  894. InPageSupport->u1.e1.PrefetchMdlHighBits = ((ULONG_PTR)Mdl >> 3);
  895. }
  896. ASSERT (MdlPages != 0);
  897. ASSERT (Mdl->Size - sizeof(MDL) == BYTES_TO_PAGES(Mdl->ByteCount) * sizeof(PFN_NUMBER));
  898. DummyPfn1->u3.e2.ReferenceCount =
  899. (USHORT)(DummyPfn1->u3.e2.ReferenceCount - NumberOfPagesNeedingIo);
  900. MmInfoCounters.PageReadIoCount += 1;
  901. MmInfoCounters.PageReadCount += NumberOfPagesNeedingIo;
  902. //
  903. // Unlock page containing prototype PTEs.
  904. //
  905. if (PfnProto != NULL) {
  906. ASSERT (PfnProto->u3.e2.ReferenceCount > 1);
  907. MI_REMOVE_LOCKED_PAGE_CHARGE_AND_DECREF(PfnProto, 49);
  908. }
  909. UNLOCK_PFN (OldIrql);
  910. if (FreeMdl != NULL) {
  911. ASSERT (MiReadInfo->IoMdl == FreeMdl);
  912. MiReadInfo->IoMdl = NULL;
  913. ExFreePool (FreeMdl);
  914. }
  915. #if DBG
  916. if (MiCcDebug & MI_CC_DELAY) {
  917. //
  918. // This delay provides a window to increase the chance of collided
  919. // faults.
  920. //
  921. KeDelayExecutionThread (KernelMode, FALSE, (PLARGE_INTEGER)&MmHalfSecond);
  922. }
  923. #endif
  924. //
  925. // Finish initialization of the prefetch MDL (and the API MDL).
  926. //
  927. ASSERT ((Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) == 0);
  928. Mdl->MdlFlags |= (MDL_PAGES_LOCKED | MDL_IO_PAGE_READ);
  929. ASSERT (InPageSupport->u1.e1.Completed == 0);
  930. ASSERT (InPageSupport->Thread == PsGetCurrentThread());
  931. ASSERT64 (InPageSupport->UsedPageTableEntries == 0);
  932. ASSERT (InPageSupport->WaitCount >= 1);
  933. ASSERT (InPageSupport->u1.e1.PrefetchMdlHighBits != 0);
  934. //
  935. // The API caller expects an MDL containing all the locked pages so
  936. // it can be used for a transfer.
  937. //
  938. // Note that an extra reference count is not taken on each page -
  939. // rather when the Io MDL completes, its reference counts are not
  940. // decremented (except for the dummy page). This combined with the
  941. // reference count already taken on the resident pages keeps the
  942. // accounting correct. Only if an error occurs will the Io MDL
  943. // completion decrement the reference counts.
  944. //
  945. //
  946. // Initialize the inpage support block Pfn field.
  947. //
  948. LocalPrototypePte = InPageSupport->BasePte;
  949. ASSERT (LocalPrototypePte->u.Hard.Valid == 0);
  950. ASSERT ((LocalPrototypePte->u.Soft.Prototype == 0) &&
  951. (LocalPrototypePte->u.Soft.Transition == 1));
  952. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE(LocalPrototypePte);
  953. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  954. InPageSupport->Pfn = Pfn1;
  955. //
  956. // Issue the paging I/O.
  957. //
  958. ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
  959. status = IoAsynchronousPageRead (InPageSupport->FilePointer,
  960. Mdl,
  961. &InPageSupport->ReadOffset,
  962. &InPageSupport->Event,
  963. &InPageSupport->IoStatus);
  964. if (!NT_SUCCESS (status)) {
  965. //
  966. // Set the event as the I/O system doesn't set it on errors.
  967. // This way our caller will automatically unroll the PFN reference
  968. // counts, etc, when the MiWaitForInPageComplete returns this status.
  969. //
  970. InPageSupport->IoStatus.Status = status;
  971. InPageSupport->IoStatus.Information = 0;
  972. KeSetEvent (&InPageSupport->Event, 0, FALSE);
  973. }
  974. #if DBG
  975. if (MiCcDebug & MI_CC_DELAY) {
  976. //
  977. // This delay provides a window to increase the chance of collided
  978. // faults.
  979. //
  980. KeDelayExecutionThread (KernelMode, FALSE, (PLARGE_INTEGER)&MmHalfSecond);
  981. }
  982. #endif
  983. return STATUS_ISSUE_PAGING_IO;
  984. }
  985. NTSTATUS
  986. MiCcCompletePrefetchIos (
  987. IN PMI_READ_INFO MiReadInfo
  988. )
  989. /*++
  990. Routine Description:
  991. This routine waits for a series of page reads to complete
  992. and completes the requests.
  993. Arguments:
  994. MiReadInfo - Pointer to the read-list.
  995. Return Value:
  996. NTSTATUS of the I/O request.
  997. Environment:
  998. Kernel mode, PASSIVE_LEVEL.
  999. --*/
  1000. {
  1001. PMDL Mdl;
  1002. PMMPFN Pfn1;
  1003. PMMPFN PfnClusterPage;
  1004. PPFN_NUMBER Page;
  1005. NTSTATUS status;
  1006. LONG NumberOfBytes;
  1007. PMMINPAGE_SUPPORT InPageSupport;
  1008. ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
  1009. InPageSupport = MiReadInfo->InPageSupport;
  1010. ASSERT (InPageSupport->Pfn != 0);
  1011. Pfn1 = InPageSupport->Pfn;
  1012. Mdl = MI_EXTRACT_PREFETCH_MDL (InPageSupport);
  1013. Page = (PPFN_NUMBER)(Mdl + 1);
  1014. status = MiWaitForInPageComplete (InPageSupport->Pfn,
  1015. InPageSupport->BasePte,
  1016. NULL,
  1017. InPageSupport->BasePte,
  1018. InPageSupport,
  1019. PREFETCH_PROCESS);
  1020. //
  1021. // MiWaitForInPageComplete RETURNS WITH THE PFN LOCK HELD!!!
  1022. //
  1023. NumberOfBytes = (LONG)Mdl->ByteCount;
  1024. while (NumberOfBytes > 0) {
  1025. //
  1026. // Only decrement reference counts if an error occurred.
  1027. //
  1028. PfnClusterPage = MI_PFN_ELEMENT (*Page);
  1029. #if DBG
  1030. if (PfnClusterPage->u4.InPageError) {
  1031. //
  1032. // If the page is marked with an error, then the whole transfer
  1033. // must be marked as not successful as well. The only exception
  1034. // is the prefetch dummy page which is used in multiple
  1035. // transfers concurrently and thus may have the inpage error
  1036. // bit set at any time (due to another transaction besides
  1037. // the current one).
  1038. //
  1039. ASSERT ((status != STATUS_SUCCESS) ||
  1040. (PfnClusterPage->PteAddress == MI_PF_DUMMY_PAGE_PTE));
  1041. }
  1042. #endif
  1043. if (PfnClusterPage->u3.e1.ReadInProgress != 0) {
  1044. ASSERT (PfnClusterPage->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  1045. PfnClusterPage->u3.e1.ReadInProgress = 0;
  1046. if (PfnClusterPage->u4.InPageError == 0) {
  1047. PfnClusterPage->u1.Event = (PKEVENT)NULL;
  1048. }
  1049. }
  1050. //
  1051. // Note the reference count for each page is NOT decremented unless
  1052. // the I/O failed, in which case it is done below. This allows the
  1053. // MmPrefetchPagesIntoLockedMdl API to return a locked page MDL.
  1054. //
  1055. Page += 1;
  1056. NumberOfBytes -= PAGE_SIZE;
  1057. }
  1058. if (status != STATUS_SUCCESS) {
  1059. //
  1060. // An I/O error occurred during the page read
  1061. // operation. All the pages which were just
  1062. // put into transition must be put onto the
  1063. // free list if InPageError is set, and their
  1064. // PTEs restored to the proper contents.
  1065. //
  1066. Page = (PPFN_NUMBER)(Mdl + 1);
  1067. NumberOfBytes = (LONG)Mdl->ByteCount;
  1068. while (NumberOfBytes > 0) {
  1069. PfnClusterPage = MI_PFN_ELEMENT (*Page);
  1070. MI_REMOVE_LOCKED_PAGE_CHARGE_AND_DECREF(PfnClusterPage, 50);
  1071. if (PfnClusterPage->u4.InPageError == 1) {
  1072. if (PfnClusterPage->u3.e2.ReferenceCount == 0) {
  1073. ASSERT (PfnClusterPage->u3.e1.PageLocation ==
  1074. StandbyPageList);
  1075. MiUnlinkPageFromList (PfnClusterPage);
  1076. MiRestoreTransitionPte (*Page);
  1077. MiInsertPageInFreeList (*Page);
  1078. }
  1079. }
  1080. Page += 1;
  1081. NumberOfBytes -= PAGE_SIZE;
  1082. }
  1083. }
  1084. //
  1085. // All the relevant prototype PTEs should be in the transition or
  1086. // valid states and all page frames should be referenced.
  1087. // LWFIX: add code to checked build to verify this.
  1088. //
  1089. ASSERT (InPageSupport->WaitCount >= 1);
  1090. UNLOCK_PFN (PASSIVE_LEVEL);
  1091. #if DBG
  1092. InPageSupport->ListEntry.Next = NULL;
  1093. #endif
  1094. MiFreeInPageSupportBlock (InPageSupport);
  1095. MiReadInfo->InPageSupport = NULL;
  1096. return status;
  1097. }