Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

sectsup.c

Go to the documentation of this file.
00001 /*++ 00002 00003 Copyright (c) 1989 Microsoft Corporation 00004 00005 Module Name: 00006 00007 sectsup.c 00008 00009 Abstract: 00010 00011 This module contains the routines which implement the 00012 section object. 00013 00014 Author: 00015 00016 Lou Perazzoli (loup) 22-May-1989 00017 Landy Wang (landyw) 02-June-1997 00018 00019 Revision History: 00020 00021 --*/ 00022 00023 00024 #include "mi.h" 00025 00026 #ifdef ALLOC_PRAGMA 00027 #pragma alloc_text(INIT,MiSectionInitialization) 00028 #endif 00029 00030 MMEVENT_COUNT_LIST MmEventCountList; 00031 00032 ULONG 00033 FASTCALL 00034 MiCheckProtoPtePageState ( 00035 IN PMMPTE PrototypePte, 00036 IN ULONG PfnLockHeld 00037 ); 00038 00039 ULONG MmUnusedSegmentForceFree; 00040 00041 SIZE_T MmSharedCommit = 0; 00042 extern ULONG MMCONTROL; 00043 00044 // 00045 // Define segment dereference thread wait object types. 00046 // 00047 00048 typedef enum _SEGMENT_DEREFERENCE_OBJECT { 00049 SegmentDereference, 00050 UsedSegmentCleanup, 00051 SegMaximumObject 00052 } BALANCE_OBJECT; 00053 00054 extern POBJECT_TYPE IoFileObjectType; 00055 00056 GENERIC_MAPPING MiSectionMapping = { 00057 STANDARD_RIGHTS_READ | 00058 SECTION_QUERY | SECTION_MAP_READ, 00059 STANDARD_RIGHTS_WRITE | 00060 SECTION_MAP_WRITE, 00061 STANDARD_RIGHTS_EXECUTE | 00062 SECTION_MAP_EXECUTE, 00063 SECTION_ALL_ACCESS 00064 }; 00065 00066 VOID 00067 VadTreeWalk ( 00068 PMMVAD Start 00069 ); 00070 00071 VOID 00072 MiRemoveUnusedSegments( 00073 VOID 00074 ); 00075 00076 00077 VOID 00078 FASTCALL 00079 MiInsertBasedSection ( 00080 IN PSECTION Section 00081 ) 00082 00083 /*++ 00084 00085 Routine Description: 00086 00087 This function inserts a virtual address descriptor into the tree and 00088 reorders the splay tree as appropriate. 00089 00090 Arguments: 00091 00092 Section - Supplies a pointer to a based section. 00093 00094 Return Value: 00095 00096 None. 00097 00098 Environment: 00099 00100 Must be holding the section based mutex. 00101 00102 --*/ 00103 00104 { 00105 PMMADDRESS_NODE *Root; 00106 00107 ASSERT (Section->Address.EndingVpn > Section->Address.StartingVpn); 00108 00109 Root = &MmSectionBasedRoot; 00110 00111 MiInsertNode (&Section->Address, Root); 00112 return; 00113 } 00114 00115 00116 VOID 00117 FASTCALL 00118 MiRemoveBasedSection ( 00119 IN PSECTION Section 00120 ) 00121 00122 /*++ 00123 00124 Routine Description: 00125 00126 This function removes a based section from the tree. 00127 00128 Arguments: 00129 00130 Section - pointer to the based section object to remove. 00131 00132 Return Value: 00133 00134 None. 00135 00136 Environment: 00137 00138 Must be holding the section based mutex. 00139 00140 --*/ 00141 00142 { 00143 PMMADDRESS_NODE *Root; 00144 00145 Root = &MmSectionBasedRoot; 00146 00147 MiRemoveNode (&Section->Address, Root); 00148 00149 return; 00150 } 00151 00152 00153 PVOID 00154 MiFindEmptySectionBaseDown ( 00155 IN ULONG SizeOfRange, 00156 IN PVOID HighestAddressToEndAt 00157 ) 00158 00159 /*++ 00160 00161 Routine Description: 00162 00163 The function examines the virtual address descriptors to locate 00164 an unused range of the specified size and returns the starting 00165 address of the range. This routine looks from the top down. 00166 00167 Arguments: 00168 00169 SectionBasedRoot - Supplies the root of the available address tree. 00170 00171 SizeOfRange - Supplies the size in bytes of the range to locate. 00172 00173 HighestAddressToEndAt - Supplies the virtual address to begin looking 00174 at. 00175 00176 Return Value: 00177 00178 Returns the starting address of a suitable range. 00179 00180 --*/ 00181 00182 { 00183 return MiFindEmptyAddressRangeDownTree (SizeOfRange, 00184 HighestAddressToEndAt, 00185 X64K, 00186 MmSectionBasedRoot); 00187 } 00188 00189 00190 VOID 00191 MiSegmentDelete ( 00192 PSEGMENT Segment 00193 ) 00194 00195 /*++ 00196 00197 Routine Description: 00198 00199 This routine is called by the object management procedures whenever 00200 the last reference to a segment object has been removed. This routine 00201 releases the pool allocated for the prototype PTEs and performs 00202 consistency checks on those PTEs. 00203 00204 For segments which map files, the file object is dereferenced. 00205 00206 Note, that for a segment which maps a file, no PTEs may be valid 00207 or transition, while a segment which is backed by a paging file 00208 may have transition pages, but no valid pages (there can be no 00209 PTEs which refer to the segment). 00210 00211 00212 Arguments: 00213 00214 Segment - a pointer to the segment structure. 00215 00216 Return Value: 00217 00218 None. 00219 00220 --*/ 00221 00222 { 00223 PMMPTE PointerPte; 00224 PMMPTE LastPte; 00225 PMMPFN Pfn1; 00226 KIRQL OldIrql; 00227 KIRQL OldIrql2; 00228 volatile PFILE_OBJECT File; 00229 volatile PCONTROL_AREA ControlArea; 00230 PEVENT_COUNTER Event; 00231 MMPTE PteContents; 00232 PSUBSECTION Subsection; 00233 PSUBSECTION NextSubsection; 00234 SIZE_T CommittedPages; 00235 00236 PointerPte = Segment->PrototypePte; 00237 LastPte = PointerPte + Segment->NonExtendedPtes; 00238 00239 #if DBG 00240 if (MmDebug & MM_DBG_SECTIONS) { 00241 DbgPrint("MM:deleting segment %lx control %lx\n",Segment, Segment->ControlArea); 00242 } 00243 #endif 00244 00245 ControlArea = Segment->ControlArea; 00246 00247 ASSERT (ControlArea->u.Flags.BeingDeleted == 1); 00248 00249 LOCK_PFN (OldIrql2); 00250 if (ControlArea->DereferenceList.Flink != NULL) { 00251 00252 // 00253 // Remove this from the list of unused segments. 00254 // 00255 00256 ExAcquireSpinLock (&MmDereferenceSegmentHeader.Lock, &OldIrql); 00257 RemoveEntryList (&ControlArea->DereferenceList); 00258 00259 MI_UNUSED_SEGMENTS_REMOVE_CHARGE (ControlArea); 00260 00261 ExReleaseSpinLock (&MmDereferenceSegmentHeader.Lock, OldIrql); 00262 } 00263 UNLOCK_PFN (OldIrql2); 00264 00265 if (ControlArea->u.Flags.Image || 00266 ControlArea->u.Flags.File ) { 00267 00268 // 00269 // If there have been committed pages in this segment, adjust 00270 // the total commit count. 00271 // 00272 00273 00274 // 00275 // Unload kernel debugger symbols if any were loaded. 00276 // 00277 00278 if (ControlArea->u.Flags.DebugSymbolsLoaded != 0) { 00279 00280 // 00281 // TEMP TEMP TEMP rip out when debugger converted 00282 // 00283 00284 ANSI_STRING AnsiName; 00285 NTSTATUS Status; 00286 00287 Status = RtlUnicodeStringToAnsiString( &AnsiName, 00288 (PUNICODE_STRING)&Segment->ControlArea->FilePointer->FileName, 00289 TRUE ); 00290 00291 if (NT_SUCCESS( Status)) { 00292 DbgUnLoadImageSymbols( &AnsiName, 00293 Segment->BasedAddress, 00294 (ULONG_PTR)PsGetCurrentProcess()); 00295 RtlFreeAnsiString( &AnsiName ); 00296 } 00297 LOCK_PFN (OldIrql); 00298 ControlArea->u.Flags.DebugSymbolsLoaded = 0; 00299 UNLOCK_PFN (OldIrql); 00300 } 00301 00302 // 00303 // If the segment was deleted due to a name collision at insertion 00304 // we don't want to dereference the file pointer. 00305 // 00306 00307 if (ControlArea->u.Flags.BeingCreated == FALSE) { 00308 00309 // 00310 // Clear the segment context and dereference the file object 00311 // for this Segment. 00312 // 00313 00314 LOCK_PFN (OldIrql); 00315 00316 MiMakeSystemAddressValidPfn (Segment); 00317 File = (volatile PFILE_OBJECT)Segment->ControlArea->FilePointer; 00318 ControlArea = (volatile PCONTROL_AREA)Segment->ControlArea; 00319 00320 Event = ControlArea->WaitingForDeletion; 00321 ControlArea->WaitingForDeletion = NULL; 00322 00323 UNLOCK_PFN (OldIrql); 00324 00325 if (Event != NULL) { 00326 KeSetEvent (&Event->Event, 0, FALSE); 00327 } 00328 00329 #if DBG 00330 if (ControlArea->u.Flags.Image == 1) { 00331 ASSERT (ControlArea->FilePointer->SectionObjectPointer->ImageSectionObject != (PVOID)ControlArea); 00332 } else { 00333 ASSERT (ControlArea->FilePointer->SectionObjectPointer->DataSectionObject != (PVOID)ControlArea); 00334 } 00335 #endif //DBG 00336 00337 PERFINFO_SEGMENT_DELETE(ControlArea->FilePointer); 00338 00339 ObDereferenceObject (ControlArea->FilePointer); 00340 } 00341 00342 if (ControlArea->u.Flags.Image == 0) { 00343 00344 // 00345 // This is a mapped data file. None of the prototype 00346 // PTEs may be referencing a physical page (valid or transition). 00347 // 00348 00349 #if DBG 00350 while (PointerPte < LastPte) { 00351 00352 // 00353 // Prototype PTEs for segments backed by paging file 00354 // are either in demand zero, page file format, or transition. 00355 // 00356 00357 ASSERT (PointerPte->u.Hard.Valid == 0); 00358 ASSERT ((PointerPte->u.Soft.Prototype == 1) || 00359 (PointerPte->u.Long == 0)); 00360 PointerPte += 1; 00361 } 00362 #endif //DBG 00363 00364 // 00365 // Deallocate the control area and subsections. 00366 // 00367 00368 ASSERT (ControlArea->u.Flags.GlobalOnlyPerSession == 0); 00369 00370 Subsection = (PSUBSECTION)(ControlArea + 1); 00371 00372 Subsection = Subsection->NextSubsection; 00373 00374 while (Subsection != NULL) { 00375 ExFreePool (Subsection->SubsectionBase); 00376 NextSubsection = Subsection->NextSubsection; 00377 ExFreePool (Subsection); 00378 Subsection = NextSubsection; 00379 } 00380 00381 if (Segment->NumberOfCommittedPages != 0) { 00382 MiReturnCommitment (Segment->NumberOfCommittedPages); 00383 MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_SEGMENT_DELETE1, 00384 Segment->NumberOfCommittedPages); 00385 00386 ExAcquireFastMutex (&MmSectionCommitMutex); 00387 MmSharedCommit -= Segment->NumberOfCommittedPages; 00388 ExReleaseFastMutex (&MmSectionCommitMutex); 00389 } 00390 00391 ExFreePool (Segment->ControlArea); 00392 ExFreePool (Segment); 00393 00394 // 00395 // The file mapped Segment object is now deleted. 00396 // 00397 00398 return; 00399 } 00400 } 00401 00402 // 00403 // This is a page file backed or image Segment. The Segment is being 00404 // deleted, remove all references to the paging file and physical memory. 00405 // 00406 00407 // 00408 // The PFN lock is required for deallocating pages from a paging 00409 // file and for deleting transition PTEs. 00410 // 00411 00412 LOCK_PFN (OldIrql); 00413 00414 MiMakeSystemAddressValidPfn (PointerPte); 00415 00416 while (PointerPte < LastPte) { 00417 00418 if (MiIsPteOnPdeBoundary(PointerPte)) { 00419 00420 // 00421 // We are on a page boundary, make sure this PTE is resident. 00422 // 00423 00424 if (MmIsAddressValid (PointerPte) == FALSE) { 00425 00426 MiMakeSystemAddressValidPfn (PointerPte); 00427 } 00428 } 00429 00430 PteContents = *PointerPte; 00431 00432 // 00433 // Prototype PTEs for Segments backed by paging file 00434 // are either in demand zero, page file format, or transition. 00435 // 00436 00437 ASSERT (PteContents.u.Hard.Valid == 0); 00438 00439 if (PteContents.u.Soft.Prototype == 0) { 00440 00441 if (PteContents.u.Soft.Transition == 1) { 00442 00443 // 00444 // Prototype PTE in transition, put the page on the free list. 00445 // 00446 00447 Pfn1 = MI_PFN_ELEMENT (PteContents.u.Trans.PageFrameNumber); 00448 00449 MI_SET_PFN_DELETED (Pfn1); 00450 00451 MiDecrementShareCount (Pfn1->PteFrame); 00452 00453 // 00454 // Check the reference count for the page, if the reference 00455 // count is zero and the page is not on the freelist, 00456 // move the page to the free list, if the reference 00457 // count is not zero, ignore this page. 00458 // When the reference count goes to zero, it will be placed 00459 // on the free list. 00460 // 00461 00462 if (Pfn1->u3.e2.ReferenceCount == 0) { 00463 MiUnlinkPageFromList (Pfn1); 00464 MiReleasePageFileSpace (Pfn1->OriginalPte); 00465 MiInsertPageInList (MmPageLocationList[FreePageList], 00466 MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents)); 00467 } 00468 00469 } else { 00470 00471 // 00472 // This is not a prototype PTE, if any paging file 00473 // space has been allocated, release it. 00474 // 00475 00476 if (IS_PTE_NOT_DEMAND_ZERO (PteContents)) { 00477 MiReleasePageFileSpace (PteContents); 00478 } 00479 } 00480 } 00481 #if DBG 00482 MI_WRITE_INVALID_PTE (PointerPte, ZeroPte); 00483 #endif 00484 PointerPte += 1; 00485 } 00486 00487 UNLOCK_PFN (OldIrql); 00488 00489 // 00490 // If there have been committed pages in this segment, adjust 00491 // the total commit count. 00492 // 00493 00494 if (Segment->NumberOfCommittedPages != 0) { 00495 MiReturnCommitment (Segment->NumberOfCommittedPages); 00496 MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_SEGMENT_DELETE2, 00497 Segment->NumberOfCommittedPages); 00498 00499 ExAcquireFastMutex (&MmSectionCommitMutex); 00500 MmSharedCommit -= Segment->NumberOfCommittedPages; 00501 ExReleaseFastMutex (&MmSectionCommitMutex); 00502 } 00503 00504 ExFreePool (Segment->ControlArea); 00505 ExFreePool (Segment); 00506 00507 return; 00508 } 00509 00510 VOID 00511 MiSectionDelete ( 00512 PVOID Object 00513 ) 00514 00515 /*++ 00516 00517 Routine Description: 00518 00519 00520 This routine is called by the object management procedures whenever 00521 the last reference to a section object has been removed. This routine 00522 dereferences the associated segment object and checks to see if 00523 the segment object should be deleted by queueing the segment to the 00524 segment deletion thread. 00525 00526 Arguments: 00527 00528 Object - a pointer to the body of the section object. 00529 00530 Return Value: 00531 00532 None. 00533 00534 --*/ 00535 00536 { 00537 PSECTION Section; 00538 volatile PCONTROL_AREA ControlArea; 00539 KIRQL OldIrql; 00540 ULONG UserRef; 00541 00542 Section = (PSECTION)Object; 00543 00544 if (Section->Segment == (PSEGMENT)NULL) { 00545 00546 // 00547 // The section was never initialized, no need to remove 00548 // any structures. 00549 // 00550 return; 00551 } 00552 00553 UserRef = Section->u.Flags.UserReference; 00554 ControlArea = (volatile PCONTROL_AREA)Section->Segment->ControlArea; 00555 00556 #if DBG 00557 if (MmDebug & MM_DBG_SECTIONS) { 00558 DbgPrint("MM:deleting section %lx control %lx\n",Section, ControlArea); 00559 } 00560 #endif 00561 00562 if (Section->Address.StartingVpn != 0) { 00563 00564 // 00565 // This section is based, remove the base address from the 00566 // tree. 00567 // 00568 00569 // 00570 // Get the allocation base mutex. 00571 // 00572 00573 ExAcquireFastMutex (&MmSectionBasedMutex); 00574 00575 MiRemoveBasedSection (Section); 00576 00577 ExReleaseFastMutex (&MmSectionBasedMutex); 00578 00579 } 00580 00581 // 00582 // Decrement the number of section references to the segment for this 00583 // section. This requires APCs to be blocked and the PFN lock to 00584 // synchronize upon. 00585 // 00586 00587 LOCK_PFN (OldIrql); 00588 00589 ControlArea->NumberOfSectionReferences -= 1; 00590 ControlArea->NumberOfUserReferences -= UserRef; 00591 00592 // 00593 // This routine returns with the PFN lock released. 00594 // 00595 00596 MiCheckControlArea (ControlArea, NULL, OldIrql); 00597 00598 return; 00599 } 00600 00601 00602 VOID 00603 MiDereferenceSegmentThread ( 00604 IN PVOID StartContext 00605 ) 00606 00607 /*++ 00608 00609 Routine Description: 00610 00611 This routine is the thread for dereferencing segments which have 00612 no references from any sections or mapped views AND there are 00613 no prototype PTEs within the segment which are in the transition 00614 state (i.e., no PFN database references to the segment). 00615 00616 It also does double duty and is used for expansion of paging files. 00617 00618 Arguments: 00619 00620 StartContext - Not used. 00621 00622 Return Value: 00623 00624 None. 00625 00626 --*/ 00627 00628 { 00629 PCONTROL_AREA ControlArea; 00630 PMMPAGE_FILE_EXPANSION PageExpand; 00631 PLIST_ENTRY NextEntry; 00632 KIRQL OldIrql; 00633 static KWAIT_BLOCK WaitBlockArray[SegMaximumObject]; 00634 PVOID WaitObjects[SegMaximumObject]; 00635 NTSTATUS Status; 00636 00637 UNREFERENCED_PARAMETER (StartContext); 00638 00639 // 00640 // Make this a real time thread. 00641 // 00642 00643 (VOID) KeSetPriorityThread (&PsGetCurrentThread()->Tcb, 00644 LOW_REALTIME_PRIORITY + 2); 00645 00646 WaitObjects[SegmentDereference] = (PVOID)&MmDereferenceSegmentHeader.Semaphore; 00647 WaitObjects[UsedSegmentCleanup] = (PVOID)&MmUnusedSegmentCleanup; 00648 00649 for (;;) { 00650 00651 Status = KeWaitForMultipleObjects(SegMaximumObject, 00652 &WaitObjects[0], 00653 WaitAny, 00654 WrVirtualMemory, 00655 UserMode, 00656 FALSE, 00657 NULL, 00658 &WaitBlockArray[0]); 00659 00660 // 00661 // Switch on the wait status. 00662 // 00663 00664 switch (Status) { 00665 00666 case SegmentDereference: 00667 00668 // 00669 // An entry is available to dereference, acquire the spinlock 00670 // and remove the entry. 00671 // 00672 00673 ExAcquireSpinLock (&MmDereferenceSegmentHeader.Lock, &OldIrql); 00674 00675 if (IsListEmpty(&MmDereferenceSegmentHeader.ListHead)) { 00676 00677 // 00678 // There is nothing in the list, rewait. 00679 // 00680 00681 ExReleaseSpinLock (&MmDereferenceSegmentHeader.Lock, OldIrql); 00682 break; 00683 } 00684 00685 NextEntry = RemoveHeadList(&MmDereferenceSegmentHeader.ListHead); 00686 00687 ExReleaseSpinLock (&MmDereferenceSegmentHeader.Lock, OldIrql); 00688 00689 ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL); 00690 00691 ControlArea = CONTAINING_RECORD( NextEntry, 00692 CONTROL_AREA, 00693 DereferenceList ); 00694 00695 if (ControlArea->Segment != NULL) { 00696 00697 // 00698 // This is a control area, delete it. 00699 // 00700 00701 #if DBG 00702 if (MmDebug & MM_DBG_SECTIONS) { 00703 DbgPrint("MM:dereferencing segment %lx control %lx\n", 00704 ControlArea->Segment, ControlArea); 00705 } 00706 #endif 00707 00708 // 00709 // Indicate this entry is not on any list. 00710 // 00711 00712 ControlArea->DereferenceList.Flink = NULL; 00713 00714 ASSERT (ControlArea->u.Flags.FilePointerNull == 1); 00715 MiSegmentDelete (ControlArea->Segment); 00716 00717 } else { 00718 00719 // 00720 // This is a request to expand or reduce the paging files. 00721 // 00722 00723 PageExpand = (PMMPAGE_FILE_EXPANSION)ControlArea; 00724 00725 if (PageExpand->RequestedExpansionSize == 0xFFFFFFFF) { 00726 00727 // 00728 // Attempt to reduce the size of the paging files. 00729 // 00730 00731 ExFreePool (PageExpand); 00732 00733 MiAttemptPageFileReduction (); 00734 } else { 00735 00736 // 00737 // Attempt to expand the size of the paging files. 00738 // 00739 00740 MiExtendPagingFiles (PageExpand); 00741 KeSetEvent (&PageExpand->Event, 0, FALSE); 00742 MiRemoveUnusedSegments(); 00743 } 00744 } 00745 break; 00746 00747 case UsedSegmentCleanup: 00748 00749 MiRemoveUnusedSegments(); 00750 00751 KeClearEvent (&MmUnusedSegmentCleanup); 00752 00753 break; 00754 00755 default: 00756 00757 KdPrint(("MMSegmentderef: Illegal wait status, %lx =\n", Status)); 00758 break; 00759 } // end switch 00760 00761 } //end for 00762 00763 return; 00764 } 00765 00766 00767 ULONG 00768 MiSectionInitialization ( 00769 ) 00770 00771 /*++ 00772 00773 Routine Description: 00774 00775 This function creates the section object type descriptor at system 00776 initialization and stores the address of the object type descriptor 00777 in global storage. 00778 00779 Arguments: 00780 00781 None. 00782 00783 Return Value: 00784 00785 TRUE - Initialization was successful. 00786 00787 FALSE - Initialization Failed. 00788 00789 00790 00791 --*/ 00792 00793 { 00794 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer; 00795 UNICODE_STRING TypeName; 00796 HANDLE ThreadHandle; 00797 OBJECT_ATTRIBUTES ObjectAttributes; 00798 UNICODE_STRING SectionName; 00799 PSECTION Section; 00800 HANDLE Handle; 00801 PSEGMENT Segment; 00802 PCONTROL_AREA ControlArea; 00803 NTSTATUS Status; 00804 00805 MmSectionBasedRoot = (PMMADDRESS_NODE)NULL; 00806 00807 // 00808 // Initialize the common fields of the Object Type Initializer record 00809 // 00810 00811 RtlZeroMemory( &ObjectTypeInitializer, sizeof( ObjectTypeInitializer ) ); 00812 ObjectTypeInitializer.Length = sizeof( ObjectTypeInitializer ); 00813 ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK; 00814 ObjectTypeInitializer.GenericMapping = MiSectionMapping; 00815 ObjectTypeInitializer.PoolType = PagedPool; 00816 ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(SECTION); 00817 00818 // 00819 // Initialize string descriptor. 00820 // 00821 00822 RtlInitUnicodeString (&TypeName, L"Section"); 00823 00824 // 00825 // Create the section object type descriptor 00826 // 00827 00828 ObjectTypeInitializer.ValidAccessMask = SECTION_ALL_ACCESS; 00829 ObjectTypeInitializer.DeleteProcedure = MiSectionDelete; 00830 ObjectTypeInitializer.GenericMapping = MiSectionMapping; 00831 ObjectTypeInitializer.UseDefaultObject = TRUE; 00832 if ( !NT_SUCCESS(ObCreateObjectType(&TypeName, 00833 &ObjectTypeInitializer, 00834 (PSECURITY_DESCRIPTOR) NULL, 00835 &MmSectionObjectType 00836 )) ) { 00837 return FALSE; 00838 } 00839 00840 // 00841 // Initialize listhead, spinlock and semaphore for 00842 // segment dereferencing thread. 00843 // 00844 00845 KeInitializeSpinLock (&MmDereferenceSegmentHeader.Lock); 00846 InitializeListHead (&MmDereferenceSegmentHeader.ListHead); 00847 KeInitializeSemaphore (&MmDereferenceSegmentHeader.Semaphore, 0, MAXLONG); 00848 00849 InitializeListHead (&MmUnusedSegmentList); 00850 KeInitializeEvent (&MmUnusedSegmentCleanup, NotificationEvent, FALSE); 00851 00852 // 00853 // Create the Segment dereferencing thread. 00854 // 00855 00856 InitializeObjectAttributes( &ObjectAttributes, 00857 NULL, 00858 0, 00859 NULL, 00860 NULL ); 00861 00862 if ( !NT_SUCCESS(PsCreateSystemThread( 00863 &ThreadHandle, 00864 THREAD_ALL_ACCESS, 00865 &ObjectAttributes, 00866 0, 00867 NULL, 00868 MiDereferenceSegmentThread, 00869 NULL 00870 )) ) { 00871 return FALSE; 00872 } 00873 ZwClose (ThreadHandle); 00874 00875 // 00876 // Create the permanent section which maps physical memory. 00877 // 00878 00879 Segment = (PSEGMENT)ExAllocatePoolWithTag (PagedPool, 00880 sizeof(SEGMENT), 00881 'gSmM'); 00882 if (Segment == NULL) { 00883 return FALSE; 00884 } 00885 00886 ControlArea = ExAllocatePoolWithTag (NonPagedPool, 00887 (ULONG)sizeof(CONTROL_AREA), 00888 MMCONTROL); 00889 if (ControlArea == NULL) { 00890 ExFreePool (Segment); 00891 return FALSE; 00892 } 00893 00894 RtlZeroMemory (Segment, sizeof(SEGMENT)); 00895 RtlZeroMemory (ControlArea, sizeof(CONTROL_AREA)); 00896 00897 ControlArea->Segment = Segment; 00898 ControlArea->NumberOfSectionReferences = 1; 00899 ControlArea->u.Flags.PhysicalMemory = 1; 00900 00901 Segment->ControlArea = ControlArea; 00902 Segment->SegmentPteTemplate = ZeroPte; 00903 00904 // 00905 // Now that the segment object is created, create a section object 00906 // which refers to the segment object. 00907 // 00908 00909 RtlInitUnicodeString (&SectionName, L"\\Device\\PhysicalMemory"); 00910 00911 InitializeObjectAttributes( &ObjectAttributes, 00912 &SectionName, 00913 OBJ_PERMANENT, 00914 NULL, 00915 NULL 00916 ); 00917 00918 Status = ObCreateObject (KernelMode, 00919 MmSectionObjectType, 00920 &ObjectAttributes, 00921 KernelMode, 00922 NULL, 00923 sizeof(SECTION), 00924 sizeof(SECTION), 00925 0, 00926 (PVOID *)&Section); 00927 if (!NT_SUCCESS(Status)) { 00928 ExFreePool (ControlArea); 00929 ExFreePool (Segment); 00930 return FALSE; 00931 } 00932 00933 Section->Segment = Segment; 00934 Section->SizeOfSection.QuadPart = ((LONGLONG)1 << PHYSICAL_ADDRESS_BITS) - 1; 00935 Section->u.LongFlags = 0; 00936 Section->InitialPageProtection = PAGE_READWRITE; 00937 00938 Status = ObInsertObject ((PVOID)Section, 00939 NULL, 00940 SECTION_MAP_READ, 00941 0, 00942 (PVOID *)NULL, 00943 &Handle); 00944 00945 if (!NT_SUCCESS( Status )) { 00946 return FALSE; 00947 } 00948 00949 if ( !NT_SUCCESS (NtClose ( Handle))) { 00950 return FALSE; 00951 } 00952 00953 return TRUE; 00954 } 00955 00956 BOOLEAN 00957 MmForceSectionClosed ( 00958 IN PSECTION_OBJECT_POINTERS SectionObjectPointer, 00959 IN BOOLEAN DelayClose 00960 ) 00961 00962 /*++ 00963 00964 Routine Description: 00965 00966 This function examines the Section object pointers. If they are NULL, 00967 no further action is taken and the value TRUE is returned. 00968 00969 If the Section object pointer is not NULL, the section reference count 00970 and the map view count are checked. If both counts are zero, the 00971 segment associated with the file is deleted and the file closed. 00972 If one of the counts is non-zero, no action is taken and the 00973 value FALSE is returned. 00974 00975 Arguments: 00976 00977 SectionObjectPointer - Supplies a pointer to a section object. 00978 00979 DelayClose - Supplies the value TRUE if the close operation should 00980 occur as soon as possible in the event this section 00981 cannot be closed now due to outstanding references. 00982 00983 Return Value: 00984 00985 TRUE - The segment was deleted and the file closed or no segment was 00986 located. 00987 00988 FALSE - The segment was not deleted and no action was performed OR 00989 an I/O error occurred trying to write the pages. 00990 00991 --*/ 00992 00993 { 00994 PCONTROL_AREA ControlArea; 00995 KIRQL OldIrql; 00996 BOOLEAN state; 00997 00998 // 00999 // Check the status of the control area, if the control area is in use 01000 // or the control area is being deleted, this operation cannot continue. 01001 // 01002 01003 state = MiCheckControlAreaStatus (CheckBothSection, 01004 SectionObjectPointer, 01005 DelayClose, 01006 &ControlArea, 01007 &OldIrql); 01008 01009 if (ControlArea == NULL) { 01010 return state; 01011 } 01012 01013 // 01014 // PFN LOCK IS NOW HELD! 01015 // 01016 01017 // 01018 // Repeat until there are no more control areas - multiple control areas 01019 // for the same image section occur to support user global DLLs - these DLLs 01020 // require data that is shared within a session but not across sessions. 01021 // Note this can only happen for Hydra. 01022 // 01023 01024 do { 01025 01026 // 01027 // Set the being deleted flag and up the number of mapped views 01028 // for the segment. Upping the number of mapped views prevents 01029 // the segment from being deleted and passed to the deletion thread 01030 // while we are forcing a delete. 01031 // 01032 01033 ControlArea->u.Flags.BeingDeleted = 1; 01034 ASSERT (ControlArea->NumberOfMappedViews == 0); 01035 ControlArea->NumberOfMappedViews = 1; 01036 01037 // 01038 // This is a page file backed or image Segment. The Segment is being 01039 // deleted, remove all references to the paging file and physical memory. 01040 // 01041 01042 UNLOCK_PFN (OldIrql); 01043 01044 // 01045 // Delete the section by flushing all modified pages back to the section 01046 // if it is a file and freeing up the pages such that the 01047 // PfnReferenceCount goes to zero. 01048 // 01049 01050 MiCleanSection (ControlArea, TRUE); 01051 01052 // 01053 // Get the next Hydra control area. 01054 // 01055 01056 if (MiHydra == TRUE) { 01057 state = MiCheckControlAreaStatus (CheckBothSection, 01058 SectionObjectPointer, 01059 DelayClose, 01060 &ControlArea, 01061 &OldIrql); 01062 } 01063 else { 01064 state = TRUE; 01065 break; 01066 } 01067 01068 } while (ControlArea); 01069 01070 return state; 01071 } 01072 01073 01074 VOID 01075 MiCleanSection ( 01076 IN PCONTROL_AREA ControlArea, 01077 IN LOGICAL DirtyDataPagesOk 01078 ) 01079 01080 /*++ 01081 01082 Routine Description: 01083 01084 This function examines each prototype PTE in the section and 01085 takes the appropriate action to "delete" the prototype PTE. 01086 01087 If the PTE is dirty and is backed by a file (not a paging file), 01088 the corresponding page is written to the file. 01089 01090 At the completion of this service, the section which was 01091 operated upon is no longer usable. 01092 01093 NOTE - ALL I/O ERRORS ARE IGNORED. IF ANY WRITES FAIL, THE 01094 DIRTY PAGES ARE MARKED CLEAN AND THE SECTION IS DELETED. 01095 01096 Arguments: 01097 01098 ControlArea - Supplies a pointer to the control area for the section. 01099 01100 DirtyDataPagesOk - Supplies TRUE if dirty data pages are ok. If FALSE 01101 is specified then no dirty data pages are expected (as 01102 this is a dereference operation) so any encountered 01103 must be due to pool corruption so bugcheck. 01104 01105 Note that dirty image pages are always discarded. 01106 This should only happen for images that were either 01107 read in from floppies or images with shared global 01108 subsections. 01109 01110 Return Value: 01111 01112 None. 01113 01114 --*/ 01115 01116 { 01117 PMMPTE PointerPte; 01118 PMMPTE LastPte; 01119 PMMPTE LastWritten; 01120 MMPTE PteContents; 01121 PMMPFN Pfn1; 01122 PMMPFN Pfn2; 01123 PMMPTE WrittenPte; 01124 MMPTE WrittenContents; 01125 KIRQL OldIrql; 01126 PMDL Mdl; 01127 PSUBSECTION Subsection; 01128 PPFN_NUMBER Page; 01129 PPFN_NUMBER LastPage; 01130 LARGE_INTEGER StartingOffset; 01131 LARGE_INTEGER TempOffset; 01132 NTSTATUS Status; 01133 IO_STATUS_BLOCK IoStatus; 01134 ULONG WriteNow; 01135 ULONG ImageSection; 01136 ULONG DelayCount; 01137 ULONG First; 01138 KEVENT IoEvent; 01139 PFN_NUMBER MdlHack[(sizeof(MDL)/sizeof(PFN_NUMBER)) + MM_MAXIMUM_WRITE_CLUSTER]; 01140 01141 WriteNow = FALSE; 01142 ImageSection = FALSE; 01143 DelayCount = 0; 01144 01145 if (ControlArea->u.Flags.Image) { 01146 ImageSection = TRUE; 01147 } 01148 ASSERT (ControlArea->FilePointer); 01149 01150 PointerPte = ControlArea->Segment->PrototypePte; 01151 LastPte = PointerPte + ControlArea->Segment->NonExtendedPtes; 01152 01153 Mdl = (PMDL)&MdlHack; 01154 01155 KeInitializeEvent (&IoEvent, NotificationEvent, FALSE); 01156 01157 LastWritten = NULL; 01158 ASSERT (MmModifiedWriteClusterSize == MM_MAXIMUM_WRITE_CLUSTER); 01159 LastPage = NULL; 01160 01161 if (ControlArea->u.Flags.GlobalOnlyPerSession == 0) { 01162 Subsection = (PSUBSECTION)(ControlArea + 1); 01163 } 01164 else { 01165 Subsection = (PSUBSECTION)((PLARGE_CONTROL_AREA)ControlArea + 1); 01166 } 01167 01168 // 01169 // The PFN lock is required for deallocating pages from a paging 01170 // file and for deleting transition PTEs. 01171 // 01172 01173 LOCK_PFN (OldIrql); 01174 01175 // 01176 // Stop the modified page writer from writing pages to this 01177 // file, and if any paging I/O is in progress, wait for it 01178 // to complete. 01179 // 01180 01181 ControlArea->u.Flags.NoModifiedWriting = 1; 01182 01183 while (ControlArea->ModifiedWriteCount != 0) { 01184 01185 // 01186 // There is modified page writing in progess. Set the 01187 // flag in the control area indicating the modified page 01188 // writer should signal when a write to this control area 01189 // is complete. Release the PFN LOCK and wait in an 01190 // atomic operation. Once the wait is satisfied, recheck 01191 // to make sure it was this file's I/O that was written. 01192 // 01193 01194 ControlArea->u.Flags.SetMappedFileIoComplete = 1; 01195 KeEnterCriticalRegion(); 01196 UNLOCK_PFN_AND_THEN_WAIT(OldIrql); 01197 01198 KeWaitForSingleObject(&MmMappedFileIoComplete, 01199 WrPageOut, 01200 KernelMode, 01201 FALSE, 01202 (PLARGE_INTEGER)NULL); 01203 LOCK_PFN (OldIrql); 01204 KeLeaveCriticalRegion(); 01205 } 01206 01207 for (;;) { 01208 01209 First = TRUE; 01210 while (PointerPte < LastPte) { 01211 01212 if ((MiIsPteOnPdeBoundary(PointerPte)) || (First)) { 01213 01214 First = FALSE; 01215 01216 if ((ImageSection) || 01217 (MiCheckProtoPtePageState(PointerPte, FALSE))) { 01218 MiMakeSystemAddressValidPfn (PointerPte); 01219 } else { 01220 01221 // 01222 // Paged pool page is not resident, hence no transition or 01223 // valid prototype PTEs can be present in it. Skip it. 01224 // 01225 01226 PointerPte = (PMMPTE)((((ULONG_PTR)PointerPte | PAGE_SIZE - 1)) + 1); 01227 if (LastWritten != NULL) { 01228 WriteNow = TRUE; 01229 } 01230 goto WriteItOut; 01231 } 01232 } 01233 01234 PteContents = *PointerPte; 01235 01236 // 01237 // Prototype PTEs for Segments backed by paging file 01238 // are either in demand zero, page file format, or transition. 01239 // 01240 01241 if (PteContents.u.Hard.Valid == 1) { 01242 KeBugCheckEx (POOL_CORRUPTION_IN_FILE_AREA, 01243 0x0, 01244 (ULONG_PTR)ControlArea, 01245 (ULONG_PTR)PointerPte, 01246 (ULONG_PTR)PteContents.u.Long); 01247 } 01248 01249 if (PteContents.u.Soft.Prototype == 1) { 01250 01251 // 01252 // This is a normal prototype PTE in mapped file format. 01253 // 01254 01255 if (LastWritten != NULL) { 01256 WriteNow = TRUE; 01257 } 01258 } 01259 else if (PteContents.u.Soft.Transition == 1) { 01260 01261 // 01262 // Prototype PTE in transition, there are 3 possible cases: 01263 // 1. The page is part of an image which is sharable and 01264 // refers to the paging file - dereference page file 01265 // space and free the physical page. 01266 // 2. The page refers to the segment but is not modified - 01267 // free the physical page. 01268 // 3. The page refers to the segment and is modified - 01269 // write the page to the file and free the physical page. 01270 // 01271 01272 Pfn1 = MI_PFN_ELEMENT (PteContents.u.Trans.PageFrameNumber); 01273 01274 if (Pfn1->u3.e2.ReferenceCount != 0) { 01275 if (DelayCount < 20) { 01276 01277 // 01278 // There must be an I/O in progress on this 01279 // page. Wait for the I/O operation to complete. 01280 // 01281 01282 UNLOCK_PFN (OldIrql); 01283 01284 KeDelayExecutionThread (KernelMode, FALSE, (PLARGE_INTEGER)&MmShortTime); 01285 01286 DelayCount += 1; 01287 01288 // 01289 // Redo the loop, if the delay count is greater than 01290 // 20, assume that this thread is deadlocked and 01291 // don't purge this page. The file system can deal 01292 // with the write operation in progress. 01293 // 01294 01295 LOCK_PFN (OldIrql); 01296 MiMakeSystemAddressValidPfn (PointerPte); 01297 continue; 01298 } 01299 #if DBG 01300 // 01301 // The I/O still has not completed, just ignore 01302 // the fact that the I/O is in progress and 01303 // delete the page. 01304 // 01305 01306 KdPrint(("MM:CLEAN - page number %lx has i/o outstanding\n", 01307 PteContents.u.Trans.PageFrameNumber)); 01308 #endif 01309 } 01310 01311 if (Pfn1->OriginalPte.u.Soft.Prototype == 0) { 01312 01313 // 01314 // Paging file reference (case 1). 01315 // 01316 01317 MI_SET_PFN_DELETED (Pfn1); 01318 01319 if (!ImageSection) { 01320 01321 // 01322 // This is not an image section, it must be a 01323 // page file backed section, therefore decrement 01324 // the PFN reference count for the control area. 01325 // 01326 01327 ControlArea->NumberOfPfnReferences -= 1; 01328 ASSERT ((LONG)ControlArea->NumberOfPfnReferences >= 0); 01329 } 01330 #if DBG 01331 else { 01332 // 01333 // This should only happen for images with shared 01334 // global subsections. 01335 // 01336 } 01337 #endif 01338 01339 MiDecrementShareCount (Pfn1->PteFrame); 01340 01341 // 01342 // Check the reference count for the page, if the // reference count is zero and the page is not on the 01343 // freelist, move the page to the free list, if the 01344 // reference count is not zero, ignore this page. When 01345 // the reference count goes to zero, it will be placed 01346 // on the free list. 01347 // 01348 01349 if ((Pfn1->u3.e2.ReferenceCount == 0) && 01350 (Pfn1->u3.e1.PageLocation != FreePageList)) { 01351 01352 MiUnlinkPageFromList (Pfn1); 01353 MiReleasePageFileSpace (Pfn1->OriginalPte); 01354 MiInsertPageInList (MmPageLocationList[FreePageList], 01355 MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents)); 01356 01357 } 01358 PointerPte->u.Long = 0; 01359 01360 // 01361 // If a cluster of pages to write has been completed, 01362 // set the WriteNow flag. 01363 // 01364 01365 if (LastWritten != NULL) { 01366 WriteNow = TRUE; 01367 } 01368 01369 } else { 01370 01371 if ((Pfn1->u3.e1.Modified == 0) || (ImageSection)) { 01372 01373 // 01374 // Non modified or image file page (case 2). 01375 // 01376 01377 MI_SET_PFN_DELETED (Pfn1); 01378 ControlArea->NumberOfPfnReferences -= 1; 01379 ASSERT ((LONG)ControlArea->NumberOfPfnReferences >= 0); 01380 01381 MiDecrementShareCount (Pfn1->PteFrame); 01382 01383 // 01384 // Check the reference count for the page, if the 01385 // reference count is zero and the page is not on 01386 // the freelist, move the page to the free list, 01387 // if the reference count is not zero, ignore this 01388 // page. When the reference count goes to zero, it 01389 // will be placed on the free list. 01390 // 01391 01392 if ((Pfn1->u3.e2.ReferenceCount == 0) && 01393 (Pfn1->u3.e1.PageLocation != FreePageList)) { 01394 01395 MiUnlinkPageFromList (Pfn1); 01396 MiReleasePageFileSpace (Pfn1->OriginalPte); 01397 MiInsertPageInList (MmPageLocationList[FreePageList], 01398 MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents)); 01399 } 01400 01401 PointerPte->u.Long = 0; 01402 01403 // 01404 // If a cluster of pages to write has been 01405 // completed, set the WriteNow flag. 01406 // 01407 01408 if (LastWritten != NULL) { 01409 WriteNow = TRUE; 01410 } 01411 01412 } else { 01413 01414 // 01415 // Modified page backed by the file (case 3). 01416 // Check to see if this is the first page of a 01417 // cluster. 01418 // 01419 01420 if (LastWritten == NULL) { 01421 LastPage = (PPFN_NUMBER)(Mdl + 1); 01422 ASSERT (MiGetSubsectionAddress(&Pfn1->OriginalPte) == 01423 Subsection); 01424 01425 // 01426 // Calculate the offset to read into the file. 01427 // offset = base + ((thispte - basepte) << PAGE_SHIFT) 01428 // 01429 01430 ASSERT (Subsection->ControlArea->u.Flags.Image == 0); 01431 StartingOffset.QuadPart = MiStartingOffset( 01432 Subsection, 01433 Pfn1->PteAddress); 01434 01435 MI_INITIALIZE_ZERO_MDL (Mdl); 01436 Mdl->MdlFlags |= MDL_PAGES_LOCKED; 01437 01438 Mdl->StartVa = 01439 (PVOID)ULongToPtr(Pfn1->u3.e1.PageColor << PAGE_SHIFT); 01440 Mdl->Size = (CSHORT)(sizeof(MDL) + 01441 (sizeof(PFN_NUMBER) * MmModifiedWriteClusterSize)); 01442 } 01443 01444 LastWritten = PointerPte; 01445 Mdl->ByteCount += PAGE_SIZE; 01446 01447 // 01448 // If the cluster is now full, 01449 // set the write now flag. 01450 // 01451 01452 if (Mdl->ByteCount == (PAGE_SIZE * MmModifiedWriteClusterSize)) { 01453 WriteNow = TRUE; 01454 } 01455 01456 MiUnlinkPageFromList (Pfn1); 01457 Pfn1->u3.e1.Modified = 0; 01458 01459 // 01460 // Up the reference count for the physical page as 01461 // there is I/O in progress. 01462 // 01463 01464 MI_ADD_LOCKED_PAGE_CHARGE_FOR_MODIFIED_PAGE(Pfn1, 22); 01465 Pfn1->u3.e2.ReferenceCount += 1; 01466 01467 // 01468 // Clear the modified bit for the page and set the 01469 // write in progress bit. 01470 // 01471 01472 *LastPage = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents); 01473 01474 LastPage += 1; 01475 } 01476 } 01477 } else { 01478 01479 if (IS_PTE_NOT_DEMAND_ZERO (PteContents)) { 01480 MiReleasePageFileSpace (PteContents); 01481 } 01482 PointerPte->u.Long = 0; 01483 01484 // 01485 // If a cluster of pages to write has been completed, 01486 // set the WriteNow flag. 01487 // 01488 01489 if (LastWritten != NULL) { 01490 WriteNow = TRUE; 01491 } 01492 } 01493 01494 // 01495 // Write the current cluster if it is complete, 01496 // full, or the loop is now complete. 01497 // 01498 01499 PointerPte += 1; 01500 WriteItOut: 01501 DelayCount = 0; 01502 01503 if ((WriteNow) || 01504 ((PointerPte == LastPte) && (LastWritten != NULL))) { 01505 01506 // 01507 // Issue the write request. 01508 // 01509 01510 UNLOCK_PFN (OldIrql); 01511 01512 if (DirtyDataPagesOk == FALSE) { 01513 KeBugCheckEx (POOL_CORRUPTION_IN_FILE_AREA, 01514 0x1, 01515 (ULONG_PTR)ControlArea, 01516 (ULONG_PTR)Mdl, 01517 (ULONG_PTR)0); 01518 } 01519 01520 WriteNow = FALSE; 01521 01522 KeClearEvent (&IoEvent); 01523 01524 // 01525 // Make sure the write does not go past the 01526 // end of file. (segment size). 01527 // 01528 01529 ASSERT (Subsection->ControlArea->u.Flags.Image == 0); 01530 01531 TempOffset = MiEndingOffset(Subsection); 01532 01533 if (((UINT64)StartingOffset.QuadPart + Mdl->ByteCount) > 01534 (UINT64)TempOffset.QuadPart) { 01535 01536 ASSERT ((ULONG)(TempOffset.QuadPart - 01537 StartingOffset.QuadPart) > 01538 (Mdl->ByteCount - PAGE_SIZE)); 01539 01540 Mdl->ByteCount = (ULONG)(TempOffset.QuadPart - 01541 StartingOffset.QuadPart); 01542 } 01543 01544 #if DBG 01545 if (MmDebug & MM_DBG_FLUSH_SECTION) { 01546 DbgPrint("MM:flush page write begun %lx\n", 01547 Mdl->ByteCount); 01548 } 01549 #endif //DBG 01550 01551 Status = IoSynchronousPageWrite (ControlArea->FilePointer, 01552 Mdl, 01553 &StartingOffset, 01554 &IoEvent, 01555 &IoStatus ); 01556 01557 if (NT_SUCCESS(Status)) { 01558 01559 KeWaitForSingleObject (&IoEvent, 01560 WrPageOut, 01561 KernelMode, 01562 FALSE, 01563 (PLARGE_INTEGER)NULL); 01564 } 01565 01566 if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) { 01567 MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl); 01568 } 01569 01570 Page = (PPFN_NUMBER)(Mdl + 1); 01571 01572 LOCK_PFN (OldIrql); 01573 01574 if (MiIsPteOnPdeBoundary(PointerPte) == 0) { 01575 01576 // 01577 // The next PTE is not in a different page, make 01578 // sure this page did not leave memory when the 01579 // I/O was in progress. 01580 // 01581 01582 MiMakeSystemAddressValidPfn (PointerPte); 01583 } 01584 01585 // 01586 // I/O complete unlock pages. 01587 // 01588 // NOTE that the error status is ignored. 01589 // 01590 01591 while (Page < LastPage) { 01592 01593 Pfn2 = MI_PFN_ELEMENT (*Page); 01594 01595 // 01596 // Make sure the page is still transition. 01597 // 01598 01599 WrittenPte = Pfn2->PteAddress; 01600 01601 MI_REMOVE_LOCKED_PAGE_CHARGE (Pfn2, 23); 01602 MiDecrementReferenceCount (*Page); 01603 01604 if (!MI_IS_PFN_DELETED (Pfn2)) { 01605 01606 // 01607 // Make sure the prototype PTE is 01608 // still in the working set. 01609 // 01610 01611 MiMakeSystemAddressValidPfn (WrittenPte); 01612 01613 if (Pfn2->PteAddress != WrittenPte) { 01614 01615 // 01616 // The PFN lock was released to make the 01617 // page table page valid, and while it 01618 // was released, the physical page 01619 // was reused. Go onto the next one. 01620 // 01621 01622 Page += 1; 01623 continue; 01624 } 01625 01626 WrittenContents = *WrittenPte; 01627 01628 if ((WrittenContents.u.Soft.Prototype == 0) && 01629 (WrittenContents.u.Soft.Transition == 1)) { 01630 01631 MI_SET_PFN_DELETED (Pfn2); 01632 ControlArea->NumberOfPfnReferences -= 1; 01633 ASSERT ((LONG)ControlArea->NumberOfPfnReferences >= 0); 01634 01635 MiDecrementShareCount (Pfn2->PteFrame); 01636 01637 // 01638 // Check the reference count for the page, 01639 // if the reference count is zero and the 01640 // page is not on the freelist, move the page 01641 // to the free list, if the reference 01642 // count is not zero, ignore this page. 01643 // When the reference count goes to zero, 01644 // it will be placed on the free list. 01645 // 01646 01647 if ((Pfn2->u3.e2.ReferenceCount == 0) && 01648 (Pfn2->u3.e1.PageLocation != FreePageList)) { 01649 01650 MiUnlinkPageFromList (Pfn2); 01651 MiReleasePageFileSpace (Pfn2->OriginalPte); 01652 MiInsertPageInList ( 01653 MmPageLocationList[FreePageList], 01654 *Page); 01655 } 01656 } 01657 WrittenPte->u.Long = 0; 01658 } 01659 Page += 1; 01660 } 01661 01662 // 01663 // Indicate that there is no current cluster being built. 01664 // 01665 01666 LastWritten = NULL; 01667 } 01668 01669 } // end while 01670 01671 // 01672 // Get the next subsection if any. 01673 // 01674 01675 if (Subsection->NextSubsection == (PSUBSECTION)NULL) { 01676 break; 01677 } 01678 01679 Subsection = Subsection->NextSubsection; 01680 PointerPte = Subsection->SubsectionBase; 01681 LastPte = PointerPte + Subsection->PtesInSubsection; 01682 01683 } // end for 01684 01685 ControlArea->NumberOfMappedViews = 0; 01686 01687 ASSERT (ControlArea->NumberOfPfnReferences == 0); 01688 01689 if (ControlArea->u.Flags.FilePointerNull == 0) { 01690 ControlArea->u.Flags.FilePointerNull = 1; 01691 01692 if (ControlArea->u.Flags.Image) { 01693 01694 if (MiHydra == TRUE) { 01695 MiRemoveImageSectionObject (ControlArea->FilePointer, 01696 ControlArea); 01697 } 01698 else { 01699 ControlArea->FilePointer->SectionObjectPointer->ImageSectionObject = NULL; 01700 } 01701 } 01702 else { 01703 01704 ASSERT (((PCONTROL_AREA)(ControlArea->FilePointer->SectionObjectPointer->DataSectionObject)) != NULL); 01705 ControlArea->FilePointer->SectionObjectPointer->DataSectionObject = NULL; 01706 01707 } 01708 } 01709 UNLOCK_PFN (OldIrql); 01710 01711 // 01712 // Delete the segment structure. 01713 // 01714 01715 MiSegmentDelete (ControlArea->Segment); 01716 01717 return; 01718 } 01719 01720 NTSTATUS 01721 MmGetFileNameForSection ( 01722 IN HANDLE Section, 01723 OUT PSTRING FileName 01724 ) 01725 01726 /*++ 01727 01728 Routine Description: 01729 01730 This function returns the file name for the corresponding section. 01731 01732 Arguments: 01733 01734 Section - Supplies the handle of the section to get the name of. 01735 01736 FileName - Returns the name of the corresponding section. 01737 01738 Return Value: 01739 01740 TBS 01741 01742 Environment: 01743 01744 Kernel mode, APC_LEVEL or below, no mutexes held. 01745 01746 --*/ 01747 01748 { 01749 01750 PSECTION SectionObject; 01751 POBJECT_NAME_INFORMATION FileNameInfo; 01752 ULONG whocares; 01753 NTSTATUS Status; 01754 ULONG Dereference; 01755 01756 Dereference = TRUE; 01757 01758 #define xMAX_NAME 1024 01759 01760 if ( (ULONG_PTR)Section & 1 ) { 01761 SectionObject = (PSECTION)((ULONG_PTR)Section & ~1); 01762 Dereference = FALSE; 01763 } else { 01764 Status = ObReferenceObjectByHandle ( Section, 01765 0, 01766 MmSectionObjectType, 01767 KernelMode, 01768 (PVOID *)&SectionObject, 01769 NULL ); 01770 01771 if (!NT_SUCCESS(Status)) { 01772 return Status; 01773 } 01774 } 01775 01776 if (SectionObject->u.Flags.Image == 0) { 01777 if ( Dereference ) 01778 ObDereferenceObject (SectionObject); 01779 return STATUS_SECTION_NOT_IMAGE; 01780 } 01781 01782 FileNameInfo = ExAllocatePoolWithTag (PagedPool, xMAX_NAME, ' mM'); 01783 01784 if ( !FileNameInfo ) { 01785 if ( Dereference ) 01786 ObDereferenceObject (SectionObject); 01787 return STATUS_NO_MEMORY; 01788 } 01789 01790 Status = ObQueryNameString( 01791 SectionObject->Segment->ControlArea->FilePointer, 01792 FileNameInfo, 01793 xMAX_NAME, 01794 &whocares 01795 ); 01796 01797 if ( Dereference ) 01798 ObDereferenceObject (SectionObject); 01799 01800 if ( !NT_SUCCESS(Status) ) { 01801 ExFreePool(FileNameInfo); 01802 return Status; 01803 } 01804 01805 FileName->Length = 0; 01806 FileName->MaximumLength = (FileNameInfo->Name.Length/sizeof(WCHAR)) + 1; 01807 FileName->Buffer = ExAllocatePoolWithTag (PagedPool, 01808 FileName->MaximumLength, 01809 ' mM'); 01810 if ( !FileName->Buffer ) { 01811 ExFreePool(FileNameInfo); 01812 return STATUS_NO_MEMORY; 01813 } 01814 RtlUnicodeStringToAnsiString((PANSI_STRING)FileName,&FileNameInfo->Name,FALSE); 01815 FileName->Buffer[FileName->Length] = '\0'; 01816 ExFreePool(FileNameInfo); 01817 01818 return STATUS_SUCCESS; 01819 } 01820 01821 01822 VOID 01823 MiCheckControlArea ( 01824 IN PCONTROL_AREA ControlArea, 01825 IN PEPROCESS CurrentProcess, 01826 IN KIRQL PreviousIrql 01827 ) 01828 01829 /*++ 01830 01831 Routine Description: 01832 01833 This routine checks the reference counts for the specified 01834 control area, and if the counts are all zero, it marks the 01835 control area for deletion and queues it to the deletion thread. 01836 01837 01838 *********************** NOTE ******************************** 01839 This routine returns with the PFN LOCK RELEASED!!!!! 01840 01841 Arguments: 01842 01843 ControlArea - Supplies a pointer to the control area to check. 01844 01845 CurrentProcess - Supplies a pointer to the current process if and ONLY 01846 IF the working set lock is held. 01847 01848 PreviousIrql - Supplies the previous IRQL. 01849 01850 Return Value: 01851 01852 NONE. 01853 01854 Environment: 01855 01856 Kernel mode, PFN lock held, PFN lock released upon return!!! 01857 01858 --*/ 01859 01860 { 01861 PEVENT_COUNTER PurgeEvent; 01862 ULONG DeleteOnClose; 01863 ULONG DereferenceSegment; 01864 01865 PurgeEvent = NULL; 01866 DeleteOnClose = FALSE; 01867 DereferenceSegment = FALSE; 01868 01869 MM_PFN_LOCK_ASSERT(); 01870 if ((ControlArea->NumberOfMappedViews == 0) && 01871 (ControlArea->NumberOfSectionReferences == 0)) { 01872 01873 ASSERT (ControlArea->NumberOfUserReferences == 0); 01874 01875 if (ControlArea->FilePointer != (PFILE_OBJECT)NULL) { 01876 01877 if (ControlArea->NumberOfPfnReferences == 0) { 01878 01879 // 01880 // There are no views and no physical pages referenced 01881 // by the Segment, dereference the Segment object. 01882 // 01883 01884 ControlArea->u.Flags.BeingDeleted = 1; 01885 DereferenceSegment = TRUE; 01886 01887 ASSERT (ControlArea->u.Flags.FilePointerNull == 0); 01888 ControlArea->u.Flags.FilePointerNull = 1; 01889 01890 if (ControlArea->u.Flags.Image) { 01891 01892 if (MiHydra == TRUE) { 01893 MiRemoveImageSectionObject (ControlArea->FilePointer, ControlArea); 01894 } 01895 else { 01896 ((PCONTROL_AREA)(ControlArea->FilePointer->SectionObjectPointer->ImageSectionObject)) = NULL; 01897 } 01898 01899 } 01900 else { 01901 01902 ASSERT (((PCONTROL_AREA)(ControlArea->FilePointer->SectionObjectPointer->DataSectionObject)) != NULL); 01903 ((PCONTROL_AREA)(ControlArea->FilePointer->SectionObjectPointer->DataSectionObject)) = NULL; 01904 01905 } 01906 } else { 01907 01908 // 01909 // Insert this segment into the unused segment list (unless 01910 // it is already on the list). 01911 // 01912 01913 if (ControlArea->DereferenceList.Flink == NULL) { 01914 InsertTailList ( &MmUnusedSegmentList, 01915 &ControlArea->DereferenceList); 01916 MI_UNUSED_SEGMENTS_INSERT_CHARGE (ControlArea); 01917 } 01918 01919 // 01920 // Indicate if this section should be deleted now that 01921 // the reference counts are zero. 01922 // 01923 01924 DeleteOnClose = ControlArea->u.Flags.DeleteOnClose; 01925 01926 // 01927 // The number of mapped views are zero, the number of 01928 // section references are zero, but there are some 01929 // pages of the file still resident. If this is 01930 // an image with Global Memory, "purge" the subsections 01931 // which contain the global memory and reset them to 01932 // point back to the file. 01933 // 01934 01935 if (ControlArea->u.Flags.GlobalMemory == 1) { 01936 ASSERT (ControlArea->u.Flags.Image == 1); 01937 01938 ControlArea->u.Flags.BeingPurged = 1; 01939 ControlArea->NumberOfMappedViews = 1; 01940 01941 MiPurgeImageSection (ControlArea, CurrentProcess); 01942 01943 ControlArea->u.Flags.BeingPurged = 0; 01944 ControlArea->NumberOfMappedViews -= 1; 01945 if ((ControlArea->NumberOfMappedViews == 0) && 01946 (ControlArea->NumberOfSectionReferences == 0) && 01947 (ControlArea->NumberOfPfnReferences == 0)) { 01948 01949 ControlArea->u.Flags.BeingDeleted = 1; 01950 DereferenceSegment = TRUE; 01951 ControlArea->u.Flags.FilePointerNull = 1; 01952 01953 if (MiHydra == TRUE) { 01954 MiRemoveImageSectionObject (ControlArea->FilePointer, ControlArea); 01955 } 01956 else { 01957 ((PCONTROL_AREA)(ControlArea->FilePointer->SectionObjectPointer->ImageSectionObject)) = NULL; 01958 } 01959 01960 } else { 01961 01962 PurgeEvent = ControlArea->WaitingForDeletion; 01963 ControlArea->WaitingForDeletion = NULL; 01964 } 01965 } 01966 01967 // 01968 // If delete on close is set and the segment was 01969 // not deleted, up the count of mapped views so the 01970 // control area will not be deleted when the PFN lock 01971 // is released. 01972 // 01973 01974 if (DeleteOnClose && !DereferenceSegment) { 01975 ControlArea->NumberOfMappedViews = 1; 01976 ControlArea->u.Flags.BeingDeleted = 1; 01977 } 01978 } 01979 01980 } else { 01981 01982 // 01983 // This Segment is backed by a paging file, dereference the 01984 // Segment object when the number of views goes from 1 to 0 01985 // without regard to the number of PFN references. 01986 // 01987 01988 ControlArea->u.Flags.BeingDeleted = 1; 01989 DereferenceSegment = TRUE; 01990 } 01991 } 01992 else if (ControlArea->WaitingForDeletion != NULL) { 01993 PurgeEvent = ControlArea->WaitingForDeletion; 01994 ControlArea->WaitingForDeletion = NULL; 01995 } 01996 01997 UNLOCK_PFN (PreviousIrql); 01998 01999 if (DereferenceSegment || DeleteOnClose) { 02000 02001 // 02002 // Release the working set mutex, if it is held as the object 02003 // management routines may page fault, etc.. 02004 // 02005 02006 if (CurrentProcess) { 02007 UNLOCK_WS_UNSAFE (CurrentProcess); 02008 } 02009 02010 if (DereferenceSegment) { 02011 02012 // 02013 // Delete the segment. 02014 // 02015 02016 MiSegmentDelete (ControlArea->Segment); 02017 02018 } else { 02019 02020 // 02021 // The segment should be forced closed now. 02022 // 02023 02024 MiCleanSection (ControlArea, TRUE); 02025 } 02026 02027 ASSERT (PurgeEvent == NULL); 02028 02029 // 02030 // Reacquire the working set lock, if a process was specified. 02031 // 02032 02033 if (CurrentProcess) { 02034 LOCK_WS_UNSAFE (CurrentProcess); 02035 } 02036 02037 } else { 02038 02039 // 02040 // If any threads are waiting for the segment, indicate the 02041 // the purge operation has completed. 02042 // 02043 02044 if (PurgeEvent != NULL) { 02045 KeSetEvent (&PurgeEvent->Event, 0, FALSE); 02046 } 02047 02048 if (MmUnusedSegmentPagedPoolUsage > MmMaxUnusedSegmentPagedPoolUsage || 02049 MmUnusedSegmentNonPagedPoolUsage > MmMaxUnusedSegmentNonPagedPoolUsage) { 02050 KeSetEvent (&MmUnusedSegmentCleanup, 0, FALSE); 02051 } 02052 } 02053 02054 return; 02055 } 02056 02057 02058 VOID 02059 MiCheckForControlAreaDeletion ( 02060 IN PCONTROL_AREA ControlArea 02061 ) 02062 02063 /*++ 02064 02065 Routine Description: 02066 02067 This routine checks the reference counts for the specified 02068 control area, and if the counts are all zero, it marks the 02069 control area for deletion and queues it to the deletion thread. 02070 02071 Arguments: 02072 02073 ControlArea - Supplies a pointer to the control area to check. 02074 02075 Return Value: 02076 02077 None. 02078 02079 Environment: 02080 02081 Kernel mode, PFN lock held. 02082 02083 --*/ 02084 02085 { 02086 KIRQL OldIrql; 02087 02088 MM_PFN_LOCK_ASSERT(); 02089 if ((ControlArea->NumberOfPfnReferences == 0) && 02090 (ControlArea->NumberOfMappedViews == 0) && 02091 (ControlArea->NumberOfSectionReferences == 0 )) { 02092 02093 // 02094 // This segment is no longer mapped in any address space 02095 // nor are there any prototype PTEs within the segment 02096 // which are valid or in a transition state. Queue 02097 // the segment to the segment-dereferencer thread 02098 // which will dereference the segment object, potentially 02099 // causing the segment to be deleted. 02100 // 02101 02102 ControlArea->u.Flags.BeingDeleted = 1; 02103 ASSERT (ControlArea->u.Flags.FilePointerNull == 0); 02104 ControlArea->u.Flags.FilePointerNull = 1; 02105 02106 if (ControlArea->u.Flags.Image) { 02107 02108 if (MiHydra == TRUE) { 02109 MiRemoveImageSectionObject (ControlArea->FilePointer, 02110 ControlArea); 02111 } 02112 else { 02113 ((PCONTROL_AREA)(ControlArea->FilePointer->SectionObjectPointer->ImageSectionObject)) = NULL; 02114 } 02115 02116 } 02117 else { 02118 ((PCONTROL_AREA)(ControlArea->FilePointer->SectionObjectPointer->DataSectionObject)) = 02119 NULL; 02120 } 02121 02122 ExAcquireSpinLock (&MmDereferenceSegmentHeader.Lock, &OldIrql); 02123 02124 ASSERT (ControlArea->DereferenceList.Flink != NULL); 02125 02126 // 02127 // Remove the entry from the unused segment list and put it 02128 // on the dereference list. 02129 // 02130 02131 RemoveEntryList (&ControlArea->DereferenceList); 02132 02133 MI_UNUSED_SEGMENTS_REMOVE_CHARGE (ControlArea); 02134 02135 InsertTailList (&MmDereferenceSegmentHeader.ListHead, 02136 &ControlArea->DereferenceList); 02137 ExReleaseSpinLock (&MmDereferenceSegmentHeader.Lock, OldIrql); 02138 02139 KeReleaseSemaphore (&MmDereferenceSegmentHeader.Semaphore, 02140 0L, 02141 1L, 02142 FALSE); 02143 } 02144 return; 02145 } 02146 02147 02148 BOOLEAN 02149 MiCheckControlAreaStatus ( 02150 IN SECTION_CHECK_TYPE SectionCheckType, 02151 IN PSECTION_OBJECT_POINTERS SectionObjectPointers, 02152 IN ULONG DelayClose, 02153 OUT PCONTROL_AREA *ControlAreaOut, 02154 OUT PKIRQL PreviousIrql 02155 ) 02156 02157 /*++ 02158 02159 Routine Description: 02160 02161 This routine checks the status of the control area for the specified 02162 SectionObjectPointers. If the control area is in use, that is, the 02163 number of section references and the number of mapped views are not 02164 both zero, no action is taken and the function returns FALSE. 02165 02166 If there is no control area associated with the specified 02167 SectionObjectPointers or the control area is in the process of being 02168 created or deleted, no action is taken and the value TRUE is returned. 02169 02170 If, there are no section objects and the control area is not being 02171 created or deleted, the address of the control area is returned 02172 in the ControlArea argument, the address of a pool block to free 02173 is returned in the SegmentEventOut argument and the PFN_LOCK is 02174 still held at the return. 02175 02176 Arguments: 02177 02178 *SegmentEventOut - Returns a pointer to NonPaged Pool which much be 02179 freed by the caller when the PFN_LOCK is released. 02180 This value is NULL if no pool is allocated and the 02181 PFN_LOCK is not held. 02182 02183 SectionCheckType - Supplies the type of section to check on, one of 02184 CheckImageSection, CheckDataSection, CheckBothSection. 02185 02186 SectionObjectPointers - Supplies the section object pointers through 02187 which the control area can be located. 02188 02189 DelayClose - Supplies a boolean which if TRUE and the control area 02190 is being used, the delay on close field should be set 02191 in the control area. 02192 02193 *ControlAreaOut - Returns the address of the control area. 02194 02195 PreviousIrql - Returns, in the case the PFN_LOCK is held, the previous 02196 IRQL so the lock can be released properly. 02197 02198 Return Value: 02199 02200 FALSE if the control area is in use, TRUE if the control area is gone or 02201 in the process or being created or deleted. 02202 02203 Environment: 02204 02205 Kernel mode, PFN lock NOT held. 02206 02207 --*/ 02208 02209 02210 { 02211 PEVENT_COUNTER IoEvent; 02212 PEVENT_COUNTER SegmentEvent; 02213 ULONG DeallocateSegmentEvent = TRUE; 02214 PCONTROL_AREA ControlArea; 02215 ULONG SectRef; 02216 KIRQL OldIrql; 02217 02218 // 02219 // Allocate an event to wait on in case the segment is in the 02220 // process of being deleted. This event cannot be allocated 02221 // with the PFN database locked as pool expansion would deadlock. 02222 // 02223 02224 *ControlAreaOut = NULL; 02225 02226 // 02227 // Acquire the PFN lock and examine the section object pointer 02228 // value within the file object. 02229 // 02230 02231 // 02232 // File control blocks live in non-paged pool. 02233 // 02234 02235 LOCK_PFN (OldIrql); 02236 02237 SegmentEvent = MiGetEventCounter (); 02238 02239 while (SegmentEvent == NULL) { 02240 UNLOCK_PFN (OldIrql); 02241 KeDelayExecutionThread (KernelMode, FALSE, (PLARGE_INTEGER)&MmShortTime); 02242 LOCK_PFN (OldIrql); 02243 SegmentEvent = MiGetEventCounter (); 02244 } 02245 02246 if (SectionCheckType != CheckImageSection) { 02247 ControlArea = ((PCONTROL_AREA)(SectionObjectPointers->DataSectionObject)); 02248 } else { 02249 ControlArea = ((PCONTROL_AREA)(SectionObjectPointers->ImageSectionObject)); 02250 } 02251 02252 if (ControlArea == NULL) { 02253 02254 if (SectionCheckType != CheckBothSection) { 02255 02256 // 02257 // This file no longer has an associated segment. 02258 // 02259 02260 MiFreeEventCounter (SegmentEvent, TRUE); 02261 UNLOCK_PFN (OldIrql); 02262 return TRUE; 02263 } else { 02264 ControlArea = ((PCONTROL_AREA)(SectionObjectPointers->ImageSectionObject)); 02265 if (ControlArea == NULL) { 02266 02267 // 02268 // This file no longer has an associated segment. 02269 // 02270 02271 MiFreeEventCounter (SegmentEvent, TRUE); 02272 UNLOCK_PFN (OldIrql); 02273 return TRUE; 02274 } 02275 } 02276 } 02277 02278 // 02279 // Depending on the type of section, check for the pertinent 02280 // reference count being non-zero. 02281 // 02282 02283 if (SectionCheckType != CheckUserDataSection) { 02284 SectRef = ControlArea->NumberOfSectionReferences; 02285 } else { 02286 SectRef = ControlArea->NumberOfUserReferences; 02287 } 02288 02289 if ((SectRef != 0) || 02290 (ControlArea->NumberOfMappedViews != 0) || 02291 (ControlArea->u.Flags.BeingCreated)) { 02292 02293 02294 // 02295 // The segment is currently in use or being created. 02296 // 02297 02298 if (DelayClose) { 02299 02300 // 02301 // The section should be deleted when the reference 02302 // counts are zero, set the delete on close flag. 02303 // 02304 02305 ControlArea->u.Flags.DeleteOnClose = 1; 02306 } 02307 02308 MiFreeEventCounter (SegmentEvent, TRUE); 02309 UNLOCK_PFN (OldIrql); 02310 return FALSE; 02311 } 02312 02313 // 02314 // The segment has no references, delete it. If the segment 02315 // is already being deleted, set the event field in the control 02316 // area and wait on the event. 02317 // 02318 02319 if (ControlArea->u.Flags.BeingDeleted) { 02320 02321 // 02322 // The segment object is in the process of being deleted. 02323 // Check to see if another thread is waiting for the deletion, 02324 // otherwise create and event object to wait upon. 02325 // 02326 02327 if (ControlArea->WaitingForDeletion == NULL) { 02328 02329 // 02330 // Create an event and put its address in the control area. 02331 // 02332 02333 DeallocateSegmentEvent = FALSE; 02334 ControlArea->WaitingForDeletion = SegmentEvent; 02335 IoEvent = SegmentEvent; 02336 } else { 02337 IoEvent = ControlArea->WaitingForDeletion; 02338 IoEvent->RefCount += 1; 02339 } 02340 02341 // 02342 // Release the mutex and wait for the event. 02343 // 02344 02345 KeEnterCriticalRegion(); 02346 UNLOCK_PFN_AND_THEN_WAIT(OldIrql); 02347 02348 KeWaitForSingleObject(&IoEvent->Event, 02349 WrPageOut, 02350 KernelMode, 02351 FALSE, 02352 (PLARGE_INTEGER)NULL); 02353 02354 LOCK_PFN (OldIrql); 02355 KeLeaveCriticalRegion(); 02356 02357 MiFreeEventCounter (IoEvent, TRUE); 02358 if (DeallocateSegmentEvent) { 02359 MiFreeEventCounter (SegmentEvent, TRUE); 02360 } 02361 UNLOCK_PFN (OldIrql); 02362 return TRUE; 02363 } 02364 02365 // 02366 // Return with the PFN database locked. 02367 // 02368 02369 MiFreeEventCounter (SegmentEvent, FALSE); 02370 *ControlAreaOut = ControlArea; 02371 *PreviousIrql = OldIrql; 02372 return FALSE; 02373 } 02374 02375 02376 PEVENT_COUNTER 02377 MiGetEventCounter ( 02378 ) 02379 02380 /*++ 02381 02382 Routine Description: 02383 02384 This function maintains a list of "events" to allow waiting 02385 on segment operations (deletion, creation, purging). 02386 02387 Arguments: 02388 02389 None. 02390 02391 Return Value: 02392 02393 Event to be used for waiting (stored into the control area) or NULL if 02394 no event could be allocated. 02395 02396 Environment: 02397 02398 Kernel mode, PFN lock held. 02399 02400 --*/ 02401 02402 { 02403 KIRQL OldIrql; 02404 PEVENT_COUNTER Support; 02405 PLIST_ENTRY NextEntry; 02406 02407 MM_PFN_LOCK_ASSERT(); 02408 02409 if (MmEventCountList.Count == 0) { 02410 ASSERT (IsListEmpty(&MmEventCountList.ListHead)); 02411 OldIrql = APC_LEVEL; 02412 UNLOCK_PFN (OldIrql); 02413 Support = ExAllocatePoolWithTag (NonPagedPool, 02414 sizeof(EVENT_COUNTER), 02415 'xEmM'); 02416 if (Support == NULL) { 02417 LOCK_PFN (OldIrql); 02418 return NULL; 02419 } 02420 KeInitializeEvent (&Support->Event, NotificationEvent, FALSE); 02421 LOCK_PFN (OldIrql); 02422 } else { 02423 ASSERT (!IsListEmpty(&MmEventCountList.ListHead)); 02424 MmEventCountList.Count -= 1; 02425 NextEntry = RemoveHeadList (&MmEventCountList.ListHead); 02426 Support = CONTAINING_RECORD (NextEntry, 02427 EVENT_COUNTER, 02428 ListEntry ); 02429 //ASSERT (Support->RefCount == 0); 02430 KeClearEvent (&Support->Event); 02431 } 02432 Support->RefCount = 1; 02433 Support->ListEntry.Flink = NULL; 02434 return Support; 02435 } 02436 02437 VOID 02438 MiFreeEventCounter ( 02439 IN PEVENT_COUNTER Support, 02440 IN ULONG Flush 02441 ) 02442 02443 /*++ 02444 02445 Routine Description: 02446 02447 This routine frees an event counter back to the free list. 02448 02449 Arguments: 02450 02451 Support - Supplies a pointer to the event counter. 02452 02453 Flush - Supplies TRUE if the PFN lock can be released and the event 02454 counter pool block actually freed. The PFN lock will be 02455 reacquired before returning. 02456 02457 Return Value: 02458 02459 None. 02460 02461 Environment: 02462 02463 Kernel mode, PFN lock held. 02464 02465 --*/ 02466 02467 { 02468 02469 MM_PFN_LOCK_ASSERT(); 02470 02471 ASSERT (Support->RefCount != 0); 02472 ASSERT (Support->ListEntry.Flink == NULL); 02473 Support->RefCount -= 1; 02474 if (Support->RefCount == 0) { 02475 InsertTailList (&MmEventCountList.ListHead, 02476 &Support->ListEntry); 02477 MmEventCountList.Count += 1; 02478 } 02479 if ((Flush) && (MmEventCountList.Count > 4)) { 02480 MiFlushEventCounter(); 02481 } 02482 return; 02483 } 02484 02485 02486 VOID 02487 MiFlushEventCounter ( 02488 ) 02489 02490 /*++ 02491 02492 Routine Description: 02493 02494 This routine examines the list of event counters and attempts 02495 to free up to 10 (if there are more than 4). 02496 02497 It will release and reacquire the PFN lock when it frees the 02498 event counters! 02499 02500 Arguments: 02501 02502 None. 02503 02504 Return Value: 02505 02506 None. 02507 02508 Environment: 02509 02510 Kernel mode, PFN lock held. 02511 02512 --*/ 02513 02514 02515 { 02516 KIRQL OldIrql; 02517 PEVENT_COUNTER Support[10]; 02518 ULONG i = 0; 02519 PLIST_ENTRY NextEntry; 02520 02521 MM_PFN_LOCK_ASSERT(); 02522 02523 while ((MmEventCountList.Count > 4) && (i < 10)) { 02524 NextEntry = RemoveHeadList (&MmEventCountList.ListHead); 02525 Support[i] = CONTAINING_RECORD (NextEntry, 02526 EVENT_COUNTER, 02527 ListEntry ); 02528 Support[i]->ListEntry.Flink = NULL; 02529 i += 1; 02530 MmEventCountList.Count -= 1; 02531 } 02532 02533 if (i == 0) { 02534 return; 02535 } 02536 02537 OldIrql = APC_LEVEL; 02538 UNLOCK_PFN (OldIrql); 02539 02540 do { 02541 i -= 1; 02542 ExFreePool(Support[i]); 02543 } while (i > 0); 02544 02545 LOCK_PFN (OldIrql); 02546 02547 return; 02548 } 02549 02550 02551 BOOLEAN 02552 MmCanFileBeTruncated ( 02553 IN PSECTION_OBJECT_POINTERS SectionPointer, 02554 IN PLARGE_INTEGER NewFileSize 02555 ) 02556 02557 /*++ 02558 02559 Routine Description: 02560 02561 This routine does the following: 02562 02563 1. Checks to see if a image section is in use for the file, 02564 if so it returns FALSE. 02565 02566 2. Checks to see if a user section exists for the file, if 02567 it does, it checks to make sure the new file size is greater 02568 than the size of the file, if not it returns FALSE. 02569 02570 3. If no image section exists, and no user created data section 02571 exists or the file's size is greater, then TRUE is returned. 02572 02573 Arguments: 02574 02575 SectionPointer - Supplies a pointer to the section object pointers 02576 from the file object. 02577 02578 NewFileSize - Supplies a pointer to the size the file is getting set to. 02579 02580 Return Value: 02581 02582 TRUE if the file can be truncated, FALSE if it cannot be. 02583 02584 Environment: 02585 02586 Kernel mode. 02587 02588 --*/ 02589 02590 { 02591 LARGE_INTEGER LocalOffset; 02592 KIRQL OldIrql; 02593 02594 // 02595 // Capture caller's file size, since we may modify it. 02596 // 02597 02598 if (ARGUMENT_PRESENT(NewFileSize)) { 02599 02600 LocalOffset = *NewFileSize; 02601 NewFileSize = &LocalOffset; 02602 } 02603 02604 if (MiCanFileBeTruncatedInternal( SectionPointer, NewFileSize, FALSE, &OldIrql )) { 02605 02606 UNLOCK_PFN (OldIrql); 02607 return TRUE; 02608 } 02609 02610 return FALSE; 02611 } 02612 02613 ULONG 02614 MiCanFileBeTruncatedInternal ( 02615 IN PSECTION_OBJECT_POINTERS SectionPointer, 02616 IN PLARGE_INTEGER NewFileSize OPTIONAL, 02617 IN LOGICAL BlockNewViews, 02618 OUT PKIRQL PreviousIrql 02619 ) 02620 02621 /*++ 02622 02623 Routine Description: 02624 02625 This routine does the following: 02626 02627 1. Checks to see if a image section is in use for the file, 02628 if so it returns FALSE. 02629 02630 2. Checks to see if a user section exists for the file, if 02631 it does, it checks to make sure the new file size is greater 02632 than the size of the file, if not it returns FALSE. 02633 02634 3. If no image section exists, and no user created data section 02635 exists or the files size is greater, then TRUE is returned. 02636 02637 Arguments: 02638 02639 SectionPointer - Supplies a pointer to the section object pointers 02640 from the file object. 02641 02642 NewFileSize - Supplies a pointer to the size the file is getting set to. 02643 02644 BlockNewViews - Supplies TRUE if the caller will block new views while 02645 the operation (usually a purge) proceeds. This allows 02646 this routine to return TRUE even if the user has section 02647 references, provided the user currently has no mapped views. 02648 02649 PreviousIrql - If returning TRUE, returns Irql to use when unlocking 02650 Pfn database. 02651 02652 Return Value: 02653 02654 TRUE if the file can be truncated (PFN locked). 02655 FALSE if it cannot be truncated (PFN not locked). 02656 02657 Environment: 02658 02659 Kernel mode. 02660 02661 --*/ 02662 02663 { 02664 KIRQL OldIrql; 02665 LARGE_INTEGER SegmentSize; 02666 PCONTROL_AREA ControlArea; 02667 PSUBSECTION Subsection; 02668 02669 if (!MmFlushImageSection (SectionPointer, MmFlushForWrite)) { 02670 return FALSE; 02671 } 02672 02673 LOCK_PFN (OldIrql); 02674 02675 ControlArea = (PCONTROL_AREA)(SectionPointer->DataSectionObject); 02676 02677 if (ControlArea != NULL) { 02678 02679 if (ControlArea->u.Flags.BeingCreated || 02680 ControlArea->u.Flags.BeingDeleted) { 02681 goto UnlockAndReturn; 02682 } 02683 02684 // 02685 // If there are user references and the size is less than the 02686 // size of the user view, don't allow the truncation. 02687 // 02688 02689 if ((ControlArea->NumberOfUserReferences != 0) && 02690 ((BlockNewViews == FALSE) || (ControlArea->NumberOfMappedViews != 0))) { 02691 02692 // 02693 // You cannot truncate the entire section if there is a user 02694 // reference. 02695 // 02696 02697 if (!ARGUMENT_PRESENT(NewFileSize)) { 02698 goto UnlockAndReturn; 02699 } 02700 02701 // 02702 // Locate last subsection and get total size. 02703 // 02704 02705 if (ControlArea->u.Flags.GlobalOnlyPerSession == 0) { 02706 Subsection = (PSUBSECTION)(ControlArea + 1); 02707 } 02708 else { 02709 Subsection = (PSUBSECTION)((PLARGE_CONTROL_AREA)ControlArea + 1); 02710 } 02711 02712 while (Subsection->NextSubsection != NULL) { 02713 Subsection = Subsection->NextSubsection; 02714 } 02715 02716 ASSERT (Subsection->ControlArea->u.Flags.Image == 0); 02717 02718 SegmentSize = MiEndingOffset(Subsection); 02719 02720 if ((UINT64)NewFileSize->QuadPart < (UINT64)SegmentSize.QuadPart) { 02721 goto UnlockAndReturn; 02722 } 02723 02724 // 02725 // If there are mapped views, we will skip the last page 02726 // of the section if the size passed in falls in that page. 02727 // The caller (like Cc) may want to clear this fractional page. 02728 // 02729 02730 SegmentSize.QuadPart += PAGE_SIZE - 1; 02731 SegmentSize.LowPart &= ~(PAGE_SIZE - 1); 02732 if ((UINT64)NewFileSize->QuadPart < (UINT64)SegmentSize.QuadPart) { 02733 *NewFileSize = SegmentSize; 02734 } 02735 } 02736 } 02737 02738 *PreviousIrql = OldIrql; 02739 return TRUE; 02740 02741 UnlockAndReturn: 02742 UNLOCK_PFN (OldIrql); 02743 return FALSE; 02744 } 02745 02746 02747 VOID 02748 MiRemoveUnusedSegments ( 02749 VOID 02750 ) 02751 02752 /*++ 02753 02754 Routine Description: 02755 02756 This routine removes unused segments (no section references, 02757 no mapped views only PFN references that are in transition state). 02758 02759 Arguments: 02760 02761 None. 02762 02763 Return Value: 02764 02765 None. 02766 02767 Environment: 02768 02769 Kernel mode. 02770 02771 --*/ 02772 02773 { 02774 KIRQL OldIrql; 02775 PLIST_ENTRY NextEntry; 02776 PCONTROL_AREA ControlArea; 02777 NTSTATUS Status; 02778 ULONG ConsecutiveFileLockFailures; 02779 ULONG ConsecutivePagingIOs; 02780 PSUBSECTION Subsection; 02781 PSUBSECTION LastSubsection; 02782 PMMPTE PointerPte; 02783 PMMPTE LastPte; 02784 IO_STATUS_BLOCK IoStatus; 02785 LOGICAL DirtyPagesOk; 02786 02787 ConsecutivePagingIOs = 0; 02788 ConsecutiveFileLockFailures = 0; 02789 02790 while ((MmUnusedSegmentPagedPoolUsage > (MmMaxUnusedSegmentPagedPoolUsage - MmUnusedSegmentPagedPoolReduction)) || 02791 (MmUnusedSegmentNonPagedPoolUsage > MmMaxUnusedSegmentNonPagedPoolUsage - MmUnusedSegmentNonPagedPoolReduction) || 02792 (MmUnusedSegmentForceFree != 0)) { 02793 02794 // 02795 // Eliminate some of the unused segments which are only 02796 // kept in memory because they contain transition pages. 02797 // 02798 02799 Status = STATUS_SUCCESS; 02800 02801 LOCK_PFN (OldIrql); 02802 02803 if (IsListEmpty(&MmUnusedSegmentList)) { 02804 02805 // 02806 // There is nothing in the list, rewait. 02807 // 02808 02809 MmUnusedSegmentForceFree = 0; 02810 ASSERT (MmUnusedSegmentCount == 0); 02811 ASSERT (MmUnusedSegmentPagedPoolUsage == 0); 02812 ASSERT (MmUnusedSegmentNonPagedPoolUsage == 0); 02813 UNLOCK_PFN (OldIrql); 02814 break; 02815 } 02816 02817 NextEntry = RemoveHeadList(&MmUnusedSegmentList); 02818 02819 ControlArea = CONTAINING_RECORD( NextEntry, 02820 CONTROL_AREA, 02821 DereferenceList ); 02822 #if DBG 02823 if (MmDebug & MM_DBG_SECTIONS) { 02824 DbgPrint("MM: cleaning segment %lx control %lx\n", 02825 ControlArea->Segment, ControlArea); 02826 } 02827 #endif 02828 02829 MI_UNUSED_SEGMENTS_REMOVE_CHARGE (ControlArea); 02830 02831 if (MmUnusedSegmentForceFree != 0) { 02832 MmUnusedSegmentForceFree -= 1; 02833 } 02834 02835 // 02836 // Indicate this entry is not on any list. 02837 // 02838 02839 #if DBG 02840 if (ControlArea->u.Flags.BeingDeleted == 0) { 02841 if (ControlArea->u.Flags.Image) { 02842 ASSERT (((PCONTROL_AREA)(ControlArea->FilePointer->SectionObjectPointer->ImageSectionObject)) != NULL); 02843 } else { 02844 ASSERT (((PCONTROL_AREA)(ControlArea->FilePointer->SectionObjectPointer->DataSectionObject)) != NULL); 02845 } 02846 } 02847 #endif //DBG 02848 02849 // 02850 // Set the flink to NULL indicating this control area 02851 // is not on any lists. 02852 // 02853 02854 ControlArea->DereferenceList.Flink = NULL; 02855 02856 if ((ControlArea->NumberOfMappedViews == 0) && 02857 (ControlArea->NumberOfSectionReferences == 0) && 02858 (ControlArea->u.Flags.BeingDeleted == 0)) { 02859 02860 // 02861 // If there is paging I/O in progress on this 02862 // segment, just put this at the tail of the list, as 02863 // the call to MiCleanSegment would block waiting 02864 // for the I/O to complete. As this could tie up 02865 // the thread, don't do it. Check if these are the only 02866 // types of segments on the dereference list so we don't 02867 // spin forever and wedge the system. 02868 // 02869 02870 if (ControlArea->ModifiedWriteCount > 0) { 02871 02872 InsertTailList ( &MmUnusedSegmentList, 02873 &ControlArea->DereferenceList); 02874 02875 MI_UNUSED_SEGMENTS_INSERT_CHARGE (ControlArea); 02876 02877 UNLOCK_PFN (OldIrql); 02878 02879 ConsecutivePagingIOs += 1; 02880 if (ConsecutivePagingIOs > 10) { 02881 KeDelayExecutionThread (KernelMode, FALSE, &MmShortTime); 02882 ConsecutivePagingIOs = 0; 02883 } 02884 continue; 02885 } 02886 ConsecutivePagingIOs = 0; 02887 02888 // 02889 // Up the number of mapped views to prevent other threads 02890 // from freeing this. Clear the accessed bit so we'll know 02891 // if another thread opens the control area while we're flushing 02892 // and closes it before we finish the flush - the other thread 02893 // may have modified some pages which can then cause our 02894 // MiCleanSection call (which expects no modified pages in this 02895 // case) to deadlock with the filesystem. 02896 // 02897 02898 ControlArea->NumberOfMappedViews = 1; 02899 ControlArea->u.Flags.Accessed = 0; 02900 02901 if (ControlArea->u.Flags.Image == 0) { 02902 02903 UNLOCK_PFN (OldIrql); 02904 02905 if (ControlArea->u.Flags.GlobalOnlyPerSession == 0) { 02906 Subsection = (PSUBSECTION)(ControlArea + 1); 02907 } 02908 else { 02909 Subsection = (PSUBSECTION)((PLARGE_CONTROL_AREA)ControlArea + 1); 02910 } 02911 02912 PointerPte = &Subsection->SubsectionBase[0]; 02913 LastSubsection = Subsection; 02914 while (LastSubsection->NextSubsection != NULL) { 02915 LastSubsection = LastSubsection->NextSubsection; 02916 } 02917 LastPte = &LastSubsection->SubsectionBase 02918 [LastSubsection->PtesInSubsection - 1]; 02919 02920 // 02921 // Preacquire the file to prevent deadlocks with other flushers 02922 // Also mark ourself as a top level IRP so the filesystem knows 02923 // we are holding no other resources and that it can unroll if 02924 // it needs to in order to avoid deadlock. Don't hold this 02925 // protection any longer than we need to. 02926 // 02927 02928 FsRtlAcquireFileForCcFlush (ControlArea->FilePointer); 02929 02930 IoSetTopLevelIrp((PIRP)FSRTL_FSP_TOP_LEVEL_IRP); 02931 02932 Status = MiFlushSectionInternal (PointerPte, 02933 LastPte, 02934 Subsection, 02935 LastSubsection, 02936 FALSE, 02937 FALSE, 02938 &IoStatus); 02939 02940 IoSetTopLevelIrp((PIRP)NULL); 02941 02942 // 02943 // Now release the file. 02944 // 02945 02946 FsRtlReleaseFileForCcFlush (ControlArea->FilePointer); 02947 02948 LOCK_PFN (OldIrql); 02949 } 02950 02951 // 02952 // Before checking for any failure codes, see if any other 02953 // threads accessed the control area while the flush was ongoing. 02954 // 02955 // Note that beyond the case of another thread currently using 02956 // the control area, the more subtle one is where another 02957 // thread accessed the control area and modified some pages. 02958 // The flush needs to redone (so the clean is guaranteed to work) 02959 // before another clean can be issued. 02960 // 02961 // If any of these cases have occurred, grant this control area 02962 // a reprieve. 02963 // 02964 02965 if (!((ControlArea->NumberOfMappedViews == 1) && 02966 (ControlArea->u.Flags.Accessed == 0) && 02967 (ControlArea->NumberOfSectionReferences == 0) && 02968 (ControlArea->u.Flags.BeingDeleted == 0))) { 02969 02970 ControlArea->NumberOfMappedViews -= 1; 02971 02972 // 02973 // If the other thread(s) are done with this control area, 02974 // it MUST be requeued here - otherwise if there are any 02975 // pages in the control area, when they are reclaimed, 02976 // MiCheckForControlAreaDeletion checks for and expects 02977 // the control area to be queued on the unused segment list. 02978 // 02979 // Note this must be done very carefully because if the other 02980 // threads are not done with the control area, it had better 02981 // not get put on the unused segment list. 02982 // 02983 02984 // 02985 // Need to do the equivalent of a MiCheckControlArea here. 02986 // or reprocess. Only iff mappedview & sectref = 0. 02987 // 02988 02989 if ((ControlArea->NumberOfMappedViews == 0) && 02990 (ControlArea->NumberOfSectionReferences == 0) && 02991 (ControlArea->u.Flags.BeingDeleted == 0)) { 02992 02993 ASSERT (ControlArea->u.Flags.Accessed == 1); 02994 ASSERT(ControlArea->DereferenceList.Flink == NULL); 02995 02996 InsertTailList ( &MmUnusedSegmentList, 02997 &ControlArea->DereferenceList); 02998 MI_UNUSED_SEGMENTS_INSERT_CHARGE (ControlArea); 02999 } 03000 03001 UNLOCK_PFN (OldIrql); 03002 continue; 03003 } 03004 03005 if (!NT_SUCCESS(Status)) { 03006 03007 // 03008 // If the filesystem told us it had to unroll to avoid 03009 // deadlock OR we hit a mapped writer collision OR 03010 // the error occurred on a local file: 03011 // 03012 // Then requeue this at the end so we can try again later. 03013 // 03014 // Any other errors for networked files are assumed to be 03015 // permanent (ie: the link may have gone down for an indefinite 03016 // period), so these sections are cleaned regardless. 03017 // 03018 03019 if ((Status == STATUS_FILE_LOCK_CONFLICT) || 03020 (Status == STATUS_MAPPED_WRITER_COLLISION) || 03021 (ControlArea->u.Flags.Networked == 0)) { 03022 03023 ASSERT(ControlArea->DereferenceList.Flink == NULL); 03024 03025 ControlArea->NumberOfMappedViews -= 1; 03026 03027 if (Status == STATUS_FILE_LOCK_CONFLICT) { 03028 ConsecutiveFileLockFailures += 1; 03029 } 03030 else { 03031 ConsecutiveFileLockFailures = 0; 03032 } 03033 03034 InsertTailList ( &MmUnusedSegmentList, 03035 &ControlArea->DereferenceList); 03036 MI_UNUSED_SEGMENTS_INSERT_CHARGE (ControlArea); 03037 03038 UNLOCK_PFN (OldIrql); 03039 03040 // 03041 // 10 consecutive file locking failures means we need to 03042 // yield the processor to allow the filesystem to unjam. 03043 // Nothing magic about 10, just a number so it 03044 // gives the worker threads a chance to run. 03045 // 03046 03047 if (ConsecutiveFileLockFailures >= 10) { 03048 KeDelayExecutionThread (KernelMode, FALSE, &MmShortTime); 03049 ConsecutiveFileLockFailures = 0; 03050 } 03051 continue; 03052 } 03053 DirtyPagesOk = TRUE; 03054 } 03055 else { 03056 ConsecutiveFileLockFailures = 0; 03057 DirtyPagesOk = FALSE; 03058 } 03059 03060 ControlArea->u.Flags.BeingDeleted = 1; 03061 03062 // 03063 // Don't let any pages be written by the modified 03064 // page writer from this point on. 03065 // 03066 03067 ControlArea->u.Flags.NoModifiedWriting = 1; 03068 ASSERT (ControlArea->u.Flags.FilePointerNull == 0); 03069 UNLOCK_PFN (OldIrql); 03070 03071 MiCleanSection (ControlArea, DirtyPagesOk); 03072 03073 } else { 03074 03075 // 03076 // The segment was not eligible for deletion. Just leave 03077 // it off the unused segment list and continue the loop. 03078 // 03079 03080 UNLOCK_PFN (OldIrql); 03081 ConsecutivePagingIOs = 0; 03082 } 03083 03084 } //end while 03085 }

Generated on Sat May 15 19:41:44 2004 for test by doxygen 1.3.7