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.

4320 lines
132 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. sectsup.c
  5. Abstract:
  6. This module contains the routines which implement the
  7. section object.
  8. Author:
  9. Lou Perazzoli (loup) 22-May-1989
  10. Landy Wang (landyw) 02-June-1997
  11. Revision History:
  12. --*/
  13. #include "mi.h"
  14. VOID
  15. FASTCALL
  16. MiRemoveBasedSection (
  17. IN PSECTION Section
  18. );
  19. #ifdef ALLOC_PRAGMA
  20. #pragma alloc_text(INIT,MiSectionInitialization)
  21. #pragma alloc_text(PAGE,MiRemoveBasedSection)
  22. #pragma alloc_text(PAGE,MmGetFileNameForSection)
  23. #pragma alloc_text(PAGE,MmGetFileNameForAddress)
  24. #pragma alloc_text(PAGE,MiSectionDelete)
  25. #pragma alloc_text(PAGE,MiInsertBasedSection)
  26. #pragma alloc_text(PAGE,MiGetEventCounter)
  27. #pragma alloc_text(PAGE,MiFreeEventCounter)
  28. #pragma alloc_text(PAGE,MmGetFileObjectForSection)
  29. #endif
  30. ULONG MmUnusedSegmentForceFree;
  31. ULONG MiSubsectionsProcessed;
  32. ULONG MiSubsectionActions;
  33. SIZE_T MmSharedCommit = 0;
  34. extern const ULONG MMCONTROL;
  35. extern MMPAGE_FILE_EXPANSION MiPageFileContract;
  36. //
  37. // Define segment dereference thread wait object types.
  38. //
  39. typedef enum _SEGMENT_DEREFERENCE_OBJECT {
  40. SegmentDereference,
  41. UsedSegmentCleanup,
  42. SegMaximumObject
  43. } BALANCE_OBJECT;
  44. extern POBJECT_TYPE IoFileObjectType;
  45. #ifdef ALLOC_DATA_PRAGMA
  46. #pragma const_seg("INITCONST")
  47. #endif
  48. const GENERIC_MAPPING MiSectionMapping = {
  49. STANDARD_RIGHTS_READ |
  50. SECTION_QUERY | SECTION_MAP_READ,
  51. STANDARD_RIGHTS_WRITE |
  52. SECTION_MAP_WRITE,
  53. STANDARD_RIGHTS_EXECUTE |
  54. SECTION_MAP_EXECUTE,
  55. SECTION_ALL_ACCESS
  56. };
  57. #ifdef ALLOC_DATA_PRAGMA
  58. #pragma const_seg()
  59. #endif
  60. VOID
  61. MiRemoveUnusedSegments (
  62. VOID
  63. );
  64. VOID
  65. FASTCALL
  66. MiInsertBasedSection (
  67. IN PSECTION Section
  68. )
  69. /*++
  70. Routine Description:
  71. This function inserts a virtual address descriptor into the tree and
  72. reorders the splay tree as appropriate.
  73. Arguments:
  74. Section - Supplies a pointer to a based section.
  75. Return Value:
  76. None.
  77. Environment:
  78. Must be holding the section based mutex.
  79. --*/
  80. {
  81. ASSERT (Section->Address.EndingVpn >= Section->Address.StartingVpn);
  82. MiInsertNode (&Section->Address, &MmSectionBasedRoot);
  83. return;
  84. }
  85. VOID
  86. FASTCALL
  87. MiRemoveBasedSection (
  88. IN PSECTION Section
  89. )
  90. /*++
  91. Routine Description:
  92. This function removes a based section from the tree.
  93. Arguments:
  94. Section - pointer to the based section object to remove.
  95. Return Value:
  96. None.
  97. Environment:
  98. Must be holding the section based mutex.
  99. --*/
  100. {
  101. MiRemoveNode (&Section->Address, &MmSectionBasedRoot);
  102. return;
  103. }
  104. VOID
  105. MiSegmentDelete (
  106. PSEGMENT Segment
  107. )
  108. /*++
  109. Routine Description:
  110. This routine is called whenever the last reference to a segment object
  111. has been removed. This routine releases the pool allocated for the
  112. prototype PTEs and performs consistency checks on those PTEs.
  113. For segments which map files, the file object is dereferenced.
  114. Note, that for a segment which maps a file, no PTEs may be valid
  115. or transition, while a segment which is backed by a paging file
  116. may have transition pages, but no valid pages (there can be no
  117. PTEs which refer to the segment).
  118. Arguments:
  119. Segment - a pointer to the segment structure.
  120. Return Value:
  121. None.
  122. --*/
  123. {
  124. PMMPTE PointerPte;
  125. PMMPTE LastPte;
  126. PMMPTE PteForProto;
  127. PMMPFN Pfn1;
  128. PMMPFN Pfn2;
  129. KIRQL OldIrql;
  130. PCONTROL_AREA ControlArea;
  131. PEVENT_COUNTER Event;
  132. MMPTE PteContents;
  133. PSUBSECTION Subsection;
  134. PSUBSECTION NextSubsection;
  135. PMSUBSECTION MappedSubsection;
  136. PFN_NUMBER PageTableFrameIndex;
  137. SIZE_T NumberOfCommittedPages;
  138. ControlArea = Segment->ControlArea;
  139. ASSERT (ControlArea->u.Flags.BeingDeleted == 1);
  140. ASSERT (ControlArea->Segment->WritableUserReferences == 0);
  141. LOCK_PFN (OldIrql);
  142. if (ControlArea->DereferenceList.Flink != NULL) {
  143. //
  144. // Remove this from the list of unused segments. The dereference
  145. // segment thread cannot be processing any subsections from this
  146. // control area right now because it bumps the NumberOfMappedViews
  147. // for the control area prior to releasing the PFN lock and it checks
  148. // for BeingDeleted.
  149. //
  150. ExAcquireSpinLockAtDpcLevel (&MmDereferenceSegmentHeader.Lock);
  151. RemoveEntryList (&ControlArea->DereferenceList);
  152. MI_UNUSED_SEGMENTS_REMOVE_CHARGE (ControlArea);
  153. ExReleaseSpinLockFromDpcLevel (&MmDereferenceSegmentHeader.Lock);
  154. }
  155. UNLOCK_PFN (OldIrql);
  156. if ((ControlArea->u.Flags.Image) || (ControlArea->u.Flags.File)) {
  157. //
  158. // Unload kernel debugger symbols if any were loaded.
  159. //
  160. if (ControlArea->u.Flags.DebugSymbolsLoaded != 0) {
  161. //
  162. // TEMP TEMP TEMP rip out when debugger converted
  163. //
  164. ANSI_STRING AnsiName;
  165. NTSTATUS Status;
  166. Status = RtlUnicodeStringToAnsiString( &AnsiName,
  167. (PUNICODE_STRING)&Segment->ControlArea->FilePointer->FileName,
  168. TRUE );
  169. if (NT_SUCCESS( Status)) {
  170. DbgUnLoadImageSymbols( &AnsiName,
  171. Segment->BasedAddress,
  172. (ULONG_PTR)PsGetCurrentProcess());
  173. RtlFreeAnsiString( &AnsiName );
  174. }
  175. LOCK_PFN (OldIrql);
  176. ControlArea->u.Flags.DebugSymbolsLoaded = 0;
  177. }
  178. else {
  179. LOCK_PFN (OldIrql);
  180. }
  181. //
  182. // Signal any threads waiting on the deletion event.
  183. //
  184. Event = ControlArea->WaitingForDeletion;
  185. ControlArea->WaitingForDeletion = NULL;
  186. UNLOCK_PFN (OldIrql);
  187. if (Event != NULL) {
  188. KeSetEvent (&Event->Event, 0, FALSE);
  189. }
  190. //
  191. // Clear the segment context and dereference the file object
  192. // for this Segment.
  193. //
  194. // If the segment was deleted due to a name collision at insertion
  195. // we don't want to dereference the file pointer.
  196. //
  197. if (ControlArea->u.Flags.BeingCreated == FALSE) {
  198. #if DBG
  199. if (ControlArea->u.Flags.Image == 1) {
  200. ASSERT (ControlArea->FilePointer->SectionObjectPointer->ImageSectionObject != (PVOID)ControlArea);
  201. }
  202. else {
  203. ASSERT (ControlArea->FilePointer->SectionObjectPointer->DataSectionObject != (PVOID)ControlArea);
  204. }
  205. #endif
  206. PERFINFO_SEGMENT_DELETE(ControlArea->FilePointer);
  207. ObDereferenceObject (ControlArea->FilePointer);
  208. }
  209. //
  210. // If there have been committed pages in this segment, adjust
  211. // the total commit count.
  212. //
  213. if (ControlArea->u.Flags.Image == 0) {
  214. //
  215. // This is a mapped data file. None of the prototype
  216. // PTEs may be referencing a physical page (valid or transition).
  217. //
  218. if (ControlArea->u.Flags.Rom == 0) {
  219. Subsection = (PSUBSECTION)(ControlArea + 1);
  220. }
  221. else {
  222. Subsection = (PSUBSECTION)((PLARGE_CONTROL_AREA)ControlArea + 1);
  223. }
  224. #if DBG
  225. if (Subsection->SubsectionBase != NULL) {
  226. PointerPte = Subsection->SubsectionBase;
  227. LastPte = PointerPte + Segment->NonExtendedPtes;
  228. while (PointerPte < LastPte) {
  229. //
  230. // Prototype PTEs for segments backed by paging file are
  231. // either in demand zero, page file format, or transition.
  232. //
  233. ASSERT (PointerPte->u.Hard.Valid == 0);
  234. ASSERT ((PointerPte->u.Soft.Prototype == 1) ||
  235. (PointerPte->u.Long == 0));
  236. PointerPte += 1;
  237. }
  238. }
  239. #endif
  240. //
  241. // Deallocate the control area and subsections.
  242. //
  243. ASSERT (ControlArea->u.Flags.GlobalOnlyPerSession == 0);
  244. if (ControlArea->FilePointer != NULL) {
  245. MappedSubsection = (PMSUBSECTION) Subsection;
  246. LOCK_PFN (OldIrql);
  247. while (MappedSubsection != NULL) {
  248. if (MappedSubsection->DereferenceList.Flink != NULL) {
  249. //
  250. // Remove this from the list of unused subsections.
  251. //
  252. RemoveEntryList (&MappedSubsection->DereferenceList);
  253. MI_UNUSED_SUBSECTIONS_COUNT_REMOVE (MappedSubsection);
  254. }
  255. MappedSubsection = (PMSUBSECTION) MappedSubsection->NextSubsection;
  256. }
  257. UNLOCK_PFN (OldIrql);
  258. if (Subsection->SubsectionBase != NULL) {
  259. ExFreePool (Subsection->SubsectionBase);
  260. }
  261. }
  262. Subsection = Subsection->NextSubsection;
  263. while (Subsection != NULL) {
  264. if (Subsection->SubsectionBase != NULL) {
  265. ExFreePool (Subsection->SubsectionBase);
  266. }
  267. NextSubsection = Subsection->NextSubsection;
  268. ExFreePool (Subsection);
  269. Subsection = NextSubsection;
  270. }
  271. NumberOfCommittedPages = Segment->NumberOfCommittedPages;
  272. if (NumberOfCommittedPages != 0) {
  273. MiReturnCommitment (NumberOfCommittedPages);
  274. MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_SEGMENT_DELETE1,
  275. NumberOfCommittedPages);
  276. InterlockedExchangeAddSizeT (&MmSharedCommit, 0-NumberOfCommittedPages);
  277. }
  278. ExFreePool (ControlArea);
  279. ExFreePool (Segment);
  280. //
  281. // The file mapped Segment object is now deleted.
  282. //
  283. return;
  284. }
  285. }
  286. //
  287. // This is a page file backed or image segment. The segment is being
  288. // deleted, remove all references to the paging file and physical memory.
  289. //
  290. // The PFN lock is required for deallocating pages from a paging
  291. // file and for deleting transition PTEs.
  292. //
  293. if ((ControlArea->u.Flags.GlobalOnlyPerSession == 0) &&
  294. (ControlArea->u.Flags.Rom == 0)) {
  295. Subsection = (PSUBSECTION)(ControlArea + 1);
  296. }
  297. else {
  298. Subsection = (PSUBSECTION)((PLARGE_CONTROL_AREA)ControlArea + 1);
  299. }
  300. PointerPte = Subsection->SubsectionBase;
  301. LastPte = PointerPte + Segment->NonExtendedPtes;
  302. PteForProto = MiGetPteAddress (PointerPte);
  303. //
  304. // Access the first prototype PTE to try and make it resident before
  305. // acquiring the PFN lock. This is purely an optimization to reduce
  306. // PFN lock hold duration.
  307. //
  308. *(volatile MMPTE *) PointerPte;
  309. LOCK_PFN (OldIrql);
  310. if (PteForProto->u.Hard.Valid == 0) {
  311. MiMakeSystemAddressValidPfn (PointerPte, OldIrql);
  312. }
  313. while (PointerPte < LastPte) {
  314. if ((MiIsPteOnPdeBoundary (PointerPte)) &&
  315. (PointerPte != Subsection->SubsectionBase)) {
  316. //
  317. // Briefly release and reacquire the PFN lock so that
  318. // processing large segments here doesn't stall other contending
  319. // threads or DPCs for long periods of time.
  320. //
  321. UNLOCK_PFN (OldIrql);
  322. PteForProto = MiGetPteAddress (PointerPte);
  323. LOCK_PFN (OldIrql);
  324. //
  325. // We are on a page boundary, make sure this PTE is resident.
  326. //
  327. if (PteForProto->u.Hard.Valid == 0) {
  328. MiMakeSystemAddressValidPfn (PointerPte, OldIrql);
  329. }
  330. }
  331. PteContents = *PointerPte;
  332. //
  333. // Prototype PTEs for segments backed by paging file
  334. // are either in demand zero, page file format, or transition.
  335. //
  336. ASSERT (PteContents.u.Hard.Valid == 0);
  337. if (PteContents.u.Soft.Prototype == 0) {
  338. if (PteContents.u.Soft.Transition == 1) {
  339. //
  340. // Prototype PTE in transition, put the page on the free list.
  341. //
  342. Pfn1 = MI_PFN_ELEMENT (PteContents.u.Trans.PageFrameNumber);
  343. MI_SET_PFN_DELETED (Pfn1);
  344. PageTableFrameIndex = Pfn1->u4.PteFrame;
  345. Pfn2 = MI_PFN_ELEMENT (PageTableFrameIndex);
  346. MiDecrementShareCountInline (Pfn2, PageTableFrameIndex);
  347. //
  348. // Check the reference count for the page, if the reference
  349. // count is zero and the page is not on the freelist,
  350. // move the page to the free list, if the reference
  351. // count is not zero, ignore this page.
  352. // When the reference count goes to zero, it will be placed
  353. // on the free list.
  354. //
  355. if (Pfn1->u3.e2.ReferenceCount == 0) {
  356. MiUnlinkPageFromList (Pfn1);
  357. MiReleasePageFileSpace (Pfn1->OriginalPte);
  358. MiInsertPageInFreeList (MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents));
  359. }
  360. }
  361. else {
  362. //
  363. // This is not a prototype PTE, if any paging file
  364. // space has been allocated, release it.
  365. //
  366. if (IS_PTE_NOT_DEMAND_ZERO (PteContents)) {
  367. MiReleasePageFileSpace (PteContents);
  368. }
  369. }
  370. }
  371. #if DBG
  372. MI_WRITE_INVALID_PTE (PointerPte, ZeroPte);
  373. #endif
  374. PointerPte += 1;
  375. }
  376. UNLOCK_PFN (OldIrql);
  377. //
  378. // If there have been committed pages in this segment, adjust
  379. // the total commit count.
  380. //
  381. NumberOfCommittedPages = Segment->NumberOfCommittedPages;
  382. if (NumberOfCommittedPages != 0) {
  383. MiReturnCommitment (NumberOfCommittedPages);
  384. if (ControlArea->u.Flags.Image) {
  385. MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_SEGMENT_DELETE2,
  386. NumberOfCommittedPages);
  387. }
  388. else {
  389. MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_SEGMENT_DELETE3,
  390. NumberOfCommittedPages);
  391. }
  392. InterlockedExchangeAddSizeT (&MmSharedCommit, 0-NumberOfCommittedPages);
  393. }
  394. ExFreePool (ControlArea);
  395. ExFreePool (Segment);
  396. return;
  397. }
  398. ULONG
  399. MmDoesFileHaveUserWritableReferences (
  400. IN PSECTION_OBJECT_POINTERS SectionPointer
  401. )
  402. /*++
  403. Routine Description:
  404. This routine is called by the transaction filesystem to determine if
  405. the given transaction is referencing a file which has user writable sections
  406. or user writable views into it. If so, the transaction must be aborted
  407. as it cannot be guaranteed atomicity.
  408. The transaction filesystem is responsible for checking and intercepting
  409. file object creates that specify write access prior to using this
  410. interface. Specifically, prior to starting a transaction, the transaction
  411. filesystem must ensure that there are no writable file objects that
  412. currently exist for the given file in the transaction. While the
  413. transaction is ongoing, requests to create file objects with write access
  414. for the transaction files must be refused.
  415. This Mm routine exists to catch the case where the user has closed the
  416. file handles and the section handles, but still has open writable views.
  417. For this reason, no locks are needed to read the value below.
  418. Arguments:
  419. SectionPointer - Supplies a pointer to the section object pointers
  420. from the file object.
  421. Return Value:
  422. Number of user writable references.
  423. Environment:
  424. Kernel mode, APC_LEVEL or below, no mutexes held.
  425. --*/
  426. {
  427. KIRQL OldIrql;
  428. ULONG WritableUserReferences;
  429. PCONTROL_AREA ControlArea;
  430. LOCK_PFN (OldIrql);
  431. ControlArea = (PCONTROL_AREA)(SectionPointer->DataSectionObject);
  432. if (ControlArea == NULL) {
  433. UNLOCK_PFN (OldIrql);
  434. return 0;
  435. }
  436. //
  437. // Up the map view count so the control area cannot be deleted
  438. // out from under the call.
  439. //
  440. ControlArea->NumberOfMappedViews += 1;
  441. if (MiGetPteAddress (&ControlArea->Segment->WritableUserReferences)->u.Hard.Valid == 0) {
  442. MiMakeSystemAddressValidPfn (&ControlArea->Segment->WritableUserReferences, OldIrql);
  443. }
  444. WritableUserReferences = ControlArea->Segment->WritableUserReferences;
  445. ASSERT ((LONG)ControlArea->NumberOfMappedViews >= 1);
  446. ControlArea->NumberOfMappedViews -= 1;
  447. //
  448. // This routine will release the PFN lock.
  449. //
  450. MiCheckControlArea (ControlArea, NULL, OldIrql);
  451. return WritableUserReferences;
  452. }
  453. VOID
  454. MiDereferenceControlAreaBySection (
  455. IN PCONTROL_AREA ControlArea,
  456. IN ULONG UserRef
  457. )
  458. /*++
  459. Routine Description:
  460. This is a nonpaged helper routine to dereference the specified control area.
  461. Arguments:
  462. ControlArea - Supplies a pointer to the control area.
  463. UserRef - Supplies the number of user dereferences to apply.
  464. Return Value:
  465. None.
  466. --*/
  467. {
  468. KIRQL OldIrql;
  469. LOCK_PFN (OldIrql);
  470. ControlArea->NumberOfSectionReferences -= 1;
  471. ControlArea->NumberOfUserReferences -= UserRef;
  472. //
  473. // Check to see if the control area (segment) should be deleted.
  474. // This routine releases the PFN lock.
  475. //
  476. MiCheckControlArea (ControlArea, NULL, OldIrql);
  477. }
  478. VOID
  479. MiSectionDelete (
  480. IN PVOID Object
  481. )
  482. /*++
  483. Routine Description:
  484. This routine is called by the object management procedures whenever
  485. the last reference to a section object has been removed. This routine
  486. dereferences the associated segment object and checks to see if
  487. the segment object should be deleted by queueing the segment to the
  488. segment deletion thread.
  489. Arguments:
  490. Object - a pointer to the body of the section object.
  491. Return Value:
  492. None.
  493. --*/
  494. {
  495. PSECTION Section;
  496. PCONTROL_AREA ControlArea;
  497. ULONG UserRef;
  498. Section = (PSECTION)Object;
  499. if (Section->Segment == NULL) {
  500. //
  501. // The section was never initialized, no need to remove
  502. // any structures.
  503. //
  504. return;
  505. }
  506. UserRef = Section->u.Flags.UserReference;
  507. ControlArea = Section->Segment->ControlArea;
  508. if (Section->Address.StartingVpn != 0) {
  509. //
  510. // This section is based, remove the base address from the
  511. // tree.
  512. //
  513. // Get the allocation base mutex.
  514. //
  515. KeAcquireGuardedMutex (&MmSectionBasedMutex);
  516. MiRemoveBasedSection (Section);
  517. KeReleaseGuardedMutex (&MmSectionBasedMutex);
  518. }
  519. //
  520. // Adjust the count of writable user sections for transaction support.
  521. //
  522. if ((Section->u.Flags.UserWritable == 1) &&
  523. (ControlArea->u.Flags.Image == 0) &&
  524. (ControlArea->FilePointer != NULL)) {
  525. ASSERT (Section->InitialPageProtection & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE));
  526. InterlockedDecrement ((PLONG)&ControlArea->Segment->WritableUserReferences);
  527. }
  528. //
  529. // Decrement the number of section references to the segment for this
  530. // section. This requires APCs to be blocked and the PFN lock to
  531. // synchronize upon.
  532. //
  533. MiDereferenceControlAreaBySection (ControlArea, UserRef);
  534. return;
  535. }
  536. VOID
  537. MiDereferenceSegmentThread (
  538. IN PVOID StartContext
  539. )
  540. /*++
  541. Routine Description:
  542. This routine is the thread for dereferencing segments which have
  543. no references from any sections or mapped views AND there are
  544. no prototype PTEs within the segment which are in the transition
  545. state (i.e., no PFN database references to the segment).
  546. It also does double duty and is used for expansion of paging files.
  547. Arguments:
  548. StartContext - Not used.
  549. Return Value:
  550. None.
  551. --*/
  552. {
  553. PCONTROL_AREA ControlArea;
  554. PETHREAD CurrentThread;
  555. PMMPAGE_FILE_EXPANSION PageExpand;
  556. PLIST_ENTRY NextEntry;
  557. KIRQL OldIrql;
  558. static KWAIT_BLOCK WaitBlockArray[SegMaximumObject];
  559. PVOID WaitObjects[SegMaximumObject];
  560. NTSTATUS Status;
  561. UNREFERENCED_PARAMETER (StartContext);
  562. //
  563. // Make this a real time thread.
  564. //
  565. CurrentThread = PsGetCurrentThread();
  566. KeSetPriorityThread (&CurrentThread->Tcb, LOW_REALTIME_PRIORITY + 2);
  567. CurrentThread->MemoryMaker = 1;
  568. WaitObjects[SegmentDereference] = (PVOID)&MmDereferenceSegmentHeader.Semaphore;
  569. WaitObjects[UsedSegmentCleanup] = (PVOID)&MmUnusedSegmentCleanup;
  570. for (;;) {
  571. Status = KeWaitForMultipleObjects(SegMaximumObject,
  572. &WaitObjects[0],
  573. WaitAny,
  574. WrVirtualMemory,
  575. UserMode,
  576. FALSE,
  577. NULL,
  578. &WaitBlockArray[0]);
  579. //
  580. // Switch on the wait status.
  581. //
  582. switch (Status) {
  583. case SegmentDereference:
  584. //
  585. // An entry is available to dereference, acquire the spinlock
  586. // and remove the entry.
  587. //
  588. ExAcquireSpinLock (&MmDereferenceSegmentHeader.Lock, &OldIrql);
  589. if (IsListEmpty (&MmDereferenceSegmentHeader.ListHead)) {
  590. //
  591. // There is nothing in the list, rewait.
  592. //
  593. ExReleaseSpinLock (&MmDereferenceSegmentHeader.Lock, OldIrql);
  594. break;
  595. }
  596. NextEntry = RemoveHeadList (&MmDereferenceSegmentHeader.ListHead);
  597. ExReleaseSpinLock (&MmDereferenceSegmentHeader.Lock, OldIrql);
  598. ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
  599. ControlArea = CONTAINING_RECORD (NextEntry,
  600. CONTROL_AREA,
  601. DereferenceList);
  602. if (ControlArea->Segment != NULL) {
  603. //
  604. // This is a control area, delete it after indicating
  605. // this entry is not on any list.
  606. //
  607. ControlArea->DereferenceList.Flink = NULL;
  608. ASSERT (ControlArea->u.Flags.FilePointerNull == 1);
  609. MiSegmentDelete (ControlArea->Segment);
  610. }
  611. else {
  612. //
  613. // This is a request to expand or reduce the paging files.
  614. //
  615. PageExpand = (PMMPAGE_FILE_EXPANSION)ControlArea;
  616. if (PageExpand->RequestedExpansionSize == MI_CONTRACT_PAGEFILES) {
  617. //
  618. // Attempt to reduce the size of the paging files.
  619. //
  620. ASSERT (PageExpand == &MiPageFileContract);
  621. MiAttemptPageFileReduction ();
  622. }
  623. else {
  624. //
  625. // Attempt to expand the size of the paging files.
  626. //
  627. MiExtendPagingFiles (PageExpand);
  628. KeSetEvent (&PageExpand->Event, 0, FALSE);
  629. MiRemoveUnusedSegments ();
  630. }
  631. }
  632. break;
  633. case UsedSegmentCleanup:
  634. MiRemoveUnusedSegments ();
  635. KeClearEvent (&MmUnusedSegmentCleanup);
  636. break;
  637. default:
  638. KdPrint(("MMSegmentderef: Illegal wait status, %lx =\n", Status));
  639. break;
  640. } // end switch
  641. } //end for
  642. return;
  643. }
  644. ULONG
  645. MiSectionInitialization (
  646. )
  647. /*++
  648. Routine Description:
  649. This function creates the section object type descriptor at system
  650. initialization and stores the address of the object type descriptor
  651. in global storage.
  652. Arguments:
  653. None.
  654. Return Value:
  655. TRUE - Initialization was successful.
  656. FALSE - Initialization Failed.
  657. --*/
  658. {
  659. OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
  660. UNICODE_STRING TypeName;
  661. HANDLE ThreadHandle;
  662. OBJECT_ATTRIBUTES ObjectAttributes;
  663. UNICODE_STRING SectionName;
  664. PSECTION Section;
  665. HANDLE Handle;
  666. PSEGMENT Segment;
  667. PCONTROL_AREA ControlArea;
  668. NTSTATUS Status;
  669. ASSERT (MmSectionBasedRoot.NumberGenericTableElements == 0);
  670. MmSectionBasedRoot.BalancedRoot.u1.Parent = &MmSectionBasedRoot.BalancedRoot;
  671. //
  672. // Initialize the common fields of the Object Type Initializer record
  673. //
  674. RtlZeroMemory (&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
  675. ObjectTypeInitializer.Length = sizeof (ObjectTypeInitializer);
  676. ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
  677. ObjectTypeInitializer.GenericMapping = MiSectionMapping;
  678. ObjectTypeInitializer.PoolType = PagedPool;
  679. ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(SECTION);
  680. //
  681. // Initialize string descriptor.
  682. //
  683. #define TYPE_SECTION L"Section"
  684. TypeName.Buffer = (const PUSHORT) TYPE_SECTION;
  685. TypeName.Length = sizeof (TYPE_SECTION) - sizeof (WCHAR);
  686. TypeName.MaximumLength = sizeof TYPE_SECTION;
  687. //
  688. // Create the section object type descriptor
  689. //
  690. ObjectTypeInitializer.ValidAccessMask = SECTION_ALL_ACCESS;
  691. ObjectTypeInitializer.DeleteProcedure = MiSectionDelete;
  692. ObjectTypeInitializer.GenericMapping = MiSectionMapping;
  693. ObjectTypeInitializer.UseDefaultObject = TRUE;
  694. if (!NT_SUCCESS(ObCreateObjectType (&TypeName,
  695. &ObjectTypeInitializer,
  696. (PSECURITY_DESCRIPTOR) NULL,
  697. &MmSectionObjectType))) {
  698. return FALSE;
  699. }
  700. //
  701. // Create the Segment dereferencing thread.
  702. //
  703. InitializeObjectAttributes (&ObjectAttributes,
  704. NULL,
  705. 0,
  706. NULL,
  707. NULL);
  708. if (!NT_SUCCESS(PsCreateSystemThread (&ThreadHandle,
  709. THREAD_ALL_ACCESS,
  710. &ObjectAttributes,
  711. 0,
  712. NULL,
  713. MiDereferenceSegmentThread,
  714. NULL))) {
  715. return FALSE;
  716. }
  717. ZwClose (ThreadHandle);
  718. //
  719. // Create the permanent section which maps physical memory.
  720. //
  721. Segment = (PSEGMENT)ExAllocatePoolWithTag (PagedPool,
  722. sizeof(SEGMENT),
  723. 'gSmM');
  724. if (Segment == NULL) {
  725. return FALSE;
  726. }
  727. ControlArea = ExAllocatePoolWithTag (NonPagedPool,
  728. (ULONG)sizeof(CONTROL_AREA),
  729. MMCONTROL);
  730. if (ControlArea == NULL) {
  731. ExFreePool (Segment);
  732. return FALSE;
  733. }
  734. RtlZeroMemory (Segment, sizeof(SEGMENT));
  735. RtlZeroMemory (ControlArea, sizeof(CONTROL_AREA));
  736. ControlArea->Segment = Segment;
  737. ControlArea->NumberOfSectionReferences = 1;
  738. ControlArea->u.Flags.PhysicalMemory = 1;
  739. Segment->ControlArea = ControlArea;
  740. Segment->SegmentPteTemplate = ZeroPte;
  741. //
  742. // Now that the segment object is created, create a section object
  743. // which refers to the segment object.
  744. //
  745. #define DEVICE_PHYSICAL_MEMORY L"\\Device\\PhysicalMemory"
  746. SectionName.Buffer = (const PUSHORT)DEVICE_PHYSICAL_MEMORY;
  747. SectionName.Length = sizeof (DEVICE_PHYSICAL_MEMORY) - sizeof (WCHAR);
  748. SectionName.MaximumLength = sizeof (DEVICE_PHYSICAL_MEMORY);
  749. InitializeObjectAttributes (&ObjectAttributes,
  750. &SectionName,
  751. OBJ_PERMANENT,
  752. NULL,
  753. NULL);
  754. Status = ObCreateObject (KernelMode,
  755. MmSectionObjectType,
  756. &ObjectAttributes,
  757. KernelMode,
  758. NULL,
  759. sizeof(SECTION),
  760. sizeof(SECTION),
  761. 0,
  762. (PVOID *)&Section);
  763. if (!NT_SUCCESS(Status)) {
  764. ExFreePool (ControlArea);
  765. ExFreePool (Segment);
  766. return FALSE;
  767. }
  768. Section->Segment = Segment;
  769. Section->SizeOfSection.QuadPart = ((LONGLONG)1 << PHYSICAL_ADDRESS_BITS) - 1;
  770. Section->u.LongFlags = 0;
  771. Section->InitialPageProtection = PAGE_EXECUTE_READWRITE;
  772. Status = ObInsertObject ((PVOID)Section,
  773. NULL,
  774. SECTION_MAP_READ,
  775. 0,
  776. NULL,
  777. &Handle);
  778. if (!NT_SUCCESS (Status)) {
  779. return FALSE;
  780. }
  781. if (!NT_SUCCESS (NtClose (Handle))) {
  782. return FALSE;
  783. }
  784. return TRUE;
  785. }
  786. BOOLEAN
  787. MmForceSectionClosed (
  788. IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
  789. IN BOOLEAN DelayClose
  790. )
  791. /*++
  792. Routine Description:
  793. This function examines the Section object pointers. If they are NULL,
  794. no further action is taken and the value TRUE is returned.
  795. If the Section object pointer is not NULL, the section reference count
  796. and the map view count are checked. If both counts are zero, the
  797. segment associated with the file is deleted and the file closed.
  798. If one of the counts is non-zero, no action is taken and the
  799. value FALSE is returned.
  800. Arguments:
  801. SectionObjectPointer - Supplies a pointer to a section object.
  802. DelayClose - Supplies the value TRUE if the close operation should
  803. occur as soon as possible in the event this section
  804. cannot be closed now due to outstanding references.
  805. Return Value:
  806. TRUE - The segment was deleted and the file closed or no segment was
  807. located.
  808. FALSE - The segment was not deleted and no action was performed OR
  809. an I/O error occurred trying to write the pages.
  810. --*/
  811. {
  812. PCONTROL_AREA ControlArea;
  813. KIRQL OldIrql;
  814. LOGICAL state;
  815. //
  816. // Check the status of the control area, if the control area is in use
  817. // or the control area is being deleted, this operation cannot continue.
  818. //
  819. state = MiCheckControlAreaStatus (CheckBothSection,
  820. SectionObjectPointer,
  821. DelayClose,
  822. &ControlArea,
  823. &OldIrql);
  824. if (ControlArea == NULL) {
  825. return (BOOLEAN) state;
  826. }
  827. //
  828. // PFN LOCK IS NOW HELD!
  829. //
  830. //
  831. // Repeat until there are no more control areas - multiple control areas
  832. // for the same image section occur to support user global DLLs - these DLLs
  833. // require data that is shared within a session but not across sessions.
  834. // Note this can only happen for Hydra.
  835. //
  836. do {
  837. //
  838. // Set the being deleted flag and up the number of mapped views
  839. // for the segment. Upping the number of mapped views prevents
  840. // the segment from being deleted and passed to the deletion thread
  841. // while we are forcing a delete.
  842. //
  843. ControlArea->u.Flags.BeingDeleted = 1;
  844. ASSERT (ControlArea->NumberOfMappedViews == 0);
  845. ControlArea->NumberOfMappedViews = 1;
  846. //
  847. // This is a page file backed or image Segment. The Segment is being
  848. // deleted, remove all references to the paging file and physical memory.
  849. //
  850. UNLOCK_PFN (OldIrql);
  851. //
  852. // Delete the section by flushing all modified pages back to the section
  853. // if it is a file and freeing up the pages such that the
  854. // PfnReferenceCount goes to zero.
  855. //
  856. MiCleanSection (ControlArea, TRUE);
  857. //
  858. // Get the next Hydra control area.
  859. //
  860. state = MiCheckControlAreaStatus (CheckBothSection,
  861. SectionObjectPointer,
  862. DelayClose,
  863. &ControlArea,
  864. &OldIrql);
  865. } while (ControlArea);
  866. return (BOOLEAN) state;
  867. }
  868. VOID
  869. MiCleanSection (
  870. IN PCONTROL_AREA ControlArea,
  871. IN LOGICAL DirtyDataPagesOk
  872. )
  873. /*++
  874. Routine Description:
  875. This function examines each prototype PTE in the section and
  876. takes the appropriate action to "delete" the prototype PTE.
  877. If the PTE is dirty and is backed by a file (not a paging file),
  878. the corresponding page is written to the file.
  879. At the completion of this service, the section which was
  880. operated upon is no longer usable.
  881. NOTE - ALL I/O ERRORS ARE IGNORED. IF ANY WRITES FAIL, THE
  882. DIRTY PAGES ARE MARKED CLEAN AND THE SECTION IS DELETED.
  883. Arguments:
  884. ControlArea - Supplies a pointer to the control area for the section.
  885. DirtyDataPagesOk - Supplies TRUE if dirty data pages are ok. If FALSE
  886. is specified then no dirty data pages are expected (as
  887. this is a dereference operation) so any encountered
  888. must be due to pool corruption so bugcheck.
  889. Note that dirty image pages are always discarded.
  890. This should only happen for images that were either
  891. read in from floppies or images with shared global
  892. subsections.
  893. Return Value:
  894. None.
  895. --*/
  896. {
  897. LOGICAL DroppedPfnLock;
  898. PMMPTE PointerPte;
  899. PMMPTE PointerPde;
  900. PMMPTE LastPte;
  901. PMMPTE LastWritten;
  902. PMMPTE FirstWritten;
  903. MMPTE PteContents;
  904. PMMPFN Pfn1;
  905. PMMPFN Pfn2;
  906. PMMPFN Pfn3;
  907. PMMPTE WrittenPte;
  908. MMPTE WrittenContents;
  909. KIRQL OldIrql;
  910. PMDL Mdl;
  911. PSUBSECTION Subsection;
  912. PPFN_NUMBER Page;
  913. PPFN_NUMBER LastPage;
  914. LARGE_INTEGER StartingOffset;
  915. LARGE_INTEGER TempOffset;
  916. NTSTATUS Status;
  917. IO_STATUS_BLOCK IoStatus;
  918. ULONG WriteNow;
  919. ULONG ImageSection;
  920. ULONG DelayCount;
  921. ULONG First;
  922. KEVENT IoEvent;
  923. PFN_NUMBER PageTableFrameIndex;
  924. PFN_NUMBER MdlHack[(sizeof(MDL)/sizeof(PFN_NUMBER)) + MM_MAXIMUM_WRITE_CLUSTER];
  925. ULONG ReflushCount;
  926. ULONG MaxClusterSize;
  927. WriteNow = FALSE;
  928. ImageSection = FALSE;
  929. DelayCount = 0;
  930. MaxClusterSize = MmModifiedWriteClusterSize;
  931. FirstWritten = NULL;
  932. ASSERT (ControlArea->FilePointer);
  933. if ((ControlArea->u.Flags.GlobalOnlyPerSession == 0) &&
  934. (ControlArea->u.Flags.Rom == 0)) {
  935. Subsection = (PSUBSECTION)(ControlArea + 1);
  936. }
  937. else {
  938. Subsection = (PSUBSECTION)((PLARGE_CONTROL_AREA)ControlArea + 1);
  939. }
  940. if (ControlArea->u.Flags.Image) {
  941. ImageSection = TRUE;
  942. PointerPte = Subsection->SubsectionBase;
  943. LastPte = PointerPte + ControlArea->Segment->NonExtendedPtes;
  944. }
  945. else {
  946. //
  947. // Initializing these are not needed for correctness as they are
  948. // overwritten below, but without it the compiler cannot compile
  949. // this code W4 to check for use of uninitialized variables.
  950. //
  951. PointerPte = NULL;
  952. LastPte = NULL;
  953. }
  954. Mdl = (PMDL) MdlHack;
  955. KeInitializeEvent (&IoEvent, NotificationEvent, FALSE);
  956. LastWritten = NULL;
  957. ASSERT (MmModifiedWriteClusterSize == MM_MAXIMUM_WRITE_CLUSTER);
  958. LastPage = NULL;
  959. //
  960. // Initializing StartingOffset is not needed for correctness
  961. // but without it the compiler cannot compile this code
  962. // W4 to check for use of uninitialized variables.
  963. //
  964. StartingOffset.QuadPart = 0;
  965. //
  966. // The PFN lock is required for deallocating pages from a paging
  967. // file and for deleting transition PTEs.
  968. //
  969. LOCK_PFN (OldIrql);
  970. //
  971. // Stop the modified page writer from writing pages to this
  972. // file, and if any paging I/O is in progress, wait for it
  973. // to complete.
  974. //
  975. ControlArea->u.Flags.NoModifiedWriting = 1;
  976. while (ControlArea->ModifiedWriteCount != 0) {
  977. //
  978. // There is modified page writing in progess. Set the
  979. // flag in the control area indicating the modified page
  980. // writer should signal when a write to this control area
  981. // is complete. Release the PFN LOCK and wait in an
  982. // atomic operation. Once the wait is satisfied, recheck
  983. // to make sure it was this file's I/O that was written.
  984. //
  985. ControlArea->u.Flags.SetMappedFileIoComplete = 1;
  986. //
  987. // Keep APCs blocked so no special APCs can be delivered in KeWait
  988. // which would cause the dispatcher lock to be released opening a
  989. // window where this thread could miss a pulse.
  990. //
  991. UNLOCK_PFN_AND_THEN_WAIT (APC_LEVEL);
  992. KeWaitForSingleObject (&MmMappedFileIoComplete,
  993. WrPageOut,
  994. KernelMode,
  995. FALSE,
  996. NULL);
  997. KeLowerIrql (OldIrql);
  998. LOCK_PFN (OldIrql);
  999. }
  1000. if (ImageSection == FALSE) {
  1001. while (Subsection->SubsectionBase == NULL) {
  1002. Subsection = Subsection->NextSubsection;
  1003. if (Subsection == NULL) {
  1004. goto alldone;
  1005. }
  1006. }
  1007. PointerPte = Subsection->SubsectionBase;
  1008. LastPte = PointerPte + Subsection->PtesInSubsection;
  1009. }
  1010. for (;;) {
  1011. restartchunk:
  1012. First = TRUE;
  1013. while (PointerPte < LastPte) {
  1014. if ((MiIsPteOnPdeBoundary(PointerPte)) || (First)) {
  1015. First = FALSE;
  1016. if ((ImageSection) ||
  1017. (MiCheckProtoPtePageState(PointerPte, MM_NOIRQL, &DroppedPfnLock))) {
  1018. MiMakeSystemAddressValidPfn (PointerPte, OldIrql);
  1019. }
  1020. else {
  1021. //
  1022. // Paged pool page is not resident, hence no transition or
  1023. // valid prototype PTEs can be present in it. Skip it.
  1024. //
  1025. PointerPte = (PMMPTE)((((ULONG_PTR)PointerPte | PAGE_SIZE - 1)) + 1);
  1026. if (LastWritten != NULL) {
  1027. WriteNow = TRUE;
  1028. }
  1029. goto WriteItOut;
  1030. }
  1031. }
  1032. PteContents = *PointerPte;
  1033. //
  1034. // Prototype PTEs for Segments backed by paging file
  1035. // are either in demand zero, page file format, or transition.
  1036. //
  1037. if (PteContents.u.Hard.Valid == 1) {
  1038. KeBugCheckEx (POOL_CORRUPTION_IN_FILE_AREA,
  1039. 0x0,
  1040. (ULONG_PTR)ControlArea,
  1041. (ULONG_PTR)PointerPte,
  1042. (ULONG_PTR)PteContents.u.Long);
  1043. }
  1044. if (PteContents.u.Soft.Prototype == 1) {
  1045. //
  1046. // This is a normal prototype PTE in mapped file format.
  1047. //
  1048. if (LastWritten != NULL) {
  1049. WriteNow = TRUE;
  1050. }
  1051. }
  1052. else if (PteContents.u.Soft.Transition == 1) {
  1053. //
  1054. // Prototype PTE in transition, there are 3 possible cases:
  1055. // 1. The page is part of an image which is sharable and
  1056. // refers to the paging file - dereference page file
  1057. // space and free the physical page.
  1058. // 2. The page refers to the segment but is not modified -
  1059. // free the physical page.
  1060. // 3. The page refers to the segment and is modified -
  1061. // write the page to the file and free the physical page.
  1062. //
  1063. Pfn1 = MI_PFN_ELEMENT (PteContents.u.Trans.PageFrameNumber);
  1064. if (Pfn1->u3.e2.ReferenceCount != 0) {
  1065. if (DelayCount < 20) {
  1066. //
  1067. // There must be an I/O in progress on this
  1068. // page. Wait for the I/O operation to complete.
  1069. //
  1070. UNLOCK_PFN (OldIrql);
  1071. //
  1072. // Drain the deferred lists as these pages may be
  1073. // sitting in there right now.
  1074. //
  1075. MiDeferredUnlockPages (0);
  1076. KeDelayExecutionThread (KernelMode, FALSE, (PLARGE_INTEGER)&MmShortTime);
  1077. DelayCount += 1;
  1078. //
  1079. // Redo the loop, if the delay count is greater than
  1080. // 20, assume that this thread is deadlocked and
  1081. // don't purge this page. The file system can deal
  1082. // with the write operation in progress.
  1083. //
  1084. PointerPde = MiGetPteAddress (PointerPte);
  1085. LOCK_PFN (OldIrql);
  1086. if (PointerPde->u.Hard.Valid == 0) {
  1087. MiMakeSystemAddressValidPfn (PointerPte, OldIrql);
  1088. }
  1089. continue;
  1090. }
  1091. #if DBG
  1092. //
  1093. // The I/O still has not completed, just ignore
  1094. // the fact that the I/O is in progress and
  1095. // delete the page.
  1096. //
  1097. KdPrint(("MM:CLEAN - page number %lx has i/o outstanding\n",
  1098. PteContents.u.Trans.PageFrameNumber));
  1099. #endif
  1100. }
  1101. if (Pfn1->OriginalPte.u.Soft.Prototype == 0) {
  1102. //
  1103. // Paging file reference (case 1).
  1104. //
  1105. MI_SET_PFN_DELETED (Pfn1);
  1106. if (!ImageSection) {
  1107. //
  1108. // This is not an image section, it must be a
  1109. // page file backed section, therefore decrement
  1110. // the PFN reference count for the control area.
  1111. //
  1112. ControlArea->NumberOfPfnReferences -= 1;
  1113. ASSERT ((LONG)ControlArea->NumberOfPfnReferences >= 0);
  1114. }
  1115. #if DBG
  1116. else {
  1117. //
  1118. // This should only happen for images with shared
  1119. // global subsections.
  1120. //
  1121. }
  1122. #endif
  1123. PageTableFrameIndex = Pfn1->u4.PteFrame;
  1124. Pfn2 = MI_PFN_ELEMENT (PageTableFrameIndex);
  1125. MiDecrementShareCountInline (Pfn2, PageTableFrameIndex);
  1126. //
  1127. // Check the reference count for the page, if the // reference count is zero and the page is not on the
  1128. // freelist, move the page to the free list, if the
  1129. // reference count is not zero, ignore this page. When
  1130. // the reference count goes to zero, it will be placed
  1131. // on the free list.
  1132. //
  1133. if ((Pfn1->u3.e2.ReferenceCount == 0) &&
  1134. (Pfn1->u3.e1.PageLocation != FreePageList)) {
  1135. MiUnlinkPageFromList (Pfn1);
  1136. MiReleasePageFileSpace (Pfn1->OriginalPte);
  1137. MiInsertPageInFreeList (MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents));
  1138. }
  1139. MI_WRITE_INVALID_PTE (PointerPte, ZeroPte);
  1140. //
  1141. // If a cluster of pages to write has been completed,
  1142. // set the WriteNow flag.
  1143. //
  1144. if (LastWritten != NULL) {
  1145. WriteNow = TRUE;
  1146. }
  1147. }
  1148. else {
  1149. if ((Pfn1->u3.e1.Modified == 0) || (ImageSection)) {
  1150. //
  1151. // Non modified or image file page (case 2).
  1152. //
  1153. MI_SET_PFN_DELETED (Pfn1);
  1154. ControlArea->NumberOfPfnReferences -= 1;
  1155. ASSERT ((LONG)ControlArea->NumberOfPfnReferences >= 0);
  1156. PageTableFrameIndex = Pfn1->u4.PteFrame;
  1157. Pfn2 = MI_PFN_ELEMENT (PageTableFrameIndex);
  1158. MiDecrementShareCountInline (Pfn2, PageTableFrameIndex);
  1159. //
  1160. // Check the reference count for the page, if the
  1161. // reference count is zero and the page is not on
  1162. // the freelist, move the page to the free list,
  1163. // if the reference count is not zero, ignore this
  1164. // page. When the reference count goes to zero, it
  1165. // will be placed on the free list.
  1166. //
  1167. if ((Pfn1->u3.e2.ReferenceCount == 0) &&
  1168. (Pfn1->u3.e1.PageLocation != FreePageList)) {
  1169. MiUnlinkPageFromList (Pfn1);
  1170. MiReleasePageFileSpace (Pfn1->OriginalPte);
  1171. MiInsertPageInFreeList (MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents));
  1172. }
  1173. MI_WRITE_INVALID_PTE (PointerPte, ZeroPte);
  1174. //
  1175. // If a cluster of pages to write has been
  1176. // completed, set the WriteNow flag.
  1177. //
  1178. if (LastWritten != NULL) {
  1179. WriteNow = TRUE;
  1180. }
  1181. }
  1182. else {
  1183. //
  1184. // Modified page backed by the file (case 3).
  1185. // Check to see if this is the first page of a
  1186. // cluster.
  1187. //
  1188. if (LastWritten == NULL) {
  1189. LastPage = (PPFN_NUMBER)(Mdl + 1);
  1190. ASSERT (MiGetSubsectionAddress(&Pfn1->OriginalPte) ==
  1191. Subsection);
  1192. //
  1193. // Calculate the offset to read into the file.
  1194. // offset = base + ((thispte - basepte) << PAGE_SHIFT)
  1195. //
  1196. ASSERT (Subsection->ControlArea->u.Flags.Image == 0);
  1197. StartingOffset.QuadPart = MiStartingOffset(
  1198. Subsection,
  1199. Pfn1->PteAddress);
  1200. MI_INITIALIZE_ZERO_MDL (Mdl);
  1201. Mdl->MdlFlags |= MDL_PAGES_LOCKED;
  1202. Mdl->StartVa = NULL;
  1203. Mdl->Size = (CSHORT)(sizeof(MDL) +
  1204. (sizeof(PFN_NUMBER) * MaxClusterSize));
  1205. FirstWritten = PointerPte;
  1206. }
  1207. LastWritten = PointerPte;
  1208. Mdl->ByteCount += PAGE_SIZE;
  1209. //
  1210. // If the cluster is now full,
  1211. // set the write now flag.
  1212. //
  1213. if (Mdl->ByteCount == (PAGE_SIZE * MaxClusterSize)) {
  1214. WriteNow = TRUE;
  1215. }
  1216. MiUnlinkPageFromList (Pfn1);
  1217. MI_SET_MODIFIED (Pfn1, 0, 0x27);
  1218. //
  1219. // Up the reference count for the physical page as
  1220. // there is I/O in progress.
  1221. //
  1222. MI_ADD_LOCKED_PAGE_CHARGE_FOR_MODIFIED_PAGE(Pfn1, TRUE, 22);
  1223. Pfn1->u3.e2.ReferenceCount += 1;
  1224. //
  1225. // Clear the modified bit for the page and set the
  1226. // write in progress bit.
  1227. //
  1228. *LastPage = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents);
  1229. LastPage += 1;
  1230. }
  1231. }
  1232. }
  1233. else {
  1234. if (IS_PTE_NOT_DEMAND_ZERO (PteContents)) {
  1235. MiReleasePageFileSpace (PteContents);
  1236. }
  1237. MI_WRITE_INVALID_PTE (PointerPte, ZeroPte);
  1238. //
  1239. // If a cluster of pages to write has been completed,
  1240. // set the WriteNow flag.
  1241. //
  1242. if (LastWritten != NULL) {
  1243. WriteNow = TRUE;
  1244. }
  1245. }
  1246. //
  1247. // Write the current cluster if it is complete,
  1248. // full, or the loop is now complete.
  1249. //
  1250. PointerPte += 1;
  1251. WriteItOut:
  1252. DelayCount = 0;
  1253. if ((WriteNow) ||
  1254. ((PointerPte == LastPte) && (LastWritten != NULL))) {
  1255. //
  1256. // Issue the write request.
  1257. //
  1258. UNLOCK_PFN (OldIrql);
  1259. if (DirtyDataPagesOk == FALSE) {
  1260. KeBugCheckEx (POOL_CORRUPTION_IN_FILE_AREA,
  1261. 0x1,
  1262. (ULONG_PTR)ControlArea,
  1263. (ULONG_PTR)Mdl,
  1264. ControlArea->u.LongFlags);
  1265. }
  1266. WriteNow = FALSE;
  1267. //
  1268. // Make sure the write does not go past the
  1269. // end of file. (segment size).
  1270. //
  1271. ASSERT (Subsection->ControlArea->u.Flags.Image == 0);
  1272. TempOffset = MiEndingOffset(Subsection);
  1273. if (((UINT64)StartingOffset.QuadPart + Mdl->ByteCount) >
  1274. (UINT64)TempOffset.QuadPart) {
  1275. ASSERT ((ULONG)(TempOffset.QuadPart -
  1276. StartingOffset.QuadPart) >
  1277. (Mdl->ByteCount - PAGE_SIZE));
  1278. Mdl->ByteCount = (ULONG)(TempOffset.QuadPart -
  1279. StartingOffset.QuadPart);
  1280. }
  1281. ReflushCount = 0;
  1282. while (TRUE) {
  1283. KeClearEvent (&IoEvent);
  1284. Status = IoSynchronousPageWrite (ControlArea->FilePointer,
  1285. Mdl,
  1286. &StartingOffset,
  1287. &IoEvent,
  1288. &IoStatus);
  1289. if (NT_SUCCESS(Status)) {
  1290. KeWaitForSingleObject (&IoEvent,
  1291. WrPageOut,
  1292. KernelMode,
  1293. FALSE,
  1294. NULL);
  1295. }
  1296. else {
  1297. IoStatus.Status = Status;
  1298. }
  1299. if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) {
  1300. MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
  1301. }
  1302. if (MmIsRetryIoStatus(IoStatus.Status)) {
  1303. ReflushCount -= 1;
  1304. if (ReflushCount & MiIoRetryMask) {
  1305. KeDelayExecutionThread (KernelMode, FALSE, (PLARGE_INTEGER)&Mm30Milliseconds);
  1306. continue;
  1307. }
  1308. }
  1309. break;
  1310. }
  1311. Page = (PPFN_NUMBER)(Mdl + 1);
  1312. LOCK_PFN (OldIrql);
  1313. if (MiIsPteOnPdeBoundary(PointerPte) == 0) {
  1314. //
  1315. // The next PTE is not in a different page, make
  1316. // sure this page did not leave memory when the
  1317. // I/O was in progress.
  1318. //
  1319. if (MiGetPteAddress (PointerPte)->u.Hard.Valid == 0) {
  1320. MiMakeSystemAddressValidPfn (PointerPte, OldIrql);
  1321. }
  1322. }
  1323. if (!NT_SUCCESS(IoStatus.Status)) {
  1324. if ((MmIsRetryIoStatus(IoStatus.Status)) &&
  1325. (MaxClusterSize != 1) &&
  1326. (Mdl->ByteCount > PAGE_SIZE)) {
  1327. //
  1328. // Retried I/O of a cluster have failed, reissue
  1329. // the cluster one page at a time as the
  1330. // storage stack should always be able to
  1331. // make forward progress this way.
  1332. //
  1333. ASSERT (FirstWritten != NULL);
  1334. ASSERT (LastWritten != NULL);
  1335. ASSERT (FirstWritten != LastWritten);
  1336. IoStatus.Information = 0;
  1337. while (Page < LastPage) {
  1338. Pfn2 = MI_PFN_ELEMENT (*Page);
  1339. //
  1340. // Mark the page dirty again so it can be rewritten.
  1341. //
  1342. MI_SET_MODIFIED (Pfn2, 1, 0xE);
  1343. MI_REMOVE_LOCKED_PAGE_CHARGE_AND_DECREF(Pfn2, 21);
  1344. Page += 1;
  1345. }
  1346. PointerPte = FirstWritten;
  1347. LastWritten = NULL;
  1348. MaxClusterSize = 1;
  1349. goto restartchunk;
  1350. }
  1351. }
  1352. //
  1353. // I/O complete unlock pages.
  1354. //
  1355. // NOTE that the error status is ignored.
  1356. //
  1357. while (Page < LastPage) {
  1358. Pfn2 = MI_PFN_ELEMENT (*Page);
  1359. //
  1360. // Make sure the page is still transition.
  1361. //
  1362. WrittenPte = Pfn2->PteAddress;
  1363. MI_REMOVE_LOCKED_PAGE_CHARGE_AND_DECREF (Pfn2, 23);
  1364. if (!MI_IS_PFN_DELETED (Pfn2)) {
  1365. //
  1366. // Make sure the prototype PTE is
  1367. // still in the working set.
  1368. //
  1369. if (MiGetPteAddress (WrittenPte)->u.Hard.Valid == 0) {
  1370. MiMakeSystemAddressValidPfn (WrittenPte, OldIrql);
  1371. }
  1372. if (Pfn2->PteAddress != WrittenPte) {
  1373. //
  1374. // The PFN lock was released to make the
  1375. // page table page valid, and while it
  1376. // was released, the physical page
  1377. // was reused. Go onto the next one.
  1378. //
  1379. Page += 1;
  1380. continue;
  1381. }
  1382. WrittenContents = *WrittenPte;
  1383. if ((WrittenContents.u.Soft.Prototype == 0) &&
  1384. (WrittenContents.u.Soft.Transition == 1)) {
  1385. MI_SET_PFN_DELETED (Pfn2);
  1386. ControlArea->NumberOfPfnReferences -= 1;
  1387. ASSERT ((LONG)ControlArea->NumberOfPfnReferences >= 0);
  1388. PageTableFrameIndex = Pfn2->u4.PteFrame;
  1389. Pfn3 = MI_PFN_ELEMENT (PageTableFrameIndex);
  1390. MiDecrementShareCountInline (Pfn3, PageTableFrameIndex);
  1391. //
  1392. // Check the reference count for the page,
  1393. // if the reference count is zero and the
  1394. // page is not on the freelist, move the page
  1395. // to the free list, if the reference
  1396. // count is not zero, ignore this page.
  1397. // When the reference count goes to zero,
  1398. // it will be placed on the free list.
  1399. //
  1400. if ((Pfn2->u3.e2.ReferenceCount == 0) &&
  1401. (Pfn2->u3.e1.PageLocation != FreePageList)) {
  1402. MiUnlinkPageFromList (Pfn2);
  1403. MiReleasePageFileSpace (Pfn2->OriginalPte);
  1404. MiInsertPageInFreeList (*Page);
  1405. }
  1406. }
  1407. WrittenPte->u.Long = 0;
  1408. }
  1409. Page += 1;
  1410. }
  1411. //
  1412. // Indicate that there is no current cluster being built.
  1413. //
  1414. LastWritten = NULL;
  1415. }
  1416. } // end while
  1417. //
  1418. // Get the next subsection if any.
  1419. //
  1420. if (Subsection->NextSubsection == NULL) {
  1421. break;
  1422. }
  1423. Subsection = Subsection->NextSubsection;
  1424. if (ImageSection == FALSE) {
  1425. while (Subsection->SubsectionBase == NULL) {
  1426. Subsection = Subsection->NextSubsection;
  1427. if (Subsection == NULL) {
  1428. goto alldone;
  1429. }
  1430. }
  1431. }
  1432. PointerPte = Subsection->SubsectionBase;
  1433. LastPte = PointerPte + Subsection->PtesInSubsection;
  1434. } // end for
  1435. alldone:
  1436. ControlArea->NumberOfMappedViews = 0;
  1437. ASSERT (ControlArea->NumberOfPfnReferences == 0);
  1438. if (ControlArea->u.Flags.FilePointerNull == 0) {
  1439. ControlArea->u.Flags.FilePointerNull = 1;
  1440. if (ControlArea->u.Flags.Image) {
  1441. MiRemoveImageSectionObject (ControlArea->FilePointer, ControlArea);
  1442. }
  1443. else {
  1444. ASSERT (((PCONTROL_AREA)(ControlArea->FilePointer->SectionObjectPointer->DataSectionObject)) != NULL);
  1445. ControlArea->FilePointer->SectionObjectPointer->DataSectionObject = NULL;
  1446. }
  1447. }
  1448. UNLOCK_PFN (OldIrql);
  1449. //
  1450. // Delete the segment structure.
  1451. //
  1452. MiSegmentDelete (ControlArea->Segment);
  1453. return;
  1454. }
  1455. NTSTATUS
  1456. MmGetFileNameForSection (
  1457. IN PSECTION SectionObject,
  1458. OUT PSTRING FileName
  1459. )
  1460. /*++
  1461. Routine Description:
  1462. This function returns the file name for the corresponding section.
  1463. Arguments:
  1464. SectionObject - Supplies the section to get the name of.
  1465. FileName - Returns the name of the corresponding section.
  1466. Return Value:
  1467. TBS
  1468. Environment:
  1469. Kernel mode, APC_LEVEL or below, no mutexes held.
  1470. --*/
  1471. {
  1472. POBJECT_NAME_INFORMATION FileNameInfo;
  1473. ULONG whocares;
  1474. NTSTATUS Status;
  1475. #define xMAX_NAME 1024
  1476. if (SectionObject->u.Flags.Image == 0) {
  1477. return STATUS_SECTION_NOT_IMAGE;
  1478. }
  1479. FileNameInfo = ExAllocatePoolWithTag (PagedPool, xMAX_NAME, ' mM');
  1480. if ( !FileNameInfo ) {
  1481. return STATUS_NO_MEMORY;
  1482. }
  1483. Status = ObQueryNameString(
  1484. SectionObject->Segment->ControlArea->FilePointer,
  1485. FileNameInfo,
  1486. xMAX_NAME,
  1487. &whocares
  1488. );
  1489. if ( !NT_SUCCESS(Status) ) {
  1490. ExFreePool(FileNameInfo);
  1491. return Status;
  1492. }
  1493. FileName->Length = 0;
  1494. FileName->MaximumLength = (USHORT)((FileNameInfo->Name.Length/sizeof(WCHAR)) + 1);
  1495. FileName->Buffer = ExAllocatePoolWithTag (PagedPool,
  1496. FileName->MaximumLength,
  1497. ' mM');
  1498. if (!FileName->Buffer) {
  1499. ExFreePool(FileNameInfo);
  1500. return STATUS_NO_MEMORY;
  1501. }
  1502. RtlUnicodeStringToAnsiString ((PANSI_STRING)FileName,
  1503. &FileNameInfo->Name,FALSE);
  1504. FileName->Buffer[FileName->Length] = '\0';
  1505. ExFreePool(FileNameInfo);
  1506. return STATUS_SUCCESS;
  1507. }
  1508. NTSTATUS
  1509. MmGetFileNameForAddress (
  1510. IN PVOID ProcessVa,
  1511. OUT PUNICODE_STRING FileName
  1512. )
  1513. /*++
  1514. Routine Description:
  1515. This function returns the file name for the corresponding process address if it corresponds to an image section.
  1516. Arguments:
  1517. ProcessVa - Process virtual address
  1518. FileName - Returns the name of the corresponding section.
  1519. Return Value:
  1520. NTSTATUS - Status of operation
  1521. Environment:
  1522. Kernel mode, APC_LEVEL or below, no mutexes held.
  1523. --*/
  1524. {
  1525. PMMVAD Vad;
  1526. PFILE_OBJECT FileObject;
  1527. PCONTROL_AREA ControlArea;
  1528. NTSTATUS Status;
  1529. ULONG RetLen;
  1530. ULONG BufLen;
  1531. PEPROCESS Process;
  1532. POBJECT_NAME_INFORMATION FileNameInfo;
  1533. PAGED_CODE ();
  1534. Process = PsGetCurrentProcess();
  1535. LOCK_ADDRESS_SPACE (Process);
  1536. Vad = MiLocateAddress (ProcessVa);
  1537. if (Vad == NULL) {
  1538. //
  1539. // No virtual address is allocated at the specified base address,
  1540. // return an error.
  1541. //
  1542. Status = STATUS_INVALID_ADDRESS;
  1543. goto ErrorReturn;
  1544. }
  1545. //
  1546. // Reject private memory.
  1547. //
  1548. if (Vad->u.VadFlags.PrivateMemory == 1) {
  1549. Status = STATUS_SECTION_NOT_IMAGE;
  1550. goto ErrorReturn;
  1551. }
  1552. ControlArea = Vad->ControlArea;
  1553. if (ControlArea == NULL) {
  1554. Status = STATUS_SECTION_NOT_IMAGE;
  1555. goto ErrorReturn;
  1556. }
  1557. //
  1558. // Reject non-image sections.
  1559. //
  1560. if (ControlArea->u.Flags.Image == 0) {
  1561. Status = STATUS_SECTION_NOT_IMAGE;
  1562. goto ErrorReturn;
  1563. }
  1564. FileObject = ControlArea->FilePointer;
  1565. ASSERT (FileObject != NULL);
  1566. ObReferenceObject (FileObject);
  1567. UNLOCK_ADDRESS_SPACE (Process);
  1568. //
  1569. // Pick an initial size big enough for most reasonable files.
  1570. //
  1571. BufLen = sizeof (*FileNameInfo) + 1024;
  1572. do {
  1573. FileNameInfo = ExAllocatePoolWithTag (PagedPool, BufLen, ' mM');
  1574. if (FileNameInfo == NULL) {
  1575. Status = STATUS_NO_MEMORY;
  1576. break;
  1577. }
  1578. RetLen = 0;
  1579. Status = ObQueryNameString (FileObject, FileNameInfo, BufLen, &RetLen);
  1580. if (NT_SUCCESS (Status)) {
  1581. FileName->Length = FileName->MaximumLength = FileNameInfo->Name.Length;
  1582. FileName->Buffer = (PWCHAR) FileNameInfo;
  1583. RtlMoveMemory (FileName->Buffer, FileNameInfo->Name.Buffer, FileName->Length);
  1584. }
  1585. else {
  1586. ExFreePool (FileNameInfo);
  1587. if (RetLen > BufLen) {
  1588. BufLen = RetLen;
  1589. continue;
  1590. }
  1591. }
  1592. break;
  1593. } while (TRUE);
  1594. ObDereferenceObject (FileObject);
  1595. return Status;
  1596. ErrorReturn:
  1597. UNLOCK_ADDRESS_SPACE (Process);
  1598. return Status;
  1599. }
  1600. PFILE_OBJECT
  1601. MmGetFileObjectForSection (
  1602. IN PVOID Section
  1603. )
  1604. /*++
  1605. Routine Description:
  1606. This routine returns a pointer to the file object backing a section object.
  1607. Arguments:
  1608. Section - Supplies the section to query.
  1609. Return Value:
  1610. A pointer to the file object backing the argument section.
  1611. Environment:
  1612. Kernel mode, PASSIVE_LEVEL.
  1613. The caller must ensure that the section is valid for the
  1614. duration of the call.
  1615. --*/
  1616. {
  1617. PFILE_OBJECT FileObject;
  1618. ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
  1619. ASSERT (Section != NULL);
  1620. FileObject = ((PSECTION)Section)->Segment->ControlArea->FilePointer;
  1621. return FileObject;
  1622. }
  1623. VOID
  1624. MiCheckControlArea (
  1625. IN PCONTROL_AREA ControlArea,
  1626. IN PEPROCESS CurrentProcess,
  1627. IN KIRQL PreviousIrql
  1628. )
  1629. /*++
  1630. Routine Description:
  1631. This routine checks the reference counts for the specified
  1632. control area, and if the counts are all zero, it marks the
  1633. control area for deletion and queues it to the deletion thread.
  1634. *********************** NOTE ********************************
  1635. This routine returns with the PFN LOCK RELEASED!!!!!
  1636. Arguments:
  1637. ControlArea - Supplies a pointer to the control area to check.
  1638. CurrentProcess - Supplies a pointer to the current process if and ONLY
  1639. IF the working set lock is held.
  1640. PreviousIrql - Supplies the previous IRQL.
  1641. Return Value:
  1642. NONE.
  1643. Environment:
  1644. Kernel mode, PFN lock held, PFN lock released upon return!!!
  1645. --*/
  1646. {
  1647. PEVENT_COUNTER PurgeEvent;
  1648. #define DELETE_ON_CLOSE 0x1
  1649. #define DEREF_SEGMENT 0x2
  1650. ULONG Action;
  1651. Action = 0;
  1652. PurgeEvent = NULL;
  1653. MM_PFN_LOCK_ASSERT();
  1654. if ((ControlArea->NumberOfMappedViews == 0) &&
  1655. (ControlArea->NumberOfSectionReferences == 0)) {
  1656. ASSERT (ControlArea->NumberOfUserReferences == 0);
  1657. if (ControlArea->FilePointer != NULL) {
  1658. if (ControlArea->NumberOfPfnReferences == 0) {
  1659. //
  1660. // There are no views and no physical pages referenced
  1661. // by the Segment, dereference the Segment object.
  1662. //
  1663. ControlArea->u.Flags.BeingDeleted = 1;
  1664. Action |= DEREF_SEGMENT;
  1665. ASSERT (ControlArea->u.Flags.FilePointerNull == 0);
  1666. ControlArea->u.Flags.FilePointerNull = 1;
  1667. if (ControlArea->u.Flags.Image) {
  1668. MiRemoveImageSectionObject (ControlArea->FilePointer, ControlArea);
  1669. }
  1670. else {
  1671. ASSERT (((PCONTROL_AREA)(ControlArea->FilePointer->SectionObjectPointer->DataSectionObject)) != NULL);
  1672. ControlArea->FilePointer->SectionObjectPointer->DataSectionObject = NULL;
  1673. }
  1674. }
  1675. else {
  1676. //
  1677. // Insert this segment into the unused segment list (unless
  1678. // it is already on the list).
  1679. //
  1680. if (ControlArea->DereferenceList.Flink == NULL) {
  1681. MI_INSERT_UNUSED_SEGMENT (ControlArea);
  1682. }
  1683. //
  1684. // Indicate if this section should be deleted now that
  1685. // the reference counts are zero.
  1686. //
  1687. if (ControlArea->u.Flags.DeleteOnClose) {
  1688. Action |= DELETE_ON_CLOSE;
  1689. }
  1690. //
  1691. // The number of mapped views are zero, the number of
  1692. // section references are zero, but there are some
  1693. // pages of the file still resident. If this is
  1694. // an image with Global Memory, "purge" the subsections
  1695. // which contain the global memory and reset them to
  1696. // point back to the file.
  1697. //
  1698. if (ControlArea->u.Flags.GlobalMemory == 1) {
  1699. ASSERT (ControlArea->u.Flags.Image == 1);
  1700. ControlArea->u.Flags.BeingPurged = 1;
  1701. ControlArea->NumberOfMappedViews = 1;
  1702. MiPurgeImageSection (ControlArea, CurrentProcess, PreviousIrql);
  1703. ControlArea->u.Flags.BeingPurged = 0;
  1704. ControlArea->NumberOfMappedViews -= 1;
  1705. if ((ControlArea->NumberOfMappedViews == 0) &&
  1706. (ControlArea->NumberOfSectionReferences == 0) &&
  1707. (ControlArea->NumberOfPfnReferences == 0)) {
  1708. ControlArea->u.Flags.BeingDeleted = 1;
  1709. Action |= DEREF_SEGMENT;
  1710. ControlArea->u.Flags.FilePointerNull = 1;
  1711. MiRemoveImageSectionObject (ControlArea->FilePointer,
  1712. ControlArea);
  1713. }
  1714. else {
  1715. PurgeEvent = ControlArea->WaitingForDeletion;
  1716. ControlArea->WaitingForDeletion = NULL;
  1717. }
  1718. }
  1719. //
  1720. // If delete on close is set and the segment was
  1721. // not deleted, up the count of mapped views so the
  1722. // control area will not be deleted when the PFN lock
  1723. // is released.
  1724. //
  1725. if (Action == DELETE_ON_CLOSE) {
  1726. ControlArea->NumberOfMappedViews = 1;
  1727. ControlArea->u.Flags.BeingDeleted = 1;
  1728. }
  1729. }
  1730. }
  1731. else {
  1732. //
  1733. // This Segment is backed by a paging file, dereference the
  1734. // Segment object when the number of views goes from 1 to 0
  1735. // without regard to the number of PFN references.
  1736. //
  1737. ControlArea->u.Flags.BeingDeleted = 1;
  1738. Action |= DEREF_SEGMENT;
  1739. }
  1740. }
  1741. else if (ControlArea->WaitingForDeletion != NULL) {
  1742. PurgeEvent = ControlArea->WaitingForDeletion;
  1743. ControlArea->WaitingForDeletion = NULL;
  1744. }
  1745. UNLOCK_PFN (PreviousIrql);
  1746. if (Action != 0) {
  1747. //
  1748. // Release the working set mutex, if it is held as the object
  1749. // management routines may page fault, etc..
  1750. //
  1751. if (CurrentProcess) {
  1752. UNLOCK_WS_UNSAFE (CurrentProcess);
  1753. }
  1754. ASSERT (ControlArea->Segment->WritableUserReferences == 0);
  1755. if (Action & DEREF_SEGMENT) {
  1756. //
  1757. // Delete the segment.
  1758. //
  1759. MiSegmentDelete (ControlArea->Segment);
  1760. }
  1761. else {
  1762. //
  1763. // The segment should be forced closed now.
  1764. //
  1765. MiCleanSection (ControlArea, TRUE);
  1766. }
  1767. ASSERT (PurgeEvent == NULL);
  1768. //
  1769. // Reacquire the working set lock, if a process was specified.
  1770. //
  1771. if (CurrentProcess) {
  1772. LOCK_WS_UNSAFE (CurrentProcess);
  1773. }
  1774. }
  1775. else {
  1776. //
  1777. // If any threads are waiting for the segment, indicate the
  1778. // the purge operation has completed.
  1779. //
  1780. if (PurgeEvent != NULL) {
  1781. KeSetEvent (&PurgeEvent->Event, 0, FALSE);
  1782. }
  1783. if (MI_UNUSED_SEGMENTS_SURPLUS()) {
  1784. KeSetEvent (&MmUnusedSegmentCleanup, 0, FALSE);
  1785. }
  1786. }
  1787. return;
  1788. }
  1789. VOID
  1790. MiCheckForControlAreaDeletion (
  1791. IN PCONTROL_AREA ControlArea
  1792. )
  1793. /*++
  1794. Routine Description:
  1795. This routine checks the reference counts for the specified
  1796. control area, and if the counts are all zero, it marks the
  1797. control area for deletion and queues it to the deletion thread.
  1798. Arguments:
  1799. ControlArea - Supplies a pointer to the control area to check.
  1800. Return Value:
  1801. None.
  1802. Environment:
  1803. Kernel mode, PFN lock held.
  1804. --*/
  1805. {
  1806. KIRQL OldIrql;
  1807. MM_PFN_LOCK_ASSERT();
  1808. if ((ControlArea->NumberOfPfnReferences == 0) &&
  1809. (ControlArea->NumberOfMappedViews == 0) &&
  1810. (ControlArea->NumberOfSectionReferences == 0 )) {
  1811. //
  1812. // This segment is no longer mapped in any address space
  1813. // nor are there any prototype PTEs within the segment
  1814. // which are valid or in a transition state. Queue
  1815. // the segment to the segment-dereferencer thread
  1816. // which will dereference the segment object, potentially
  1817. // causing the segment to be deleted.
  1818. //
  1819. ControlArea->u.Flags.BeingDeleted = 1;
  1820. ASSERT (ControlArea->u.Flags.FilePointerNull == 0);
  1821. ControlArea->u.Flags.FilePointerNull = 1;
  1822. if (ControlArea->u.Flags.Image) {
  1823. MiRemoveImageSectionObject (ControlArea->FilePointer,
  1824. ControlArea);
  1825. }
  1826. else {
  1827. ControlArea->FilePointer->SectionObjectPointer->DataSectionObject =
  1828. NULL;
  1829. }
  1830. ExAcquireSpinLock (&MmDereferenceSegmentHeader.Lock, &OldIrql);
  1831. if (ControlArea->DereferenceList.Flink != NULL) {
  1832. //
  1833. // Remove the entry from the unused segment list and put it
  1834. // on the dereference list.
  1835. //
  1836. RemoveEntryList (&ControlArea->DereferenceList);
  1837. MI_UNUSED_SEGMENTS_REMOVE_CHARGE (ControlArea);
  1838. }
  1839. //
  1840. // Image sections still have useful header information in their segment
  1841. // even if no pages are valid or transition so put these at the tail.
  1842. // Data sections have nothing of use if all the data pages are gone so
  1843. // we used to put those at the front. Now both types go to the rear
  1844. // so that commit extensions go to the front for earlier processing.
  1845. //
  1846. InsertTailList (&MmDereferenceSegmentHeader.ListHead,
  1847. &ControlArea->DereferenceList);
  1848. ExReleaseSpinLock (&MmDereferenceSegmentHeader.Lock, OldIrql);
  1849. KeReleaseSemaphore (&MmDereferenceSegmentHeader.Semaphore,
  1850. 0L,
  1851. 1L,
  1852. FALSE);
  1853. }
  1854. return;
  1855. }
  1856. LOGICAL
  1857. MiCheckControlAreaStatus (
  1858. IN SECTION_CHECK_TYPE SectionCheckType,
  1859. IN PSECTION_OBJECT_POINTERS SectionObjectPointers,
  1860. IN ULONG DelayClose,
  1861. OUT PCONTROL_AREA *ControlAreaOut,
  1862. OUT PKIRQL PreviousIrql
  1863. )
  1864. /*++
  1865. Routine Description:
  1866. This routine checks the status of the control area for the specified
  1867. SectionObjectPointers. If the control area is in use, that is, the
  1868. number of section references and the number of mapped views are not
  1869. both zero, no action is taken and the function returns FALSE.
  1870. If there is no control area associated with the specified
  1871. SectionObjectPointers or the control area is in the process of being
  1872. created or deleted, no action is taken and the value TRUE is returned.
  1873. If, there are no section objects and the control area is not being
  1874. created or deleted, the address of the control area is returned
  1875. in the ControlArea argument, the address of a pool block to free
  1876. is returned in the SegmentEventOut argument and the PFN_LOCK is
  1877. still held at the return.
  1878. Arguments:
  1879. *SegmentEventOut - Returns a pointer to NonPaged Pool which much be
  1880. freed by the caller when the PFN_LOCK is released.
  1881. This value is NULL if no pool is allocated and the
  1882. PFN_LOCK is not held.
  1883. SectionCheckType - Supplies the type of section to check on, one of
  1884. CheckImageSection, CheckDataSection, CheckBothSection.
  1885. SectionObjectPointers - Supplies the section object pointers through
  1886. which the control area can be located.
  1887. DelayClose - Supplies a boolean which if TRUE and the control area
  1888. is being used, the delay on close field should be set
  1889. in the control area.
  1890. *ControlAreaOut - Returns the address of the control area.
  1891. PreviousIrql - Returns, in the case the PFN_LOCK is held, the previous
  1892. IRQL so the lock can be released properly.
  1893. Return Value:
  1894. FALSE if the control area is in use, TRUE if the control area is gone or
  1895. in the process or being created or deleted.
  1896. Environment:
  1897. Kernel mode, PFN lock NOT held.
  1898. --*/
  1899. {
  1900. PKTHREAD CurrentThread;
  1901. PEVENT_COUNTER IoEvent;
  1902. PEVENT_COUNTER SegmentEvent;
  1903. LOGICAL DeallocateSegmentEvent;
  1904. PCONTROL_AREA ControlArea;
  1905. ULONG SectRef;
  1906. KIRQL OldIrql;
  1907. //
  1908. // Allocate an event to wait on in case the segment is in the
  1909. // process of being deleted. This event cannot be allocated
  1910. // with the PFN database locked as pool expansion would deadlock.
  1911. //
  1912. *ControlAreaOut = NULL;
  1913. do {
  1914. SegmentEvent = MiGetEventCounter ();
  1915. if (SegmentEvent != NULL) {
  1916. break;
  1917. }
  1918. KeDelayExecutionThread (KernelMode,
  1919. FALSE,
  1920. (PLARGE_INTEGER)&MmShortTime);
  1921. } while (TRUE);
  1922. //
  1923. // Acquire the PFN lock and examine the section object pointer
  1924. // value within the file object.
  1925. //
  1926. // File control blocks live in non-paged pool.
  1927. //
  1928. LOCK_PFN (OldIrql);
  1929. if (SectionCheckType != CheckImageSection) {
  1930. ControlArea = ((PCONTROL_AREA)(SectionObjectPointers->DataSectionObject));
  1931. }
  1932. else {
  1933. ControlArea = ((PCONTROL_AREA)(SectionObjectPointers->ImageSectionObject));
  1934. }
  1935. if (ControlArea == NULL) {
  1936. if (SectionCheckType != CheckBothSection) {
  1937. //
  1938. // This file no longer has an associated segment.
  1939. //
  1940. UNLOCK_PFN (OldIrql);
  1941. MiFreeEventCounter (SegmentEvent);
  1942. return TRUE;
  1943. }
  1944. else {
  1945. ControlArea = ((PCONTROL_AREA)(SectionObjectPointers->ImageSectionObject));
  1946. if (ControlArea == NULL) {
  1947. //
  1948. // This file no longer has an associated segment.
  1949. //
  1950. UNLOCK_PFN (OldIrql);
  1951. MiFreeEventCounter (SegmentEvent);
  1952. return TRUE;
  1953. }
  1954. }
  1955. }
  1956. //
  1957. // Depending on the type of section, check for the pertinent
  1958. // reference count being non-zero.
  1959. //
  1960. if (SectionCheckType != CheckUserDataSection) {
  1961. SectRef = ControlArea->NumberOfSectionReferences;
  1962. }
  1963. else {
  1964. SectRef = ControlArea->NumberOfUserReferences;
  1965. }
  1966. if ((SectRef != 0) ||
  1967. (ControlArea->NumberOfMappedViews != 0) ||
  1968. (ControlArea->u.Flags.BeingCreated)) {
  1969. //
  1970. // The segment is currently in use or being created.
  1971. //
  1972. if (DelayClose) {
  1973. //
  1974. // The section should be deleted when the reference
  1975. // counts are zero, set the delete on close flag.
  1976. //
  1977. ControlArea->u.Flags.DeleteOnClose = 1;
  1978. }
  1979. UNLOCK_PFN (OldIrql);
  1980. MiFreeEventCounter (SegmentEvent);
  1981. return FALSE;
  1982. }
  1983. //
  1984. // The segment has no references, delete it. If the segment
  1985. // is already being deleted, set the event field in the control
  1986. // area and wait on the event.
  1987. //
  1988. if (ControlArea->u.Flags.BeingDeleted) {
  1989. //
  1990. // The segment object is in the process of being deleted.
  1991. // Check to see if another thread is waiting for the deletion,
  1992. // otherwise create and event object to wait upon.
  1993. //
  1994. if (ControlArea->WaitingForDeletion == NULL) {
  1995. //
  1996. // Create an event and put its address in the control area.
  1997. //
  1998. DeallocateSegmentEvent = FALSE;
  1999. ControlArea->WaitingForDeletion = SegmentEvent;
  2000. IoEvent = SegmentEvent;
  2001. }
  2002. else {
  2003. DeallocateSegmentEvent = TRUE;
  2004. IoEvent = ControlArea->WaitingForDeletion;
  2005. //
  2006. // No interlock is needed for the RefCount increment as
  2007. // no thread can be decrementing it since it is still
  2008. // pointed to by the control area.
  2009. //
  2010. IoEvent->RefCount += 1;
  2011. }
  2012. //
  2013. // Release the mutex and wait for the event.
  2014. //
  2015. CurrentThread = KeGetCurrentThread ();
  2016. KeEnterCriticalRegionThread (CurrentThread);
  2017. UNLOCK_PFN_AND_THEN_WAIT(OldIrql);
  2018. KeWaitForSingleObject(&IoEvent->Event,
  2019. WrPageOut,
  2020. KernelMode,
  2021. FALSE,
  2022. (PLARGE_INTEGER)NULL);
  2023. //
  2024. // Before this event can be set, the control area
  2025. // WaitingForDeletion field must be cleared (and may be
  2026. // reinitialized to something else), but cannot be reset
  2027. // to our local event. This allows us to dereference the
  2028. // event count lock free.
  2029. //
  2030. #if 0
  2031. //
  2032. // Note that the control area cannot be referenced at this
  2033. // point because it may have been freed.
  2034. //
  2035. ASSERT (IoEvent != ControlArea->WaitingForDeletion);
  2036. #endif
  2037. KeLeaveCriticalRegionThread (CurrentThread);
  2038. MiFreeEventCounter (IoEvent);
  2039. if (DeallocateSegmentEvent == TRUE) {
  2040. MiFreeEventCounter (SegmentEvent);
  2041. }
  2042. return TRUE;
  2043. }
  2044. //
  2045. // Return with the PFN database locked.
  2046. //
  2047. ASSERT (SegmentEvent->RefCount == 1);
  2048. ASSERT (SegmentEvent->ListEntry.Next == NULL);
  2049. //
  2050. // NO interlock is needed for the RefCount clearing as the event counter
  2051. // was never pointed to by a control area.
  2052. //
  2053. #if DBG
  2054. SegmentEvent->RefCount = 0;
  2055. #endif
  2056. InterlockedPushEntrySList (&MmEventCountSListHead,
  2057. (PSLIST_ENTRY)&SegmentEvent->ListEntry);
  2058. *ControlAreaOut = ControlArea;
  2059. *PreviousIrql = OldIrql;
  2060. return FALSE;
  2061. }
  2062. PEVENT_COUNTER
  2063. MiGetEventCounter (
  2064. VOID
  2065. )
  2066. /*++
  2067. Routine Description:
  2068. This function maintains a list of "events" to allow waiting
  2069. on segment operations (deletion, creation, purging).
  2070. Arguments:
  2071. None.
  2072. Return Value:
  2073. Event to be used for waiting (stored into the control area) or NULL if
  2074. no event could be allocated.
  2075. Environment:
  2076. Kernel mode, APC_LEVEL or below.
  2077. --*/
  2078. {
  2079. PSLIST_ENTRY SingleListEntry;
  2080. PEVENT_COUNTER Support;
  2081. ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
  2082. if (ExQueryDepthSList (&MmEventCountSListHead) != 0) {
  2083. SingleListEntry = InterlockedPopEntrySList (&MmEventCountSListHead);
  2084. if (SingleListEntry != NULL) {
  2085. Support = CONTAINING_RECORD (SingleListEntry,
  2086. EVENT_COUNTER,
  2087. ListEntry);
  2088. ASSERT (Support->RefCount == 0);
  2089. KeClearEvent (&Support->Event);
  2090. Support->RefCount = 1;
  2091. #if DBG
  2092. Support->ListEntry.Next = NULL;
  2093. #endif
  2094. return Support;
  2095. }
  2096. }
  2097. Support = ExAllocatePoolWithTag (NonPagedPool,
  2098. sizeof(EVENT_COUNTER),
  2099. 'xEmM');
  2100. if (Support == NULL) {
  2101. return NULL;
  2102. }
  2103. KeInitializeEvent (&Support->Event, NotificationEvent, FALSE);
  2104. Support->RefCount = 1;
  2105. #if DBG
  2106. Support->ListEntry.Next = NULL;
  2107. #endif
  2108. return Support;
  2109. }
  2110. VOID
  2111. MiFreeEventCounter (
  2112. IN PEVENT_COUNTER Support
  2113. )
  2114. /*++
  2115. Routine Description:
  2116. This routine frees an event counter back to the free list.
  2117. Arguments:
  2118. Support - Supplies a pointer to the event counter.
  2119. Return Value:
  2120. None.
  2121. Environment:
  2122. Kernel mode, APC_LEVEL or below.
  2123. --*/
  2124. {
  2125. PSLIST_ENTRY SingleListEntry;
  2126. ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
  2127. ASSERT (Support->RefCount != 0);
  2128. ASSERT (Support->ListEntry.Next == NULL);
  2129. //
  2130. // An interlock is needed for the RefCount decrement as the event counter
  2131. // is no longer pointed to by a control area and thus, any number of
  2132. // threads can be running this code without any other serialization.
  2133. //
  2134. if (InterlockedDecrement ((PLONG)&Support->RefCount) == 0) {
  2135. if (ExQueryDepthSList (&MmEventCountSListHead) < 4) {
  2136. InterlockedPushEntrySList (&MmEventCountSListHead,
  2137. &Support->ListEntry);
  2138. return;
  2139. }
  2140. ExFreePool (Support);
  2141. }
  2142. //
  2143. // If excess event blocks are stashed then free them now.
  2144. //
  2145. while (ExQueryDepthSList (&MmEventCountSListHead) > 4) {
  2146. SingleListEntry = InterlockedPopEntrySList (&MmEventCountSListHead);
  2147. if (SingleListEntry != NULL) {
  2148. Support = CONTAINING_RECORD (SingleListEntry,
  2149. EVENT_COUNTER,
  2150. ListEntry);
  2151. ExFreePool (Support);
  2152. }
  2153. }
  2154. return;
  2155. }
  2156. BOOLEAN
  2157. MmCanFileBeTruncated (
  2158. IN PSECTION_OBJECT_POINTERS SectionPointer,
  2159. IN PLARGE_INTEGER NewFileSize
  2160. )
  2161. /*++
  2162. Routine Description:
  2163. This routine does the following:
  2164. 1. Checks to see if a image section is in use for the file,
  2165. if so it returns FALSE.
  2166. 2. Checks to see if a user section exists for the file, if
  2167. it does, it checks to make sure the new file size is greater
  2168. than the size of the file, if not it returns FALSE.
  2169. 3. If no image section exists, and no user created data section
  2170. exists or the file's size is greater, then TRUE is returned.
  2171. Arguments:
  2172. SectionPointer - Supplies a pointer to the section object pointers
  2173. from the file object.
  2174. NewFileSize - Supplies a pointer to the size the file is getting set to.
  2175. Return Value:
  2176. TRUE if the file can be truncated, FALSE if it cannot be.
  2177. Environment:
  2178. Kernel mode.
  2179. --*/
  2180. {
  2181. LARGE_INTEGER LocalOffset;
  2182. KIRQL OldIrql;
  2183. //
  2184. // Capture caller's file size, since we may modify it.
  2185. //
  2186. if (ARGUMENT_PRESENT(NewFileSize)) {
  2187. LocalOffset = *NewFileSize;
  2188. NewFileSize = &LocalOffset;
  2189. }
  2190. if (MiCanFileBeTruncatedInternal( SectionPointer, NewFileSize, FALSE, &OldIrql )) {
  2191. UNLOCK_PFN (OldIrql);
  2192. return TRUE;
  2193. }
  2194. return FALSE;
  2195. }
  2196. ULONG
  2197. MiCanFileBeTruncatedInternal (
  2198. IN PSECTION_OBJECT_POINTERS SectionPointer,
  2199. IN PLARGE_INTEGER NewFileSize OPTIONAL,
  2200. IN LOGICAL BlockNewViews,
  2201. OUT PKIRQL PreviousIrql
  2202. )
  2203. /*++
  2204. Routine Description:
  2205. This routine does the following:
  2206. 1. Checks to see if a image section is in use for the file,
  2207. if so it returns FALSE.
  2208. 2. Checks to see if a user section exists for the file, if
  2209. it does, it checks to make sure the new file size is greater
  2210. than the size of the file, if not it returns FALSE.
  2211. 3. If no image section exists, and no user created data section
  2212. exists or the files size is greater, then TRUE is returned.
  2213. Arguments:
  2214. SectionPointer - Supplies a pointer to the section object pointers
  2215. from the file object.
  2216. NewFileSize - Supplies a pointer to the size the file is getting set to.
  2217. BlockNewViews - Supplies TRUE if the caller will block new views while
  2218. the operation (usually a purge) proceeds. This allows
  2219. this routine to return TRUE even if the user has section
  2220. references, provided the user currently has no mapped views.
  2221. PreviousIrql - If returning TRUE, returns Irql to use when unlocking
  2222. Pfn database.
  2223. Return Value:
  2224. TRUE if the file can be truncated (PFN locked).
  2225. FALSE if it cannot be truncated (PFN not locked).
  2226. Environment:
  2227. Kernel mode.
  2228. --*/
  2229. {
  2230. KIRQL OldIrql;
  2231. LARGE_INTEGER SegmentSize;
  2232. PCONTROL_AREA ControlArea;
  2233. PSUBSECTION Subsection;
  2234. PMAPPED_FILE_SEGMENT Segment;
  2235. if (!MmFlushImageSection (SectionPointer, MmFlushForWrite)) {
  2236. return FALSE;
  2237. }
  2238. LOCK_PFN (OldIrql);
  2239. ControlArea = (PCONTROL_AREA)(SectionPointer->DataSectionObject);
  2240. if (ControlArea != NULL) {
  2241. if ((ControlArea->u.Flags.BeingCreated) ||
  2242. (ControlArea->u.Flags.BeingDeleted) ||
  2243. (ControlArea->u.Flags.Rom)) {
  2244. goto UnlockAndReturn;
  2245. }
  2246. //
  2247. // If there are user references and the size is less than the
  2248. // size of the user view, don't allow the truncation.
  2249. //
  2250. if ((ControlArea->NumberOfUserReferences != 0) &&
  2251. ((BlockNewViews == FALSE) || (ControlArea->NumberOfMappedViews != 0))) {
  2252. //
  2253. // You cannot truncate the entire section if there is a user
  2254. // reference.
  2255. //
  2256. if (!ARGUMENT_PRESENT(NewFileSize)) {
  2257. goto UnlockAndReturn;
  2258. }
  2259. //
  2260. // Locate last subsection and get total size.
  2261. //
  2262. ASSERT (ControlArea->u.Flags.Image == 0);
  2263. ASSERT (ControlArea->u.Flags.GlobalOnlyPerSession == 0);
  2264. Subsection = (PSUBSECTION)(ControlArea + 1);
  2265. if (ControlArea->FilePointer != NULL) {
  2266. Segment = (PMAPPED_FILE_SEGMENT) ControlArea->Segment;
  2267. if (MiIsAddressValid (Segment, TRUE)) {
  2268. if (Segment->LastSubsectionHint != NULL) {
  2269. Subsection = (PSUBSECTION) Segment->LastSubsectionHint;
  2270. }
  2271. }
  2272. }
  2273. while (Subsection->NextSubsection != NULL) {
  2274. Subsection = Subsection->NextSubsection;
  2275. }
  2276. ASSERT (Subsection->ControlArea == ControlArea);
  2277. SegmentSize = MiEndingOffset(Subsection);
  2278. if ((UINT64)NewFileSize->QuadPart < (UINT64)SegmentSize.QuadPart) {
  2279. goto UnlockAndReturn;
  2280. }
  2281. //
  2282. // If there are mapped views, we will skip the last page
  2283. // of the section if the size passed in falls in that page.
  2284. // The caller (like Cc) may want to clear this fractional page.
  2285. //
  2286. SegmentSize.QuadPart += PAGE_SIZE - 1;
  2287. SegmentSize.LowPart &= ~(PAGE_SIZE - 1);
  2288. if ((UINT64)NewFileSize->QuadPart < (UINT64)SegmentSize.QuadPart) {
  2289. *NewFileSize = SegmentSize;
  2290. }
  2291. }
  2292. }
  2293. *PreviousIrql = OldIrql;
  2294. return TRUE;
  2295. UnlockAndReturn:
  2296. UNLOCK_PFN (OldIrql);
  2297. return FALSE;
  2298. }
  2299. PFILE_OBJECT *
  2300. MmPerfUnusedSegmentsEnumerate (
  2301. VOID
  2302. )
  2303. /*++
  2304. Routine Description:
  2305. This routine walks the MmUnusedSegmentList and returns
  2306. a pointer to a pool allocation containing the
  2307. referenced file object pointers.
  2308. Arguments:
  2309. None.
  2310. Return Value:
  2311. Returns a pointer to a NULL terminated pool allocation containing the
  2312. file object pointers from the unused segment list, NULL if the memory
  2313. could not be allocated.
  2314. It is also the responsibility of the caller to dereference each
  2315. file object in the list and then free the returned pool.
  2316. Environment:
  2317. PASSIVE_LEVEL, arbitrary thread context.
  2318. --*/
  2319. {
  2320. KIRQL OldIrql;
  2321. ULONG SegmentCount;
  2322. PFILE_OBJECT *FileObjects;
  2323. PFILE_OBJECT *File;
  2324. PLIST_ENTRY NextEntry;
  2325. PCONTROL_AREA ControlArea;
  2326. ASSERT (KeGetCurrentIrql () == PASSIVE_LEVEL);
  2327. ReAllocate:
  2328. SegmentCount = MmUnusedSegmentCount + 10;
  2329. FileObjects = (PFILE_OBJECT *) ExAllocatePoolWithTag (
  2330. NonPagedPool,
  2331. SegmentCount * sizeof(PFILE_OBJECT),
  2332. '01pM');
  2333. if (FileObjects == NULL) {
  2334. return NULL;
  2335. }
  2336. File = FileObjects;
  2337. LOCK_PFN (OldIrql);
  2338. //
  2339. // Leave space for NULL terminator.
  2340. //
  2341. if (SegmentCount - 1 < MmUnusedSegmentCount) {
  2342. UNLOCK_PFN (OldIrql);
  2343. ExFreePool (FileObjects);
  2344. goto ReAllocate;
  2345. }
  2346. NextEntry = MmUnusedSegmentList.Flink;
  2347. while (NextEntry != &MmUnusedSegmentList) {
  2348. ControlArea = CONTAINING_RECORD (NextEntry,
  2349. CONTROL_AREA,
  2350. DereferenceList);
  2351. *File = ControlArea->FilePointer;
  2352. ObReferenceObject(*File);
  2353. File += 1;
  2354. NextEntry = NextEntry->Flink;
  2355. }
  2356. UNLOCK_PFN (OldIrql);
  2357. *File = NULL;
  2358. return FileObjects;
  2359. }
  2360. #if DBG
  2361. PMSUBSECTION MiActiveSubsection;
  2362. LOGICAL MiRemoveSubsectionsFirst;
  2363. #define MI_DEREF_ACTION_SIZE 64
  2364. ULONG MiDerefActions[MI_DEREF_ACTION_SIZE];
  2365. #define MI_INSTRUMENT_DEREF_ACTION(i) \
  2366. ASSERT (i < MI_DEREF_ACTION_SIZE); \
  2367. MiDerefActions[i] += 1;
  2368. #else
  2369. #define MI_INSTRUMENT_DEREF_ACTION(i)
  2370. #endif
  2371. VOID
  2372. MiRemoveUnusedSegments (
  2373. VOID
  2374. )
  2375. /*++
  2376. Routine Description:
  2377. This routine removes unused segments (no section references,
  2378. no mapped views only PFN references that are in transition state).
  2379. Arguments:
  2380. None.
  2381. Return Value:
  2382. None.
  2383. Environment:
  2384. Kernel mode.
  2385. --*/
  2386. {
  2387. LOGICAL DroppedPfnLock;
  2388. KIRQL OldIrql;
  2389. PLIST_ENTRY NextEntry;
  2390. PCONTROL_AREA ControlArea;
  2391. NTSTATUS Status;
  2392. ULONG ConsecutiveFileLockFailures;
  2393. ULONG ConsecutivePagingIOs;
  2394. PSUBSECTION Subsection;
  2395. PSUBSECTION LastSubsection;
  2396. PSUBSECTION LastSubsectionWithProtos;
  2397. PMSUBSECTION MappedSubsection;
  2398. ULONG NumberOfPtes;
  2399. MMPTE PteContents;
  2400. PMMPTE PointerPte;
  2401. PMMPTE LastPte;
  2402. PMMPTE ProtoPtes;
  2403. PMMPTE ProtoPtes2;
  2404. PMMPTE LastProtoPte;
  2405. PMMPFN Pfn1;
  2406. PMMPFN Pfn2;
  2407. IO_STATUS_BLOCK IoStatus;
  2408. LOGICAL DirtyPagesOk;
  2409. PFN_NUMBER PageFrameIndex;
  2410. PFN_NUMBER PageTableFrameIndex;
  2411. ULONG ForceFree;
  2412. ULONG LoopCount;
  2413. PMMPAGE_FILE_EXPANSION PageExpand;
  2414. LoopCount = 0;
  2415. ConsecutivePagingIOs = 0;
  2416. ConsecutiveFileLockFailures = 0;
  2417. //
  2418. // If overall system pool usage is acceptable, then don't discard
  2419. // any cache.
  2420. //
  2421. while ((MI_UNUSED_SEGMENTS_SURPLUS()) || (MmUnusedSegmentForceFree != 0)) {
  2422. LoopCount += 1;
  2423. MI_INSTRUMENT_DEREF_ACTION(1);
  2424. if ((LoopCount & (64 - 1)) == 0) {
  2425. MI_INSTRUMENT_DEREF_ACTION(2);
  2426. //
  2427. // Periodically delay so the mapped and modified writers get
  2428. // a shot at writing out the pages this (higher priority) thread
  2429. // is releasing.
  2430. //
  2431. ExAcquireSpinLock (&MmDereferenceSegmentHeader.Lock, &OldIrql);
  2432. while (!IsListEmpty (&MmDereferenceSegmentHeader.ListHead)) {
  2433. MiSubsectionActions |= 0x8000000;
  2434. //
  2435. // The list is not empty, see if the first request is for
  2436. // a commit extension and if so, process it now.
  2437. //
  2438. NextEntry = MmDereferenceSegmentHeader.ListHead.Flink;
  2439. ControlArea = CONTAINING_RECORD (NextEntry,
  2440. CONTROL_AREA,
  2441. DereferenceList);
  2442. if (ControlArea->Segment != NULL) {
  2443. MI_INSTRUMENT_DEREF_ACTION(3);
  2444. break;
  2445. }
  2446. PageExpand = (PMMPAGE_FILE_EXPANSION) ControlArea;
  2447. if (PageExpand->RequestedExpansionSize == MI_CONTRACT_PAGEFILES) {
  2448. MI_INSTRUMENT_DEREF_ACTION(4);
  2449. break;
  2450. }
  2451. MI_INSTRUMENT_DEREF_ACTION(5);
  2452. //
  2453. // This is a request to expand the paging files.
  2454. //
  2455. MiSubsectionActions |= 0x10000000;
  2456. RemoveEntryList (NextEntry);
  2457. ExReleaseSpinLock (&MmDereferenceSegmentHeader.Lock, OldIrql);
  2458. MiExtendPagingFiles (PageExpand);
  2459. KeSetEvent (&PageExpand->Event, 0, FALSE);
  2460. ExAcquireSpinLock (&MmDereferenceSegmentHeader.Lock, &OldIrql);
  2461. }
  2462. ExReleaseSpinLock (&MmDereferenceSegmentHeader.Lock, OldIrql);
  2463. //
  2464. // If we are looping without freeing enough pool then
  2465. // signal the cache manager to start unmapping
  2466. // system cache views in an attempt to get back the paged
  2467. // pool containing its prototype PTEs.
  2468. //
  2469. if (LoopCount >= 128) {
  2470. MI_INSTRUMENT_DEREF_ACTION(55);
  2471. if (CcUnmapInactiveViews (50) == TRUE) {
  2472. MI_INSTRUMENT_DEREF_ACTION(56);
  2473. }
  2474. }
  2475. KeDelayExecutionThread (KernelMode, FALSE, (PLARGE_INTEGER)&MmShortTime);
  2476. }
  2477. //
  2478. // Eliminate some of the unused segments which are only
  2479. // kept in memory because they contain transition pages.
  2480. //
  2481. Status = STATUS_SUCCESS;
  2482. LOCK_PFN (OldIrql);
  2483. if ((IsListEmpty(&MmUnusedSegmentList)) &&
  2484. (IsListEmpty(&MmUnusedSubsectionList))) {
  2485. //
  2486. // There is nothing in the list, rewait.
  2487. //
  2488. MI_INSTRUMENT_DEREF_ACTION(6);
  2489. ForceFree = MmUnusedSegmentForceFree;
  2490. MmUnusedSegmentForceFree = 0;
  2491. ASSERT (MmUnusedSegmentCount == 0);
  2492. UNLOCK_PFN (OldIrql);
  2493. //
  2494. // We weren't able to get as many segments or subsections as we
  2495. // wanted. So signal the cache manager to start unmapping
  2496. // system cache views in an attempt to get back the paged
  2497. // pool containing its prototype PTEs. If Cc was able to free
  2498. // any at all, then restart our loop.
  2499. //
  2500. if (CcUnmapInactiveViews (50) == TRUE) {
  2501. LOCK_PFN (OldIrql);
  2502. if (ForceFree > MmUnusedSegmentForceFree) {
  2503. MmUnusedSegmentForceFree = ForceFree;
  2504. }
  2505. MI_INSTRUMENT_DEREF_ACTION(7);
  2506. UNLOCK_PFN (OldIrql);
  2507. continue;
  2508. }
  2509. break;
  2510. }
  2511. MI_INSTRUMENT_DEREF_ACTION(8);
  2512. if (MmUnusedSegmentForceFree != 0) {
  2513. MmUnusedSegmentForceFree -= 1;
  2514. MI_INSTRUMENT_DEREF_ACTION(9);
  2515. }
  2516. #if DBG
  2517. if (MiRemoveSubsectionsFirst == TRUE) {
  2518. if (!IsListEmpty(&MmUnusedSubsectionList)) {
  2519. goto ProcessSubsectionsFirst;
  2520. }
  2521. }
  2522. #endif
  2523. if (IsListEmpty(&MmUnusedSegmentList)) {
  2524. #if DBG
  2525. ProcessSubsectionsFirst:
  2526. #endif
  2527. MI_INSTRUMENT_DEREF_ACTION(10);
  2528. //
  2529. // The unused segment list was empty, go for the unused subsection
  2530. // list instead.
  2531. //
  2532. ASSERT (!IsListEmpty(&MmUnusedSubsectionList));
  2533. MiSubsectionsProcessed += 1;
  2534. NextEntry = RemoveHeadList(&MmUnusedSubsectionList);
  2535. MappedSubsection = CONTAINING_RECORD (NextEntry,
  2536. MSUBSECTION,
  2537. DereferenceList);
  2538. ControlArea = MappedSubsection->ControlArea;
  2539. ASSERT (ControlArea->u.Flags.Image == 0);
  2540. ASSERT (ControlArea->u.Flags.PhysicalMemory == 0);
  2541. ASSERT (ControlArea->FilePointer != NULL);
  2542. ASSERT (MappedSubsection->NumberOfMappedViews == 0);
  2543. ASSERT (MappedSubsection->u.SubsectionFlags.SubsectionStatic == 0);
  2544. MI_UNUSED_SUBSECTIONS_COUNT_REMOVE (MappedSubsection);
  2545. //
  2546. // Set the flink to NULL indicating this subsection
  2547. // is not on any lists.
  2548. //
  2549. MappedSubsection->DereferenceList.Flink = NULL;
  2550. if (ControlArea->u.Flags.BeingDeleted == 1) {
  2551. MI_INSTRUMENT_DEREF_ACTION(11);
  2552. MiSubsectionActions |= 0x1;
  2553. UNLOCK_PFN (OldIrql);
  2554. ConsecutivePagingIOs = 0;
  2555. continue;
  2556. }
  2557. if (ControlArea->u.Flags.NoModifiedWriting == 1) {
  2558. MiSubsectionActions |= 0x2;
  2559. MI_INSTRUMENT_DEREF_ACTION(12);
  2560. InsertTailList (&MmUnusedSubsectionList,
  2561. &MappedSubsection->DereferenceList);
  2562. MI_UNUSED_SUBSECTIONS_COUNT_INSERT (MappedSubsection);
  2563. UNLOCK_PFN (OldIrql);
  2564. ConsecutivePagingIOs = 0;
  2565. continue;
  2566. }
  2567. //
  2568. // Up the number of mapped views to prevent other threads
  2569. // from freeing this. Clear the accessed bit so we'll know
  2570. // if another thread opens the subsection while we're flushing
  2571. // and closes it before we finish the flush - the other thread
  2572. // may have modified some pages which can then cause our
  2573. // MiCleanSection call (which expects no modified pages in this
  2574. // case) to deadlock with the filesystem.
  2575. //
  2576. MappedSubsection->NumberOfMappedViews = 1;
  2577. MappedSubsection->u2.SubsectionFlags2.SubsectionAccessed = 0;
  2578. #if DBG
  2579. MiActiveSubsection = MappedSubsection;
  2580. #endif
  2581. //
  2582. // Increment the number of mapped views on the control area to
  2583. // prevent threads that are purging the section from deleting it
  2584. // from underneath us while we process one of its subsections.
  2585. //
  2586. ControlArea->NumberOfMappedViews += 1;
  2587. UNLOCK_PFN (OldIrql);
  2588. ASSERT (MappedSubsection->SubsectionBase != NULL);
  2589. PointerPte = &MappedSubsection->SubsectionBase[0];
  2590. LastPte = &MappedSubsection->SubsectionBase
  2591. [MappedSubsection->PtesInSubsection - 1];
  2592. //
  2593. // Preacquire the file to prevent deadlocks with other flushers
  2594. // Also mark ourself as a top level IRP so the filesystem knows
  2595. // we are holding no other resources and that it can unroll if
  2596. // it needs to in order to avoid deadlock. Don't hold this
  2597. // protection any longer than we need to.
  2598. //
  2599. Status = FsRtlAcquireFileForCcFlushEx (ControlArea->FilePointer);
  2600. if (NT_SUCCESS(Status)) {
  2601. PIRP tempIrp = (PIRP)FSRTL_FSP_TOP_LEVEL_IRP;
  2602. MI_INSTRUMENT_DEREF_ACTION (13);
  2603. IoSetTopLevelIrp (tempIrp);
  2604. Status = MiFlushSectionInternal (PointerPte,
  2605. LastPte,
  2606. (PSUBSECTION) MappedSubsection,
  2607. (PSUBSECTION) MappedSubsection,
  2608. FALSE,
  2609. FALSE,
  2610. &IoStatus);
  2611. IoSetTopLevelIrp (NULL);
  2612. //
  2613. // Now release the file.
  2614. //
  2615. FsRtlReleaseFileForCcFlush (ControlArea->FilePointer);
  2616. }
  2617. else {
  2618. MI_INSTRUMENT_DEREF_ACTION (14);
  2619. }
  2620. LOCK_PFN (OldIrql);
  2621. #if DBG
  2622. MiActiveSubsection = NULL;
  2623. #endif
  2624. //
  2625. // Before checking for any failure codes, see if any other
  2626. // threads accessed the subsection while the flush was ongoing.
  2627. //
  2628. // Note that beyond the case of another thread currently using
  2629. // the subsection, the more subtle one is where another
  2630. // thread accessed the subsection and modified some pages.
  2631. // The flush needs to redone (so the clean is guaranteed to work)
  2632. // before another clean can be issued.
  2633. //
  2634. // If any of these cases have occurred, grant this subsection
  2635. // a reprieve.
  2636. //
  2637. ASSERT (MappedSubsection->u.SubsectionFlags.SubsectionStatic == 0);
  2638. if ((MappedSubsection->NumberOfMappedViews != 1) ||
  2639. (MappedSubsection->u2.SubsectionFlags2.SubsectionAccessed == 1) ||
  2640. (ControlArea->u.Flags.BeingDeleted == 1)) {
  2641. MI_INSTRUMENT_DEREF_ACTION(15);
  2642. Requeue:
  2643. MI_INSTRUMENT_DEREF_ACTION(16);
  2644. ASSERT ((LONG_PTR)MappedSubsection->NumberOfMappedViews >= 1);
  2645. MappedSubsection->NumberOfMappedViews -= 1;
  2646. MiSubsectionActions |= 0x4;
  2647. //
  2648. // If the other thread(s) are done with this subsection,
  2649. // it MUST be requeued here - otherwise if there are any
  2650. // pages in the subsection, when they are reclaimed,
  2651. // MiCheckForControlAreaDeletion checks for and expects
  2652. // the control area to be queued on the unused segment list.
  2653. //
  2654. // Note this must be done very carefully because if the other
  2655. // threads are not done with the subsection, it had better
  2656. // not get put on the unused subsection list.
  2657. //
  2658. if ((MappedSubsection->NumberOfMappedViews == 0) &&
  2659. (ControlArea->u.Flags.BeingDeleted == 0)) {
  2660. MI_INSTRUMENT_DEREF_ACTION(17);
  2661. MiSubsectionActions |= 0x8;
  2662. ASSERT (MappedSubsection->u2.SubsectionFlags2.SubsectionAccessed == 1);
  2663. ASSERT (MappedSubsection->DereferenceList.Flink == NULL);
  2664. InsertTailList (&MmUnusedSubsectionList,
  2665. &MappedSubsection->DereferenceList);
  2666. MI_UNUSED_SUBSECTIONS_COUNT_INSERT (MappedSubsection);
  2667. }
  2668. ControlArea->NumberOfMappedViews -= 1;
  2669. UNLOCK_PFN (OldIrql);
  2670. continue;
  2671. }
  2672. MI_INSTRUMENT_DEREF_ACTION(18);
  2673. ASSERT (MappedSubsection->DereferenceList.Flink == NULL);
  2674. if (!NT_SUCCESS(Status)) {
  2675. MiSubsectionActions |= 0x10;
  2676. //
  2677. // If the filesystem told us it had to unroll to avoid
  2678. // deadlock OR we hit a mapped writer collision OR
  2679. // the error occurred on a local file:
  2680. //
  2681. // Then requeue this at the end so we can try again later.
  2682. //
  2683. // Any other errors for networked files are assumed to be
  2684. // permanent (ie: the link may have gone down for an indefinite
  2685. // period), so these sections are cleaned regardless.
  2686. //
  2687. ASSERT ((LONG_PTR)MappedSubsection->NumberOfMappedViews >= 1);
  2688. MappedSubsection->NumberOfMappedViews -= 1;
  2689. InsertTailList (&MmUnusedSubsectionList,
  2690. &MappedSubsection->DereferenceList);
  2691. MI_UNUSED_SUBSECTIONS_COUNT_INSERT (MappedSubsection);
  2692. ControlArea->NumberOfMappedViews -= 1;
  2693. UNLOCK_PFN (OldIrql);
  2694. if (Status == STATUS_FILE_LOCK_CONFLICT) {
  2695. MI_INSTRUMENT_DEREF_ACTION(19);
  2696. ConsecutiveFileLockFailures += 1;
  2697. }
  2698. else {
  2699. MI_INSTRUMENT_DEREF_ACTION(20);
  2700. ConsecutiveFileLockFailures = 0;
  2701. }
  2702. //
  2703. // 10 consecutive file locking failures means we need to
  2704. // yield the processor to allow the filesystem to unjam.
  2705. // Nothing magic about 10, just a number so it
  2706. // gives the worker threads a chance to run.
  2707. //
  2708. if (ConsecutiveFileLockFailures >= 10) {
  2709. MI_INSTRUMENT_DEREF_ACTION(21);
  2710. KeDelayExecutionThread (KernelMode, FALSE, (PLARGE_INTEGER)&MmShortTime);
  2711. ConsecutiveFileLockFailures = 0;
  2712. }
  2713. continue;
  2714. }
  2715. //
  2716. // The final check that must be made is whether any faults are
  2717. // currently in progress which are backed by this subsection.
  2718. // Note this is the perverse case where one thread in a process
  2719. // has unmapped the relevant VAD even while other threads in the
  2720. // same process are faulting on the addresses in that VAD (if the
  2721. // VAD had not been unmapped then the subsection view count would
  2722. // have been nonzero and caught above). Clearly this is a bad
  2723. // process, but nonetheless it must be detected and handled here
  2724. // because upon conclusion of the inpage, the thread will compare
  2725. // (unsynchronized) against the prototype PTEs which may in
  2726. // various stages of deletion below and would cause corruption.
  2727. //
  2728. MI_INSTRUMENT_DEREF_ACTION(22);
  2729. MiSubsectionActions |= 0x20;
  2730. ASSERT (MappedSubsection->NumberOfMappedViews == 1);
  2731. ProtoPtes = MappedSubsection->SubsectionBase;
  2732. NumberOfPtes = MappedSubsection->PtesInSubsection;
  2733. //
  2734. // Note checking the prototype PTEs must be done carefully as
  2735. // they are pagable and the PFN lock is (and must be) held.
  2736. //
  2737. ProtoPtes2 = ProtoPtes;
  2738. LastProtoPte = ProtoPtes + NumberOfPtes;
  2739. while (ProtoPtes2 < LastProtoPte) {
  2740. if ((ProtoPtes2 == ProtoPtes) ||
  2741. (MiIsPteOnPdeBoundary (ProtoPtes2))) {
  2742. if (MiCheckProtoPtePageState (ProtoPtes2, OldIrql, &DroppedPfnLock) == FALSE) {
  2743. //
  2744. // Skip this chunk as it is paged out and thus, cannot
  2745. // have any valid or transition PTEs within it.
  2746. //
  2747. ProtoPtes2 = (PMMPTE)(((ULONG_PTR)ProtoPtes2 | (PAGE_SIZE - 1)) + 1);
  2748. MI_INSTRUMENT_DEREF_ACTION(23);
  2749. continue;
  2750. }
  2751. else {
  2752. //
  2753. // The prototype PTE page is resident right now - but
  2754. // if the PFN lock was dropped & reacquired to make it
  2755. // so, then anything could have changed - so everything
  2756. // must be rechecked.
  2757. //
  2758. if (DroppedPfnLock == TRUE) {
  2759. if ((MappedSubsection->NumberOfMappedViews != 1) ||
  2760. (MappedSubsection->u2.SubsectionFlags2.SubsectionAccessed == 1) ||
  2761. (ControlArea->u.Flags.BeingDeleted == 1)) {
  2762. MI_INSTRUMENT_DEREF_ACTION(57);
  2763. MiSubsectionActions |= 0x40;
  2764. goto Requeue;
  2765. }
  2766. }
  2767. }
  2768. MI_INSTRUMENT_DEREF_ACTION(24);
  2769. }
  2770. MI_INSTRUMENT_DEREF_ACTION(25);
  2771. PteContents = *ProtoPtes2;
  2772. if (PteContents.u.Hard.Valid == 1) {
  2773. KeBugCheckEx (POOL_CORRUPTION_IN_FILE_AREA,
  2774. 0x3,
  2775. (ULONG_PTR)MappedSubsection,
  2776. (ULONG_PTR)ProtoPtes2,
  2777. (ULONG_PTR)PteContents.u.Long);
  2778. }
  2779. if (PteContents.u.Soft.Prototype == 1) {
  2780. MI_INSTRUMENT_DEREF_ACTION(26);
  2781. MiSubsectionActions |= 0x200;
  2782. NOTHING; // This is the expected case.
  2783. }
  2784. else if (PteContents.u.Soft.Transition == 1) {
  2785. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents);
  2786. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  2787. ASSERT (Pfn1->OriginalPte.u.Soft.Prototype == 1);
  2788. if (Pfn1->u3.e1.Modified == 1) {
  2789. //
  2790. // An I/O transfer finished after the last view was
  2791. // unmapped. MmUnlockPages can set the modified bit
  2792. // in this situation so it must be handled properly
  2793. // here - ie: mark the subsection as needing to be
  2794. // reprocessed and march on.
  2795. //
  2796. MI_INSTRUMENT_DEREF_ACTION(27);
  2797. MiSubsectionActions |= 0x8000000;
  2798. MappedSubsection->u2.SubsectionFlags2.SubsectionAccessed = 1;
  2799. goto Requeue;
  2800. }
  2801. if (Pfn1->u3.e2.ReferenceCount != 0) {
  2802. ASSERT (Pfn1->u4.LockCharged == 1);
  2803. //
  2804. // A fault is being satisfied for deleted address space,
  2805. // so don't eliminate this subsection right now.
  2806. //
  2807. MI_INSTRUMENT_DEREF_ACTION(28);
  2808. MiSubsectionActions |= 0x400;
  2809. MappedSubsection->u2.SubsectionFlags2.SubsectionAccessed = 1;
  2810. goto Requeue;
  2811. }
  2812. MiSubsectionActions |= 0x800;
  2813. }
  2814. else {
  2815. if (PteContents.u.Long != 0) {
  2816. KeBugCheckEx (POOL_CORRUPTION_IN_FILE_AREA,
  2817. 0x4,
  2818. (ULONG_PTR)MappedSubsection,
  2819. (ULONG_PTR)ProtoPtes2,
  2820. (ULONG_PTR)PteContents.u.Long);
  2821. }
  2822. MI_INSTRUMENT_DEREF_ACTION(29);
  2823. MiSubsectionActions |= 0x1000;
  2824. }
  2825. ProtoPtes2 += 1;
  2826. }
  2827. MiSubsectionActions |= 0x2000;
  2828. MI_INSTRUMENT_DEREF_ACTION(30);
  2829. //
  2830. // There can be no modified pages in this subsection at this point.
  2831. // Sever the subsection's tie to the prototype PTEs while still
  2832. // holding the lock and then decrement the counts on any resident
  2833. // prototype pages.
  2834. //
  2835. ASSERT (MappedSubsection->NumberOfMappedViews == 1);
  2836. MappedSubsection->NumberOfMappedViews = 0;
  2837. MappedSubsection->SubsectionBase = NULL;
  2838. MiSubsectionActions |= 0x8000;
  2839. ProtoPtes2 = ProtoPtes;
  2840. while (ProtoPtes2 < LastProtoPte) {
  2841. if ((ProtoPtes2 == ProtoPtes) ||
  2842. (MiIsPteOnPdeBoundary (ProtoPtes2))) {
  2843. if (MiCheckProtoPtePageState (ProtoPtes2, OldIrql, &DroppedPfnLock) == FALSE) {
  2844. //
  2845. // Skip this chunk as it is paged out and thus, cannot
  2846. // have any valid or transition PTEs within it.
  2847. //
  2848. ProtoPtes2 = (PMMPTE)(((ULONG_PTR)ProtoPtes2 | (PAGE_SIZE - 1)) + 1);
  2849. MI_INSTRUMENT_DEREF_ACTION(31);
  2850. continue;
  2851. }
  2852. else {
  2853. //
  2854. // The prototype PTE page is resident right now - but
  2855. // if the PFN lock was dropped & reacquired to make it
  2856. // so, then anything could have changed - but notice
  2857. // that the SubsectionBase was zeroed above before
  2858. // entering this loop, so even if the PFN lock was
  2859. // dropped & reacquired, nothing needs to be rechecked.
  2860. //
  2861. }
  2862. MI_INSTRUMENT_DEREF_ACTION(32);
  2863. }
  2864. MI_INSTRUMENT_DEREF_ACTION(33);
  2865. PteContents = *ProtoPtes2;
  2866. ASSERT (PteContents.u.Hard.Valid == 0);
  2867. if (PteContents.u.Soft.Prototype == 1) {
  2868. MiSubsectionActions |= 0x10000;
  2869. MI_INSTRUMENT_DEREF_ACTION(34);
  2870. NOTHING; // This is the expected case.
  2871. }
  2872. else if (PteContents.u.Soft.Transition == 1) {
  2873. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents);
  2874. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  2875. ASSERT (Pfn1->OriginalPte.u.Soft.Prototype == 1);
  2876. ASSERT (Pfn1->u3.e1.Modified == 0);
  2877. //
  2878. // If the page is on the standby list, move it to the
  2879. // freelist. If it's not on the standby list (ie: I/O
  2880. // is still in progress), when the Iast I/O completes, the
  2881. // page will be placed on the freelist as the PFN entry
  2882. // is always marked as deleted now.
  2883. //
  2884. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  2885. ASSERT (Pfn1->u4.LockCharged == 0);
  2886. MI_SET_PFN_DELETED (Pfn1);
  2887. ControlArea->NumberOfPfnReferences -= 1;
  2888. ASSERT ((LONG)ControlArea->NumberOfPfnReferences >= 0);
  2889. PageTableFrameIndex = Pfn1->u4.PteFrame;
  2890. Pfn2 = MI_PFN_ELEMENT (PageTableFrameIndex);
  2891. MiDecrementShareCountInline (Pfn2, PageTableFrameIndex);
  2892. ASSERT (Pfn1->u3.e1.PageLocation != FreePageList);
  2893. MiUnlinkPageFromList (Pfn1);
  2894. MiReleasePageFileSpace (Pfn1->OriginalPte);
  2895. MiInsertPageInFreeList (PageFrameIndex);
  2896. MiSubsectionActions |= 0x20000;
  2897. MI_INSTRUMENT_DEREF_ACTION(35);
  2898. }
  2899. else {
  2900. MiSubsectionActions |= 0x80000;
  2901. ASSERT (PteContents.u.Long == 0);
  2902. MI_INSTRUMENT_DEREF_ACTION(36);
  2903. }
  2904. ProtoPtes2 += 1;
  2905. }
  2906. //
  2907. // If all the cached pages for this control area have been removed
  2908. // then delete it. This will actually insert the control
  2909. // area into the dereference segment header list.
  2910. //
  2911. ControlArea->NumberOfMappedViews -= 1;
  2912. #if DBG
  2913. if ((ControlArea->NumberOfPfnReferences == 0) &&
  2914. (ControlArea->NumberOfMappedViews == 0) &&
  2915. (ControlArea->NumberOfSectionReferences == 0 )) {
  2916. MiSubsectionActions |= 0x100000;
  2917. }
  2918. #endif
  2919. MI_INSTRUMENT_DEREF_ACTION(37);
  2920. MiCheckForControlAreaDeletion (ControlArea);
  2921. UNLOCK_PFN (OldIrql);
  2922. ExFreePool (ProtoPtes);
  2923. ConsecutiveFileLockFailures = 0;
  2924. continue;
  2925. }
  2926. ASSERT (!IsListEmpty(&MmUnusedSegmentList));
  2927. NextEntry = RemoveHeadList(&MmUnusedSegmentList);
  2928. ControlArea = CONTAINING_RECORD (NextEntry,
  2929. CONTROL_AREA,
  2930. DereferenceList);
  2931. MI_UNUSED_SEGMENTS_REMOVE_CHARGE (ControlArea);
  2932. #if DBG
  2933. if (ControlArea->u.Flags.BeingDeleted == 0) {
  2934. if (ControlArea->u.Flags.Image) {
  2935. ASSERT (((PCONTROL_AREA)(ControlArea->FilePointer->SectionObjectPointer->ImageSectionObject)) != NULL);
  2936. }
  2937. else {
  2938. ASSERT (((PCONTROL_AREA)(ControlArea->FilePointer->SectionObjectPointer->DataSectionObject)) != NULL);
  2939. }
  2940. }
  2941. #endif
  2942. //
  2943. // Set the flink to NULL indicating this control area
  2944. // is not on any lists.
  2945. //
  2946. MI_INSTRUMENT_DEREF_ACTION(38);
  2947. ControlArea->DereferenceList.Flink = NULL;
  2948. if ((ControlArea->NumberOfMappedViews == 0) &&
  2949. (ControlArea->NumberOfSectionReferences == 0) &&
  2950. (ControlArea->u.Flags.BeingDeleted == 0)) {
  2951. //
  2952. // If there is paging I/O in progress on this
  2953. // segment, just put this at the tail of the list, as
  2954. // the call to MiCleanSegment would block waiting
  2955. // for the I/O to complete. As this could tie up
  2956. // the thread, don't do it. Check if these are the only
  2957. // types of segments on the dereference list so we don't
  2958. // spin forever and wedge the system.
  2959. //
  2960. if (ControlArea->ModifiedWriteCount > 0) {
  2961. MI_INSERT_UNUSED_SEGMENT (ControlArea);
  2962. UNLOCK_PFN (OldIrql);
  2963. ConsecutivePagingIOs += 1;
  2964. if (ConsecutivePagingIOs > 10) {
  2965. KeDelayExecutionThread (KernelMode, FALSE, (PLARGE_INTEGER)&MmShortTime);
  2966. MI_INSTRUMENT_DEREF_ACTION(39);
  2967. ConsecutivePagingIOs = 0;
  2968. }
  2969. MI_INSTRUMENT_DEREF_ACTION(40);
  2970. continue;
  2971. }
  2972. ConsecutivePagingIOs = 0;
  2973. //
  2974. // Up the number of mapped views to prevent other threads
  2975. // from freeing this. Clear the accessed bit so we'll know
  2976. // if another thread opens the control area while we're flushing
  2977. // and closes it before we finish the flush - the other thread
  2978. // may have modified some pages which can then cause our
  2979. // MiCleanSection call (which expects no modified pages in this
  2980. // case) to deadlock with the filesystem.
  2981. //
  2982. ControlArea->NumberOfMappedViews = 1;
  2983. ControlArea->u.Flags.Accessed = 0;
  2984. MI_INSTRUMENT_DEREF_ACTION(41);
  2985. if (ControlArea->u.Flags.Image == 0) {
  2986. ASSERT (ControlArea->u.Flags.GlobalOnlyPerSession == 0);
  2987. if (ControlArea->u.Flags.Rom == 0) {
  2988. Subsection = (PSUBSECTION)(ControlArea + 1);
  2989. }
  2990. else {
  2991. Subsection = (PSUBSECTION)((PLARGE_CONTROL_AREA)ControlArea + 1);
  2992. }
  2993. MiSubsectionActions |= 0x200000;
  2994. MI_INSTRUMENT_DEREF_ACTION(42);
  2995. while (Subsection->SubsectionBase == NULL) {
  2996. Subsection = Subsection->NextSubsection;
  2997. if (Subsection == NULL) {
  2998. MiSubsectionActions |= 0x400000;
  2999. //
  3000. // All the subsections for this segment have already
  3001. // been trimmed so nothing left to flush. Just get rid
  3002. // of the segment carcass provided no other thread
  3003. // accessed it while we weren't holding the PFN lock.
  3004. //
  3005. MI_INSTRUMENT_DEREF_ACTION(43);
  3006. UNLOCK_PFN (OldIrql);
  3007. goto skip_flush;
  3008. }
  3009. else {
  3010. MI_INSTRUMENT_DEREF_ACTION(44);
  3011. MiSubsectionActions |= 0x800000;
  3012. }
  3013. }
  3014. PointerPte = &Subsection->SubsectionBase[0];
  3015. LastSubsection = Subsection;
  3016. LastSubsectionWithProtos = Subsection;
  3017. MI_INSTRUMENT_DEREF_ACTION(45);
  3018. while (LastSubsection->NextSubsection != NULL) {
  3019. if (LastSubsection->SubsectionBase != NULL) {
  3020. LastSubsectionWithProtos = LastSubsection;
  3021. MiSubsectionActions |= 0x1000000;
  3022. }
  3023. else {
  3024. MiSubsectionActions |= 0x2000000;
  3025. }
  3026. LastSubsection = LastSubsection->NextSubsection;
  3027. }
  3028. if (LastSubsection->SubsectionBase == NULL) {
  3029. MiSubsectionActions |= 0x4000000;
  3030. LastSubsection = LastSubsectionWithProtos;
  3031. }
  3032. UNLOCK_PFN (OldIrql);
  3033. LastPte = &LastSubsection->SubsectionBase
  3034. [LastSubsection->PtesInSubsection - 1];
  3035. //
  3036. // Preacquire the file to prevent deadlocks with other flushers
  3037. // Also mark ourself as a top level IRP so the filesystem knows
  3038. // we are holding no other resources and that it can unroll if
  3039. // it needs to in order to avoid deadlock. Don't hold this
  3040. // protection any longer than we need to.
  3041. //
  3042. Status = FsRtlAcquireFileForCcFlushEx (ControlArea->FilePointer);
  3043. if (NT_SUCCESS(Status)) {
  3044. PIRP tempIrp = (PIRP)FSRTL_FSP_TOP_LEVEL_IRP;
  3045. MI_INSTRUMENT_DEREF_ACTION(46);
  3046. IoSetTopLevelIrp (tempIrp);
  3047. Status = MiFlushSectionInternal (PointerPte,
  3048. LastPte,
  3049. Subsection,
  3050. LastSubsection,
  3051. FALSE,
  3052. FALSE,
  3053. &IoStatus);
  3054. IoSetTopLevelIrp(NULL);
  3055. //
  3056. // Now release the file.
  3057. //
  3058. FsRtlReleaseFileForCcFlush (ControlArea->FilePointer);
  3059. }
  3060. else {
  3061. MI_INSTRUMENT_DEREF_ACTION(47);
  3062. }
  3063. skip_flush:
  3064. LOCK_PFN (OldIrql);
  3065. }
  3066. //
  3067. // Before checking for any failure codes, see if any other
  3068. // threads accessed the control area while the flush was ongoing.
  3069. //
  3070. // Note that beyond the case of another thread currently using
  3071. // the control area, the more subtle one is where another
  3072. // thread accessed the control area and modified some pages.
  3073. // The flush needs to redone (so the clean is guaranteed to work)
  3074. // before another clean can be issued.
  3075. //
  3076. // If any of these cases have occurred, grant this control area
  3077. // a reprieve.
  3078. //
  3079. if (!((ControlArea->NumberOfMappedViews == 1) &&
  3080. (ControlArea->u.Flags.Accessed == 0) &&
  3081. (ControlArea->NumberOfSectionReferences == 0) &&
  3082. (ControlArea->u.Flags.BeingDeleted == 0))) {
  3083. ControlArea->NumberOfMappedViews -= 1;
  3084. MI_INSTRUMENT_DEREF_ACTION(48);
  3085. //
  3086. // If the other thread(s) are done with this control area,
  3087. // it MUST be requeued here - otherwise if there are any
  3088. // pages in the control area, when they are reclaimed,
  3089. // MiCheckForControlAreaDeletion checks for and expects
  3090. // the control area to be queued on the unused segment list.
  3091. //
  3092. // Note this must be done very carefully because if the other
  3093. // threads are not done with the control area, it had better
  3094. // not get put on the unused segment list.
  3095. //
  3096. //
  3097. // Need to do the equivalent of a MiCheckControlArea here.
  3098. // or reprocess. Only iff mappedview & sectref = 0.
  3099. //
  3100. if ((ControlArea->NumberOfMappedViews == 0) &&
  3101. (ControlArea->NumberOfSectionReferences == 0) &&
  3102. (ControlArea->u.Flags.BeingDeleted == 0)) {
  3103. ASSERT (ControlArea->u.Flags.Accessed == 1);
  3104. ASSERT(ControlArea->DereferenceList.Flink == NULL);
  3105. MI_INSERT_UNUSED_SEGMENT (ControlArea);
  3106. }
  3107. UNLOCK_PFN (OldIrql);
  3108. continue;
  3109. }
  3110. MI_INSTRUMENT_DEREF_ACTION(49);
  3111. if (!NT_SUCCESS(Status)) {
  3112. //
  3113. // If the filesystem told us it had to unroll to avoid
  3114. // deadlock OR we hit a mapped writer collision OR
  3115. // the error occurred on a local file:
  3116. //
  3117. // Then requeue this at the end so we can try again later.
  3118. //
  3119. // Any other errors for networked files are assumed to be
  3120. // permanent (ie: the link may have gone down for an indefinite
  3121. // period), so these sections are cleaned regardless.
  3122. //
  3123. MI_INSTRUMENT_DEREF_ACTION(50);
  3124. if ((Status == STATUS_FILE_LOCK_CONFLICT) ||
  3125. (Status == STATUS_MAPPED_WRITER_COLLISION) ||
  3126. (ControlArea->u.Flags.Networked == 0)) {
  3127. ASSERT(ControlArea->DereferenceList.Flink == NULL);
  3128. ControlArea->NumberOfMappedViews -= 1;
  3129. MI_INSERT_UNUSED_SEGMENT (ControlArea);
  3130. UNLOCK_PFN (OldIrql);
  3131. if (Status == STATUS_FILE_LOCK_CONFLICT) {
  3132. ConsecutiveFileLockFailures += 1;
  3133. }
  3134. else {
  3135. ConsecutiveFileLockFailures = 0;
  3136. }
  3137. //
  3138. // 10 consecutive file locking failures means we need to
  3139. // yield the processor to allow the filesystem to unjam.
  3140. // Nothing magic about 10, just a number so it
  3141. // gives the worker threads a chance to run.
  3142. //
  3143. MI_INSTRUMENT_DEREF_ACTION(51);
  3144. if (ConsecutiveFileLockFailures >= 10) {
  3145. KeDelayExecutionThread (KernelMode, FALSE, (PLARGE_INTEGER)&MmShortTime);
  3146. ConsecutiveFileLockFailures = 0;
  3147. }
  3148. continue;
  3149. }
  3150. DirtyPagesOk = TRUE;
  3151. }
  3152. else {
  3153. MI_INSTRUMENT_DEREF_ACTION(52);
  3154. ConsecutiveFileLockFailures = 0;
  3155. DirtyPagesOk = FALSE;
  3156. }
  3157. ControlArea->u.Flags.BeingDeleted = 1;
  3158. //
  3159. // Don't let any pages be written by the modified
  3160. // page writer from this point on.
  3161. //
  3162. ControlArea->u.Flags.NoModifiedWriting = 1;
  3163. ASSERT (ControlArea->u.Flags.FilePointerNull == 0);
  3164. UNLOCK_PFN (OldIrql);
  3165. MI_INSTRUMENT_DEREF_ACTION(53);
  3166. MiCleanSection (ControlArea, DirtyPagesOk);
  3167. }
  3168. else {
  3169. //
  3170. // The segment was not eligible for deletion. Just leave
  3171. // it off the unused segment list and continue the loop.
  3172. //
  3173. MI_INSTRUMENT_DEREF_ACTION(54);
  3174. UNLOCK_PFN (OldIrql);
  3175. ConsecutivePagingIOs = 0;
  3176. }
  3177. }
  3178. }