test: flushsec.c File Reference

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

# flushsec.c File Reference

`#include "mi.h"`

Go to the source code of this file.

## Functions

PSUBSECTION | MiGetSystemCacheSubsection (IN PVOID BaseAddress, IN PEPROCESS Process, OUT PMMPTE *ProtoPte)
VOID | MiFlushDirtyBitsToPfn (IN PMMPTE PointerPte, IN PMMPTE LastPte, IN PEPROCESS Process, IN BOOLEAN SystemCache)
ULONG FASTCALL | MiCheckProtoPtePageState (IN PMMPTE PrototypePte, IN ULONG PfnLockHeld)
NTSTATUS | NtFlushVirtualMemory (IN HANDLE ProcessHandle, IN OUT PVOID *BaseAddress, IN OUT PSIZE_T RegionSize, OUT PIO_STATUS_BLOCK IoStatus)
VOID | MiFlushAcquire (IN PCONTROL_AREA ControlArea)
VOID | MiFlushRelease (IN PCONTROL_AREA ControlArea)
NTSTATUS | MmFlushVirtualMemory (IN PEPROCESS Process, IN OUT PVOID *BaseAddress, IN OUT PSIZE_T RegionSize, OUT PIO_STATUS_BLOCK IoStatus)
NTSTATUS | MmFlushSection (IN PSECTION_OBJECT_POINTERS SectionObjectPointer, IN PLARGE_INTEGER Offset, IN SIZE_T RegionSize, OUT PIO_STATUS_BLOCK IoStatus, IN ULONG AcquireFile)
LONGLONG | MiStartingOffset (IN PSUBSECTION Subsection, IN PMMPTE PteAddress)
LARGE_INTEGER | MiEndingOffset (IN PSUBSECTION Subsection)
NTSTATUS | MiFlushSectionInternal (IN PMMPTE StartingPte, IN PMMPTE FinalPte, IN PSUBSECTION FirstSubsection, IN PSUBSECTION LastSubsection, IN ULONG Synchronize, IN LOGICAL WriteInProgressOk, OUT PIO_STATUS_BLOCK IoStatus)
BOOLEAN | MmPurgeSection (IN PSECTION_OBJECT_POINTERS SectionObjectPointer, IN PLARGE_INTEGER Offset, IN SIZE_T RegionSize, IN ULONG IgnoreCacheViews)
BOOLEAN | MmFlushImageSection (IN PSECTION_OBJECT_POINTERS SectionPointer, IN MMFLUSH_TYPE FlushType)

## Variables

POBJECT_TYPE | IoFileObjectType

## Function Documentation

ULONG FASTCALL MiCheckProtoPtePageState | ( | IN PMMPTE | PrototypePte,
|  | IN ULONG | PfnLockHeld
| ) |

Definition at line 2427 of file flushsec.c.

References ActiveAndValid, FALSE, MI_GET_PAGE_FRAME_FROM_PTE, MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE, MI_PFN_ELEMENT, MiCheckPdeForPagedPool(), MiGetPdeAddress, MiGetPteAddress, MiMakeSystemAddressValidPfn(), PrototypePte, TRUE, _MMPTE::u, _MMPFN::u2, and _MMPFN::u3.

    02434                    :
    02435
    02436     Checks the state of the page containing the specified
    02437     prototype PTE.
    02438
    02439     If the page is valid or transition and has transition or valid prototype
    02440     PTEs contained with it, TRUE is returned and the page is made valid
    02441     (if transition).  Otherwise return FALSE indicating no prototype
    02442     PTEs within this page are of interest.
    02443
    02444 Arguments:
    02445
    02446     PrototypePte - Supplies a pointer to a prototype PTE within the page.
    02447
    02448 Return Value:
    02449
    02450     TRUE if the page containing the proto PTE was made resident.
    02451     FALSE if otherwise.
    02452
    02453 --*/
    02454
    02455 {
    02456     PMMPTE PointerPte;
    02457     MMPTE PteContents;
    02458     PFN_NUMBER PageFrameIndex;
    02459     PMMPFN Pfn;
    02460
    02461 #if defined (_WIN64)
    02462     //
    02463     // First check whether the page directory page is present.  Since there
    02464     // is no lazy loading of PPEs, the validity check alone is sufficient.
    02465     //
    02466
    02467     PointerPte = MiGetPdeAddress(PrototypePte);
    02468     PteContents = *PointerPte;
    02469
    02470     if (PteContents.u.Hard.Valid == 0) {
    02471         return FALSE;
    02472     }
    02473 #endif
    02474
    02475     PointerPte = MiGetPteAddress(PrototypePte);
    02476 #if !defined (_WIN64)
    02477     if (PointerPte->u.Hard.Valid == 0) {
    02478         MiCheckPdeForPagedPool (PrototypePte);
    02479     }
    02480 #endif
    02481     PteContents = *PointerPte;
    02482
    02483     if (PteContents.u.Hard.Valid == 1) {
    02484         PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&PteContents);
    02485         Pfn = MI_PFN_ELEMENT (PageFrameIndex);
    02486         if (Pfn->u2.ShareCount != 1) {
    02487             return TRUE;
    02488         }
    02489     } else if ((PteContents.u.Soft.Prototype == 0) &&
    02490                (PteContents.u.Soft.Transition == 1)) {
    02491
    02492         //
    02493         // Transition, if on standby or modified, return false.
    02494         //
    02495
    02496         PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents);
    02497         Pfn = MI_PFN_ELEMENT (PageFrameIndex);
    02498         if (Pfn->u3.e1.PageLocation >= ActiveAndValid) {
    02499             if (PfnLockHeld) {
    02500                 MiMakeSystemAddressValidPfn (PrototypePte);
    02501             }
    02502             return TRUE;
    02503         }
    02504     }
    02505
    02506     //
    02507     // Page is not resident or is on standby / modified list.
    02508     //
    02509
    02510     return FALSE;
    02511 }
    }

LARGE_INTEGER MiEndingOffset | ( | IN PSUBSECTION | Subsection | ) |

Definition at line 965 of file flushsec.c.

References Mi4KStartFromSubsection, MM4K_SHIFT, and MMSECTOR_SHIFT.

Referenced by MiCanFileBeTruncatedInternal(), MiCleanSection(), MiFlushSectionInternal(), and MiResolveMappedFileFault().

    00971                    :
    00972
    00973     This function calculates the last valid file offset in a given subsection.
    00974     offset.  Note that images are stored in 512-byte units whereas data is
    00975     stored in 4K units.
    00976
    00977     When this is all debugged, this should be made into a macro.
    00978
    00979 Arguments:
    00980
    00981     Subsection - Supplies a subsection to reference for the file address.
    00982
    00983     PteAddress - Supplies a PTE within the subsection
    00984
    00985 Return Value:
    00986
    00987     Returns the file offset to obtain the backing data from.
    00988
    00989 --*/
    00990
    00991 {
    00992     LARGE_INTEGER FileByteOffset;
    00993
    00994     if (Subsection->ControlArea->u.Flags.Image == 1) {
    00995         FileByteOffset.QuadPart =
    00996             (Subsection->StartingSector + Subsection->NumberOfFullSectors) <<
    00997                 MMSECTOR_SHIFT;
    00998     }
    00999     else {
    01000         Mi4KStartFromSubsection (&FileByteOffset, Subsection);
    01001
    01002         FileByteOffset.QuadPart += Subsection->NumberOfFullSectors;
    01003
    01004         FileByteOffset.QuadPart = FileByteOffset.QuadPart << MM4K_SHIFT;
    01005     }
    01006
    01007     FileByteOffset.QuadPart += Subsection->u.SubsectionFlags.SectorEndOffset;
    01008
    01009     return FileByteOffset;
    01010 }

VOID MiFlushAcquire | ( | IN PCONTROL_AREA | ControlArea | ) |

Definition at line 223 of file flushsec.c.

References ASSERT, LOCK_PFN, and UNLOCK_PFN.

Referenced by MmFlushVirtualMemory(), and NtAllocateVirtualMemory().

    00229                    :
    00230
    00231     This is a helper routine to reference count the control area if needed
    00232     during a flush section call to prevent the section object from being
    00233     deleted while the flush is ongoing.
    00234
    00235 Arguments:
    00236
    00237     ControlArea - Supplies a pointer to the control area.
    00238
    00239 Return Value:
    00240
    00241     None.
    00242
    00243 --*/
    00244
    00245 {
    00246     KIRQL OldIrql;
    00247
    00248     LOCK_PFN (OldIrql);
    00249
    00250     ASSERT ((LONG)ControlArea->NumberOfMappedViews >= 1);
    00251     ControlArea->NumberOfMappedViews += 1;
    00252
    00253     UNLOCK_PFN (OldIrql);
    00254 }

VOID MiFlushDirtyBitsToPfn | ( | IN PMMPTE | PointerPte,
|  | IN PMMPTE | LastPte,
|  | IN PEPROCESS | Process,
|  | IN BOOLEAN | SystemCache
| ) |

Definition at line 2281 of file flushsec.c.

References FALSE, KeFlushSingleTb(), LOCK_PFN, MI_IS_PTE_DIRTY, MI_PFN_ELEMENT, MI_SET_PTE_CLEAN, MiDoesPdeExistAndMakeValid(), MiDoesPpeExistAndMakeValid, MiGetPteAddress, MiGetVirtualAddressMappedByPte, MiIsPteOnPdeBoundary, PAGE_SIZE, TRUE, _MMPTE::u, _MMPFN::u3, UNLOCK_PFN, and VOID().

Referenced by MmFlushVirtualMemory().

    02288 {
    02289     KIRQL OldIrql;
    02290     MMPTE PteContents;
    02291     PMMPFN Pfn1;
    02292     PVOID Va;
    02293     PMMPTE PointerPde;
    02294     PMMPTE PointerPpe;
    02295     ULONG Waited;
    02296
    02297     Va = MiGetVirtualAddressMappedByPte (PointerPte);
    02298     LOCK_PFN (OldIrql);
    02299
    02300     while (PointerPte <= LastPte) {
    02301
    02302         PteContents = *PointerPte;
    02303
    02304         if ((PteContents.u.Hard.Valid == 1) &&
    02305             (MI_IS_PTE_DIRTY (PteContents))) {
    02306
    02307             //
    02308             // Flush the modify bit to the PFN database.
    02309             //
    02310
    02311             Pfn1 = MI_PFN_ELEMENT (PteContents.u.Hard.PageFrameNumber);
    02312             Pfn1->u3.e1.Modified = 1;
    02313
    02314             MI_SET_PTE_CLEAN (PteContents);
    02315
    02316             //
    02317             // No need to capture the PTE contents as we are going to
    02318             // write the page anyway and the Modify bit will be cleared
    02319             // before the write is done.
    02320             //
    02321
    02322             (VOID)KeFlushSingleTb (Va,
    02323                                    FALSE,
    02324                                    SystemCache,
    02325                                    (PHARDWARE_PTE)PointerPte,
    02326                                    PteContents.u.Flush);
    02327         }
    02328
    02329         Va = (PVOID)((PCHAR)Va + PAGE_SIZE);
    02330         PointerPte += 1;
    02331
    02332         if (MiIsPteOnPdeBoundary (PointerPte)) {
    02333
    02334             PointerPde = MiGetPteAddress (PointerPte);
    02335
    02336             while (PointerPte <= LastPte) {
    02337
    02338                 PointerPpe = MiGetPteAddress (PointerPde);
    02339
    02340                 if (!MiDoesPpeExistAndMakeValid (PointerPpe,
    02341                                                  Process,
    02342                                                  TRUE,
    02343                                                  &Waited)) {
    02344
    02345                     //
    02346                     // No page directory page exists for this address.
    02347                     //
    02348
    02349                     PointerPpe += 1;
    02350
    02351                     PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
    02352                     PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
    02353                 }
    02354                 else {
    02355
    02356                     Waited = 0;
    02357
    02358                     if (!MiDoesPdeExistAndMakeValid (PointerPde,
    02359                                                      Process,
    02360                                                      TRUE,
    02361                                                      &Waited)) {
    02362
    02363                         //
    02364                         // No page table page exists for this address.
    02365                         //
    02366
    02367                         PointerPde += 1;
    02368
    02369                         PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
    02370                     }
    02371                     else {
    02372
    02373                         //
    02374                         // If the PFN lock (and accordingly the WS mutex) was
    02375                         // released and reacquired we must retry the operation.
    02376                         //
    02377
    02378                         if (Waited != 0) {
    02379                             continue;
    02380                         }
    02381
    02382                         //
    02383                         // The PFN lock has been held since we acquired the
    02384                         // page directory parent, ie: this PTE we can operate on
    02385                         // immediately.
    02386                         //
    02387
    02388                         break;
    02389                     }
    02390                 }
    02391             }
    02392
    02393             Va = MiGetVirtualAddressMappedByPte (PointerPte);
    02394         }
    02395     }
    02396
    02397     UNLOCK_PFN (OldIrql);
    02398     return;
    02399 }

VOID MiFlushRelease | ( | IN PCONTROL_AREA | ControlArea | ) |

Definition at line 258 of file flushsec.c.

References ASSERT, LOCK_PFN, MiCheckControlArea(), and NULL.

Referenced by MmFlushVirtualMemory(), and NtAllocateVirtualMemory().

    00264                    :
    00265
    00266     This is a helper routine to release the control area reference needed
    00267     during a flush section call.
    00268
    00269 Arguments:
    00270
    00271     ControlArea - Supplies a pointer to the control area.
    00272
    00273 Return Value:
    00274
    00275     None.
    00276
    00277 --*/
    00278
    00279 {
    00280     KIRQL OldIrql;
    00281
    00282     LOCK_PFN (OldIrql);
    00283
    00284     ASSERT ((LONG)ControlArea->NumberOfMappedViews >= 1);
    00285     ControlArea->NumberOfMappedViews -= 1;
    00286
    00287     //
    00288     // Check to see if the control area should be deleted.  This
    00289     // will release the PFN lock.
    00290     //
    00291
    00292     MiCheckControlArea (ControlArea, NULL, OldIrql);
    00293 }

NTSTATUS MiFlushSectionInternal | ( | IN PMMPTE | StartingPte,
|  | IN PMMPTE | FinalPte,
|  | IN PSUBSECTION | FirstSubsection,
|  | IN PSUBSECTION | LastSubsection,
|  | IN ULONG | Synchronize,
|  | IN LOGICAL | WriteInProgressOk,
|  | OUT PIO_STATUS_BLOCK | IoStatus
| ) |

Definition at line 1014 of file flushsec.c.

References ASSERT, _MDL::ByteCount, DbgPrint, FALSE, _CONTROL_AREA::FilePointer, _CONTROL_AREA::FlushInProgressCount, IoSynchronousPageWrite(), KeClearEvent, KeDelayExecutionThread(), KeEnterCriticalRegion, KeInitializeEvent, KeLeaveCriticalRegion, KePulseEvent(), KernelMode, KeWaitForSingleObject(), LOCK_PFN, _MDL::MappedSystemVa, MDL_MAPPED_TO_SYSTEM_VA, MDL_PAGES_LOCKED, _MDL::MdlFlags, MI_ADD_LOCKED_PAGE_CHARGE, MI_ADD_LOCKED_PAGE_CHARGE_FOR_MODIFIED_PAGE, MI_GET_PAGE_FRAME_FROM_PTE, MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE, MI_INITIALIZE_ZERO_MDL, MI_PFN_ELEMENT, MI_REMOVE_LOCKED_PAGE_CHARGE, MiCheckProtoPtePageState(), MiDecrementReferenceCount(), MiEndingOffset(), MiIoRetryLevel, MiIsPteOnPdeBoundary, MiMakeSystemAddressValidPfn(), MiStartingOffset(), MiUnlinkPageFromList(), Mm30Milliseconds, MM_DBG_FLUSH_SECTION, MM_MAXIMUM_DISK_IO_SIZE, MmCollidedFlushEvent, MmIsRetryIoStatus, MmModifiedWriteClusterSize, MmOneSecond, MmUnmapLockedPages(), _SUBSECTION::NextSubsection, NT_SUCCESS, NTSTATUS(), NULL, _CONTROL_AREA::NumberOfPfnReferences, _MMPFN::OriginalPte, PAGE_SHIFT, PAGE_SIZE, _MMPFN::PteAddress, _SUBSECTION::PtesInSubsection, _MDL::Size, _MDL::StartVa, Status, STATUS_MAPPED_WRITER_COLLISION, _SUBSECTION::SubsectionBase, TRUE, _CONTROL_AREA::u, _MMPTE::u, _MMPFN::u3, UNLOCK_PFN, UNLOCK_PFN_AND_THEN_WAIT, and WrPageOut.

Referenced by MiRemoveUnusedSegments(), MmFlushSection(), and MmFlushVirtualMemory().

    01026                    :
    01027
    01028     This function flushes to the backing file any modified pages within
    01029     the specified range of the section.  The parameters describe the
    01030     section's prototype PTEs (start and end) and the subsections
    01031     which correspond to the starting and ending PTE.
    01032
    01033     Each PTE in the subsection between the specified start and end
    01034     is examined and if the page is either valid or transition AND
    01035     the page has been modified, the modify bit is cleared in the PFN
    01036     database and the page is flushed to its backing file.
    01037
    01038 Arguments:
    01039
    01040     StartingPte - Supplies a pointer to the first prototype PTE to
    01041                   be examined for flushing.
    01042
    01043     FinalPte - Supplies a pointer to the last prototype PTE to be
    01044                examined for flushing.
    01045
    01046     FirstSubsection - Supplies the subsection that contains the
    01047                       StartingPte.
    01048
    01049     LastSubsection - Supplies the subsection that contains the
    01050                      FinalPte.
    01051
    01052     Synchronize - Supplies TRUE if synchronization with all threads
    01053                   doing flush operations to this section should occur.
    01054
    01055     WriteInProgressOk - Supplies TRUE if the caller can tolerate a write
    01056                         already in progress for any dirty pages.
    01057
    01058     IoStatus - Returns the value of the IoStatus for the last attempted
    01059                I/O operation.
    01060
    01061 Return Value:
    01062
    01063     Returns status of the operation.
    01064
    01065 --*/
    01066
    01067 {
    01068     PCONTROL_AREA ControlArea;
    01069     PMMPTE PointerPte;
    01070     PMMPTE LastPte;
    01071     PMMPTE LastWritten;
    01072     MMPTE PteContents;
    01073     PMMPFN Pfn1;
    01074     PMMPFN Pfn2;
    01075     KIRQL OldIrql;
    01076     PMDL Mdl;
    01077     KEVENT IoEvent;
    01078     PSUBSECTION Subsection;
    01079     PPFN_NUMBER Page;
    01080     PFN_NUMBER PageFrameIndex;
    01081     PPFN_NUMBER LastPage;
    01082     NTSTATUS Status;
    01083     UINT64 StartingOffset;
    01084     UINT64 TempOffset;
    01085     BOOLEAN WriteNow;
    01086     LOGICAL Bail;
    01087     PFN_NUMBER MdlHack[(sizeof(MDL)/sizeof(PFN_NUMBER)) + (MM_MAXIMUM_DISK_IO_SIZE / PAGE_SIZE) + 1];
    01088     ULONG ReflushCount;
    01089
    01090     WriteNow = FALSE;
    01091     Bail = FALSE;
    01092
    01093     IoStatus->Status = STATUS_SUCCESS;
    01094     IoStatus->Information = 0;
    01095     Mdl = (PMDL)&MdlHack[0];
    01096
    01097     KeInitializeEvent (&IoEvent, NotificationEvent, FALSE);
    01098
    01099     FinalPte += 1;  // Point to 1 past the last one.
    01100
    01101     LastWritten = NULL;
    01102     LastPage = 0;
    01103     Subsection = FirstSubsection;
    01104     PointerPte = StartingPte;
    01105     ControlArea = FirstSubsection->ControlArea;
    01106
    01107     LOCK_PFN (OldIrql);
    01108
    01109     ASSERT (ControlArea->u.Flags.Image == 0);
    01110
    01111     if (ControlArea->NumberOfPfnReferences == 0) {
    01112
    01113         //
    01114         // No transition or valid prototype PTEs present, hence
    01115         // no need to flush anything.
    01116         //
    01117
    01118         UNLOCK_PFN (OldIrql);
    01119         return STATUS_SUCCESS;
    01120     }
    01121
    01122     while ((Synchronize) && (ControlArea->FlushInProgressCount != 0)) {
    01123
    01124         //
    01125         // Another thread is currently performing a flush operation on
    01126         // this file.  Wait for that flush to complete.
    01127         //
    01128
    01129         KeEnterCriticalRegion();
    01130         ControlArea->u.Flags.CollidedFlush = 1;
    01131         UNLOCK_PFN_AND_THEN_WAIT(OldIrql);
    01132
    01133         KeWaitForSingleObject (&MmCollidedFlushEvent,
    01134                                WrPageOut,
    01135                                KernelMode,
    01136                                FALSE,
    01137                                &MmOneSecond);
    01138         LOCK_PFN (OldIrql);
    01139         KeLeaveCriticalRegion();
    01140     }
    01141
    01142     ControlArea->FlushInProgressCount += 1;
    01143
    01144     for (;;) {
    01145
    01146         if (LastSubsection != Subsection) {
    01147
    01148             //
    01149             // Flush to the last PTE in this subsection.
    01150             //
    01151
    01152             LastPte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
    01153         } else {
    01154
    01155             //
    01156             // Flush to the end of the range.
    01157             //
    01158
    01159             LastPte = FinalPte;
    01160         }
    01161
    01162         //
    01163         // If the prototype PTEs are paged out or have a share count
    01164         // of 1, they cannot contain any transition or valid PTEs.
    01165         //
    01166
    01167         if (!MiCheckProtoPtePageState(PointerPte, TRUE)) {
    01168             PointerPte = (PMMPTE)(((ULONG_PTR)PointerPte | (PAGE_SIZE - 1)) + 1);
    01169         }
    01170
    01171         while (PointerPte < LastPte) {
    01172
    01173             if (MiIsPteOnPdeBoundary(PointerPte)) {
    01174
    01175                 //
    01176                 // We are on a page boundary, make sure this PTE is resident.
    01177                 //
    01178
    01179                 if (!MiCheckProtoPtePageState(PointerPte, TRUE)) {
    01180                     PointerPte = (PMMPTE)((PCHAR)PointerPte + PAGE_SIZE);
    01181
    01182                     //
    01183                     // If there are dirty pages to be written, write them
    01184                     // now as we are skipping over PTEs.
    01185                     //
    01186
    01187                     if (LastWritten != NULL) {
    01188                         WriteNow = TRUE;
    01189                         goto CheckForWrite;
    01190                     }
    01191                     continue;
    01192                 }
    01193             }
    01194
    01195             PteContents = *PointerPte;
    01196
    01197             if ((PteContents.u.Hard.Valid == 1) ||
    01198                    ((PteContents.u.Soft.Prototype == 0) &&
    01199                      (PteContents.u.Soft.Transition == 1))) {
    01200
    01201                 //
    01202                 // Prototype PTE in transition, there are 3 possible cases:
    01203                 //  1. The page is part of an image which is sharable and
    01204                 //     refers to the paging file - dereference page file
    01205                 //     space and free the physical page.
    01206                 //  2. The page refers to the segment but is not modified -
    01207                 //     free the physical page.
    01208                 //  3. The page refers to the segment and is modified -
    01209                 //     write the page to the file and free the physical page.
    01210                 //
    01211
    01212                 if (PteContents.u.Hard.Valid == 1) {
    01213                     PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&PteContents);
    01214                 } else {
    01215                     PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents);
    01216                 }
    01217
    01218                 Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
    01219                 ASSERT (Pfn1->OriginalPte.u.Soft.Prototype == 1);
    01220                 ASSERT (Pfn1->OriginalPte.u.Hard.Valid == 0);
    01221
    01222                 //
    01223                 // If the page is modified OR a write is in progress
    01224                 // flush it.  The write in progress case catches problems
    01225                 // where the modified page write continually writes a
    01226                 // page and gets errors writing it, by writing pages
    01227                 // in this state, the error will be propagated back to
    01228                 // the caller.
    01229                 //
    01230
    01231                 if ((Pfn1->u3.e1.Modified == 1) ||
    01232                     (Pfn1->u3.e1.WriteInProgress)) {
    01233
    01234                     if ((WriteInProgressOk == FALSE) &&
    01235                         (Pfn1->u3.e1.WriteInProgress)) {
    01236
    01237                             PointerPte = LastPte;
    01238                             Bail = TRUE;
    01239
    01240                             if (LastWritten != NULL) {
    01241                                 WriteNow = TRUE;
    01242                             }
    01243                             goto CheckForWrite;
    01244                     }
    01245
    01246                     if (LastWritten == NULL) {
    01247
    01248                         //
    01249                         // This is the first page of a cluster, initialize
    01250                         // the MDL, etc.
    01251                         //
    01252
    01253                         LastPage = (PPFN_NUMBER)(Mdl + 1);
    01254
    01255                         //
    01256                         // Calculate the offset to read into the file.
    01257                         //  offset = base + ((thispte - basepte) << PAGE_SHIFT)
    01258                         //
    01259
    01260                         StartingOffset = (UINT64) MiStartingOffset (
    01261                                                              Subsection,
    01262                                                              Pfn1->PteAddress);
    01263
    01264                         MI_INITIALIZE_ZERO_MDL (Mdl);
    01265
    01266                         Mdl->MdlFlags |= MDL_PAGES_LOCKED;
    01267                         Mdl->StartVa =
    01268                                   (PVOID)ULongToPtr(Pfn1->u3.e1.PageColor << PAGE_SHIFT);
    01269                         Mdl->Size = (CSHORT)(sizeof(MDL) +
    01270                                    (sizeof(PFN_NUMBER) * MmModifiedWriteClusterSize));
    01271                     }
    01272
    01273                     LastWritten = PointerPte;
    01274                     Mdl->ByteCount += PAGE_SIZE;
    01275                     if (Mdl->ByteCount == (PAGE_SIZE * MmModifiedWriteClusterSize)) {
    01276                         WriteNow = TRUE;
    01277                     }
    01278
    01279                     if (PteContents.u.Hard.Valid == 0) {
    01280
    01281                         //
    01282                         // The page is in transition.
    01283                         //
    01284
    01285                         MiUnlinkPageFromList (Pfn1);
    01286                         MI_ADD_LOCKED_PAGE_CHARGE_FOR_MODIFIED_PAGE(Pfn1, 18);
    01287                     }
    01288                     else {
    01289                         MI_ADD_LOCKED_PAGE_CHARGE(Pfn1, 20);
    01290                     }
    01291
    01292                     //
    01293                     // Clear the modified bit for this page.
    01294                     //
    01295
    01296                     Pfn1->u3.e1.Modified = 0;
    01297
    01298                     //
    01299                     // Up the reference count for the physical page as there
    01300                     // is I/O in progress.
    01301                     //
    01302
    01303                     Pfn1->u3.e2.ReferenceCount += 1;
    01304
    01305                     *LastPage = PageFrameIndex;
    01306                     LastPage += 1;
    01307                 } else {
    01308
    01309                     //
    01310                     // This page was not modified and therefore ends the
    01311                     // current write cluster if any.  Set WriteNow to TRUE
    01312                     // if there is a cluster being built.
    01313                     //
    01314
    01315                     if (LastWritten != NULL) {
    01316                         WriteNow = TRUE;
    01317                     }
    01318                 }
    01319             } else {
    01320
    01321                 //
    01322                 // This page was not modified and therefore ends the
    01323                 // current write cluster if any.  Set WriteNow to TRUE
    01324                 // if there is a cluster being built.
    01325                 //
    01326
    01327                 if (LastWritten != NULL) {
    01328                     WriteNow = TRUE;
    01329                 }
    01330             }
    01331
    01332             PointerPte += 1;
    01333
    01334 CheckForWrite:
    01335
    01336             //
    01337             // Write the current cluster if it is complete,
    01338             // full, or the loop is now complete.
    01339             //
    01340
    01341             if ((WriteNow) ||
    01342                 ((PointerPte == LastPte) && (LastWritten != NULL))) {
    01343
    01344                 LARGE_INTEGER EndOfFile;
    01345
    01346                 //
    01347                 // Issue the write request.
    01348                 //
    01349
    01350                 UNLOCK_PFN (OldIrql);
    01351
    01352                 WriteNow = FALSE;
    01353
    01354                 //
    01355                 // Make sure the write does not go past the
    01356                 // end of file. (segment size).
    01357                 //
    01358
    01359                 EndOfFile = MiEndingOffset(Subsection);
    01360                 TempOffset = (UINT64) EndOfFile.QuadPart;
    01361
    01362                 if (StartingOffset + Mdl->ByteCount > TempOffset) {
    01363
    01364                     ASSERT ((ULONG_PTR)(TempOffset - StartingOffset) >
    01365                              (Mdl->ByteCount - PAGE_SIZE));
    01366
    01367                     Mdl->ByteCount = (ULONG)(TempOffset-
    01368                                              StartingOffset);
    01369                 }
    01370
    01371                 ReflushCount = MiIoRetryLevel;
    01372
    01373                 while ( TRUE ) {
    01374
    01375                     KeClearEvent (&IoEvent);
    01376
    01377 #if DBG
    01378                     if (MmDebug & MM_DBG_FLUSH_SECTION) {
    01379                         DbgPrint("flush page write begun %lx\n",
    01380                                 Mdl->ByteCount);
    01381                     }
    01382 #endif //DBG
    01383
    01384                     Status = IoSynchronousPageWrite (ControlArea->FilePointer,
    01385                                                      Mdl,
    01386                                                      (PLARGE_INTEGER)&StartingOffset,
    01387                                                      &IoEvent,
    01388                                                      IoStatus );
    01389
    01390                     //
    01391                     // If success is returned, wait for the i/o event to be set.
    01392                     //
    01393
    01394                     if (NT_SUCCESS(Status)) {
    01395                         KeWaitForSingleObject( &IoEvent,
    01396                                                WrPageOut,
    01397                                                KernelMode,
    01398                                                FALSE,
    01399                                                (PLARGE_INTEGER)NULL);
    01400                     //
    01401                     //  Otherwise, copy the error to the IoStatus, for error
    01402                     //  handling below.
    01403                     //
    01404
    01405                     } else {
    01406                         IoStatus->Status = Status;
    01407                     }
    01408
    01409                     if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) {
    01410                         MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
    01411                     }
    01412
    01413                     Page = (PPFN_NUMBER)(Mdl + 1);
    01414
    01415                     if (MmIsRetryIoStatus(IoStatus->Status)) {
    01416
    01417                         ReflushCount -= 1;
    01418                         if (ReflushCount != 0) {
    01419                             KeDelayExecutionThread (KernelMode, FALSE, &Mm30Milliseconds);
    01420                             continue;
    01421                         }
    01422                     }
    01423
    01424                     break;
    01425                 }
    01426
    01427                 LOCK_PFN (OldIrql);
    01428
    01429                 if (MiIsPteOnPdeBoundary(PointerPte) == 0) {
    01430
    01431                     //
    01432                     // The next PTE is not in a different page, make
    01433                     // sure this page did not leave memory when the
    01434                     // I/O was in progress.
    01435                     //
    01436
    01437                     MiMakeSystemAddressValidPfn (PointerPte);
    01438                 }
    01439
    01440                 if (NT_SUCCESS(IoStatus->Status)) {
    01441
    01442                     //
    01443                     // The I/O completed successfully, unlock the pages.
    01444                     //
    01445
    01446                     while (Page < LastPage) {
    01447
    01448                         Pfn2 = MI_PFN_ELEMENT (*Page);
    01449                         MI_REMOVE_LOCKED_PAGE_CHARGE(Pfn2, 19);
    01450                         MiDecrementReferenceCount (*Page);
    01451                         Page += 1;
    01452                     }
    01453                 } else {
    01454
    01455                     //
    01456                     //  Don't count on the file system to convey anything on errors
    01457                     //  in the information field.
    01458                     //
    01459
    01460                     IoStatus->Information = 0;
    01461
    01462                     //
    01463                     // The I/O completed unsuccessfully, unlock the pages
    01464                     // and return an error status.
    01465                     //
    01466
    01467                     while (Page < LastPage) {
    01468
    01469                         Pfn2 = MI_PFN_ELEMENT (*Page);
    01470
    01471                         //
    01472                         // Mark the page dirty again so it can be rewritten.
    01473                         //
    01474
    01475                         Pfn2->u3.e1.Modified = 1;
    01476
    01477                         MI_REMOVE_LOCKED_PAGE_CHARGE(Pfn2, 21);
    01478                         MiDecrementReferenceCount (*Page);
    01479                         Page += 1;
    01480                     }
    01481
    01482                     //
    01483                     // Calculate how much was written thus far
    01484                     // and add that to the information field
    01485                     // of the IOSB.
    01486                     //
    01487
    01488                     IoStatus->Information +=
    01489                         (((LastWritten - StartingPte) << PAGE_SHIFT) -
    01490                                                         Mdl->ByteCount);
    01491
    01492                     goto ErrorReturn;
    01493                 }
    01494
    01495                 //
    01496                 // As the PFN lock has been released and
    01497                 // reacquired, do this loop again as the
    01498                 // PTE may have changed state.
    01499                 //
    01500
    01501                 LastWritten = NULL;
    01502             }
    01503
    01504         } //end while
    01505
    01506         if ((Bail == TRUE) || (Subsection == LastSubsection)) {
    01507
    01508             //
    01509             // The last range has been flushed or we have collided with the
    01510             // mapped page writer.  Regardless, exit the top FOR loop
    01511             // and return.
    01512             //
    01513
    01514             break;
    01515         }
    01516
    01517         Subsection = Subsection->NextSubsection;
    01518         PointerPte = Subsection->SubsectionBase;
    01519
    01520     }  //end for
    01521
    01522     ASSERT (LastWritten == NULL);
    01523
    01524 ErrorReturn:
    01525
    01526     ControlArea->FlushInProgressCount -= 1;
    01527     if ((ControlArea->u.Flags.CollidedFlush == 1) &&
    01528         (ControlArea->FlushInProgressCount == 0)) {
    01529         ControlArea->u.Flags.CollidedFlush = 0;
    01530         KePulseEvent (&MmCollidedFlushEvent, 0, FALSE);
    01531     }
    01532     UNLOCK_PFN (OldIrql);
    01533
    01534     if (Bail == TRUE) {
    01535
    01536         //
    01537         // This routine collided with the mapped page writer and the caller
    01538         // expects an error for this.  Give it to him.
    01539         //
    01540
    01541         return STATUS_MAPPED_WRITER_COLLISION;
    01542     }
    01543
    01544     return IoStatus->Status;
    01545 }

PSUBSECTION MiGetSystemCacheSubsection | ( | IN PVOID | BaseAddress,
|  | IN PEPROCESS | Process,
|  | OUT PMMPTE * | ProtoPte
| ) |

Definition at line 2402 of file flushsec.c.

References LOCK_PFN, MiGetPteAddress, MiGetSubsectionAndProtoFromPte(), and UNLOCK_PFN.

Referenced by MmFlushVirtualMemory().

    02408 {
    02409     KIRQL OldIrql;
    02410     PMMPTE PointerPte;
    02411     PSUBSECTION Subsection;
    02412
    02413     LOCK_PFN (OldIrql);
    02414
    02415     PointerPte = MiGetPteAddress (BaseAddress);
    02416
    02417     Subsection = MiGetSubsectionAndProtoFromPte (PointerPte,
    02418                                                  ProtoPte,
    02419                                                  Process);
    02420     UNLOCK_PFN (OldIrql);
    02421     return Subsection;
    02422 }

LONGLONG MiStartingOffset | ( | IN PSUBSECTION | Subsection,
|  | IN PMMPTE | PteAddress
| ) |

Definition at line 916 of file flushsec.c.

References Mi4KStartFromSubsection, MI_STARTING_OFFSET, MM4K_SHIFT, and PAGE_SHIFT.

Referenced by MiCleanSection(), MiFlushSectionInternal(), MiGatherMappedPages(), MiResolveMappedFileFault(), and MmShutdownSystem().

    00923                    :
    00924
    00925     This function calculates the file offset given a subsection and a PTE
    00926     offset.  Note that images are stored in 512-byte units whereas data is
    00927     stored in 4K units.
    00928
    00929     When this is all debugged, this should be made into a macro.
    00930
    00931 Arguments:
    00932
    00933     Subsection - Supplies a subsection to reference for the file address.
    00934
    00935     PteAddress - Supplies a PTE within the subsection
    00936
    00937 Return Value:
    00938
    00939     Returns the file offset to obtain the backing data from.
    00940
    00941 --*/
    00942
    00943 {
    00944     LONGLONG PteByteOffset;
    00945     LARGE_INTEGER StartAddress;
    00946
    00947     if (Subsection->ControlArea->u.Flags.Image == 1) {
    00948             return MI_STARTING_OFFSET ( Subsection,
    00949                                         PteAddress);
    00950     }
    00951
    00952     PteByteOffset = (LONGLONG)((PteAddress - Subsection->SubsectionBase))
    00953                             << PAGE_SHIFT;
    00954
    00955     Mi4KStartFromSubsection (&StartAddress, Subsection);
    00956
    00957     StartAddress.QuadPart = StartAddress.QuadPart << MM4K_SHIFT;
    00958
    00959     PteByteOffset += StartAddress.QuadPart;
    00960
    00961     return PteByteOffset;
    00962 }

BOOLEAN MmFlushImageSection | ( | IN PSECTION_OBJECT_POINTERS | SectionPointer,
|  | IN MMFLUSH_TYPE | FlushType
| ) |

Definition at line 2094 of file flushsec.c.

References ASSERT, CheckImageSection, FALSE, LOCK_PFN, MiCheckControlArea(), MiCheckControlAreaStatus(), MiCleanSection(), MiHydra, MMFLUSH_TYPE, MmFlushForDelete, NULL, _CONTROL_AREA::NumberOfMappedViews, _LARGE_CONTROL_AREA::NumberOfSectionReferences, _CONTROL_AREA::NumberOfUserReferences, TRUE, _LARGE_CONTROL_AREA::u, _CONTROL_AREA::u, and UNLOCK_PFN.

Referenced by MiCanFileBeTruncatedInternal(), and UdfPurgeVolume().

    02101                    :
    02102
    02103     This function determines if any views of the specified image section
    02104     are mapped, and if not, flushes valid pages (even modified ones)
    02105     from the specified section and returns any used pages to the free
    02106     list.  This is accomplished by examining the prototype PTEs
    02107     from the specified offset to the end of the section, and if
    02108     any prototype PTEs are in the transition state, putting the
    02109     prototype PTE back into its original state and putting the
    02110     physical page on the free list.
    02111
    02112 Arguments:
    02113
    02114     SectionPointer - Supplies a pointer to a section object pointers
    02115                      within the FCB.
    02116
    02117     FlushType - Supplies the type of flush to check for.  One of
    02118                 MmFlushForDelete or MmFlushForWrite.
    02119
    02120 Return Value:
    02121
    02122     Returns TRUE if either no section exists for the file object or
    02123     the section is not mapped and the purge was done, FALSE otherwise.
    02124
    02125 --*/
    02126
    02127 {
    02128     PLIST_ENTRY Next;
    02129     PCONTROL_AREA ControlArea;
    02130     PLARGE_CONTROL_AREA LargeControlArea;
    02131     KIRQL OldIrql;
    02132     BOOLEAN state;
    02133     BOOLEAN FinalControlArea;
    02134
    02135
    02136     if (FlushType == MmFlushForDelete) {
    02137
    02138         //
    02139         // Do a quick check to see if there are any mapped views for
    02140         // the data section.  If so, just return FALSE.
    02141         //
    02142
    02143         LOCK_PFN (OldIrql);
    02144         ControlArea = (PCONTROL_AREA)(SectionPointer->DataSectionObject);
    02145         if (ControlArea != NULL) {
    02146             if ((ControlArea->NumberOfUserReferences != 0) ||
    02147                 (ControlArea->u.Flags.BeingCreated)) {
    02148                 UNLOCK_PFN (OldIrql);
    02149                 return FALSE;
    02150             }
    02151         }
    02152         UNLOCK_PFN (OldIrql);
    02153     }
    02154
    02155     //
    02156     // Check the status of the control area.  If the control area is in use
    02157     // or the control area is being deleted, this operation cannot continue.
    02158     //
    02159
    02160     state = MiCheckControlAreaStatus (CheckImageSection,
    02161                                       SectionPointer,
    02162                                       FALSE,
    02163                                       &ControlArea,
    02164                                       &OldIrql);
    02165
    02166     if (ControlArea == NULL) {
    02167         return state;
    02168     }
    02169
    02170     //
    02171     // PFN LOCK IS NOW HELD!
    02172     //
    02173
    02174     //
    02175     // Repeat until there are no more control areas - multiple control areas
    02176     // for the same image section occur to support user global DLLs - these DLLs
    02177     // require data that is shared within a session but not across sessions.
    02178     // Note this can only happen for Hydra.
    02179     //
    02180
    02181     do {
    02182
    02183         //
    02184         // Set the being deleted flag and up the number of mapped views
    02185         // for the segment.  Upping the number of mapped views prevents
    02186         // the segment from being deleted and passed to the deletion thread
    02187         // while we are forcing a delete.
    02188         //
    02189
    02190         ControlArea->u.Flags.BeingDeleted = 1;
    02191         ControlArea->NumberOfMappedViews = 1;
    02192         FinalControlArea = TRUE;
    02193
    02194         if (MiHydra == FALSE) {
    02195             ASSERT (ControlArea ==
    02196                     (PCONTROL_AREA)SectionPointer->ImageSectionObject);
    02197         }
    02198
    02199         else if (ControlArea->u.Flags.GlobalOnlyPerSession == 0) {
    02200         }
    02201         else if (IsListEmpty(&((PLARGE_CONTROL_AREA)ControlArea)->UserGlobalList)) {
    02202             ASSERT (ControlArea ==
    02203                     (PCONTROL_AREA)SectionPointer->ImageSectionObject);
    02204         }
    02205         else {
    02206
    02207             //
    02208             // Check if there's only one image section in this control area, so
    02209             // we don't reference the section object pointers as the
    02210             // MiCleanSection call may result in its deletion.
    02211             //
    02212
    02213             FinalControlArea = FALSE;
    02214
    02215             //
    02216             // There are multiple control areas, bump the reference count
    02217             // on one of them (doesn't matter which one) so that it can't
    02218             // go away.  This ensures the section object pointers will stick
    02219             // around even after the calls below so we can safely reloop to
    02220             // flush any other remaining control areas.
    02221             //
    02222
    02223             ASSERT (ControlArea->u.Flags.GlobalOnlyPerSession == 1);
    02224
    02225             Next = ((PLARGE_CONTROL_AREA)ControlArea)->UserGlobalList.Flink;
    02226
    02227             LargeControlArea = CONTAINING_RECORD (Next,
    02228                                                   LARGE_CONTROL_AREA,
    02229                                                   UserGlobalList);
    02230
    02231             ASSERT (LargeControlArea->u.Flags.GlobalOnlyPerSession == 1);
    02232
    02233             LargeControlArea->NumberOfSectionReferences += 1;
    02234         }
    02235
    02236         //
    02237         // This is a page file backed or image segment.  The segment is being
    02238         // deleted, remove all references to the paging file and physical
    02239         // memory.
    02240         //
    02241
    02242         UNLOCK_PFN (OldIrql);
    02243
    02244         MiCleanSection (ControlArea, TRUE);
    02245
    02246         //
    02247         // Get the next Hydra control area.
    02248         //
    02249
    02250         if (FinalControlArea == FALSE) {
    02251             state = MiCheckControlAreaStatus (CheckImageSection,
    02252                                               SectionPointer,
    02253                                               FALSE,
    02254                                               &ControlArea,
    02255                                               &OldIrql);
    02256             if (!ControlArea) {
    02257                 LOCK_PFN (OldIrql);
    02258                 LargeControlArea->NumberOfSectionReferences -= 1;
    02259                 MiCheckControlArea ((PCONTROL_AREA)LargeControlArea,
    02260                                     NULL,
    02261                                     OldIrql);
    02262             }
    02263             else {
    02264                 LargeControlArea->NumberOfSectionReferences -= 1;
    02265                 MiCheckControlArea ((PCONTROL_AREA)LargeControlArea,
    02266                                     NULL,
    02267                                     OldIrql);
    02268                 LOCK_PFN (OldIrql);
    02269             }
    02270         } else {
    02271             state = TRUE;
    02272             break;
    02273         }
    02274
    02275     } while (ControlArea);
    02276
    02277     return state;
    02278 }

NTSTATUS MmFlushSection | ( | IN PSECTION_OBJECT_POINTERS | SectionObjectPointer,
|  | IN PLARGE_INTEGER | Offset,
|  | IN SIZE_T | RegionSize,
|  | OUT PIO_STATUS_BLOCK | IoStatus,
|  | IN ULONG | AcquireFile
| ) |

Definition at line 668 of file flushsec.c.

References ASSERT, BYTE_OFFSET, FALSE, _CONTROL_AREA::FilePointer, _ETHREAD::ForwardClusterOnly, FsRtlAcquireFileForCcFlush(), FsRtlReleaseFileForCcFlush(), KeDelayExecutionThread(), KernelMode, LOCK_PFN, MiCheckControlArea(), MiFlushSectionInternal(), MmShortTime, _SUBSECTION::NextSubsection, NTSTATUS(), NULL, _CONTROL_AREA::NumberOfMappedViews, _CONTROL_AREA::NumberOfPfnReferences, Offset, PAGE_SHIFT, PsGetCurrentThread, _SUBSECTION::PtesInSubsection, _SUBSECTION::SubsectionBase, TRUE, _CONTROL_AREA::u, and UNLOCK_PFN.

Referenced by CcFlushCache(), CcMapAndCopy(), CcMdlWriteComplete2(), CcPurgeAndClearCacheSection(), CcSetFileSizes(), CcUnpinRepinnedBcb(), and MiFlushDataSection().

    00678                    :
    00679
    00680     This function flushes to the backing file any modified pages within
    00681     the specified range of the section.
    00682
    00683 Arguments:
    00684
    00685     SectionObjectPointer - Supplies a pointer to the section objects.
    00686
    00687     Offset - Supplies the offset into the section in which to begin
    00688              flushing pages.  If this argument is not present, then the
    00689              whole section is flushed without regard to the region size
    00690              argument.
    00691
    00692     RegionSize - Supplies the size in bytes to flush.  This is rounded
    00693                  to a page multiple.
    00694
    00695     IoStatus - Returns the value of the IoStatus for the last attempted
    00696          I/O operation.
    00697
    00698     AcquireFile - Nonzero if the callback should be used to acquire the file
    00699
    00700 Return Value:
    00701
    00702     Returns status of the operation.
    00703
    00704 --*/
    00705
    00706 {
    00707     PCONTROL_AREA ControlArea;
    00708     PMMPTE PointerPte;
    00709     PMMPTE LastPte;
    00710     KIRQL OldIrql;
    00711     ULONG PteOffset;
    00712     PSUBSECTION Subsection;
    00713     PSUBSECTION LastSubsection;
    00714     BOOLEAN DeleteSegment = FALSE;
    00715     PETHREAD CurrentThread;
    00716     NTSTATUS status;
    00717     BOOLEAN OldClusterState;
    00718     ULONG ConsecutiveFileLockFailures;
    00719
    00720     //
    00721     // Initialize IoStatus for success, in case we take an early exit.
    00722     //
    00723
    00724     IoStatus->Status = STATUS_SUCCESS;
    00725     IoStatus->Information = RegionSize;
    00726
    00727     LOCK_PFN (OldIrql);
    00728
    00729     ControlArea = ((PCONTROL_AREA)(SectionObjectPointer->DataSectionObject));
    00730
    00731     ASSERT ((ControlArea == NULL) || (ControlArea->u.Flags.Image == 0));
    00732
    00733     if ((ControlArea == NULL) ||
    00734         (ControlArea->u.Flags.BeingDeleted) ||
    00735         (ControlArea->u.Flags.BeingCreated) ||
    00736         (ControlArea->NumberOfPfnReferences == 0)) {
    00737
    00738         //
    00739         // This file no longer has an associated segment or is in the
    00740         // process of coming or going.
    00741         // If the number of PFN references is zero, then this control
    00742         // area does not have any valid or transition pages that need
    00743         // to be flushed.
    00744         //
    00745
    00746         UNLOCK_PFN (OldIrql);
    00747         return STATUS_SUCCESS;
    00748     }
    00749
    00750     //
    00751     // Locate the subsection.
    00752     //
    00753
    00754     ASSERT (ControlArea->u.Flags.GlobalOnlyPerSession == 0);
    00755
    00756     Subsection = (PSUBSECTION)(ControlArea + 1);
    00757
    00758     if (!ARGUMENT_PRESENT (Offset)) {
    00759
    00760         //
    00761         // If the offset is not specified, flush the complete file ignoring
    00762         // the region size.
    00763         //
    00764
    00765         PointerPte = &Subsection->SubsectionBase[0];
    00766         LastSubsection = Subsection;
    00767         while (LastSubsection->NextSubsection != NULL) {
    00768             LastSubsection = LastSubsection->NextSubsection;
    00769         }
    00770         LastPte = &LastSubsection->SubsectionBase
    00771                             [LastSubsection->PtesInSubsection - 1];
    00772     } else {
    00773
    00774         PteOffset = (ULONG)(Offset->QuadPart >> PAGE_SHIFT);
    00775
    00776         //
    00777         // Make sure the PTEs are not in the extended part of the
    00778         // segment.
    00779         //
    00780
    00781         while (PteOffset >= Subsection->PtesInSubsection) {
    00782             PteOffset -= Subsection->PtesInSubsection;
    00783             if (Subsection->NextSubsection == NULL) {
    00784
    00785                 //
    00786                 // Past end of mapping, just return success.
    00787                 //
    00788
    00789                 UNLOCK_PFN (OldIrql);
    00790                 return STATUS_SUCCESS;
    00791             }
    00792             Subsection = Subsection->NextSubsection;
    00793         }
    00794
    00795         ASSERT (PteOffset < Subsection->PtesInSubsection);
    00796         PointerPte = &Subsection->SubsectionBase[PteOffset];
    00797
    00798         //
    00799         // Locate the address of the last prototype PTE to be flushed.
    00800         //
    00801
    00802         PteOffset += (ULONG)(((RegionSize + BYTE_OFFSET(Offset->LowPart)) - 1) >> PAGE_SHIFT);
    00803
    00804         LastSubsection = Subsection;
    00805
    00806         while (PteOffset >= LastSubsection->PtesInSubsection) {
    00807             PteOffset -= LastSubsection->PtesInSubsection;
    00808             if (LastSubsection->NextSubsection == NULL) {
    00809                 PteOffset = LastSubsection->PtesInSubsection - 1;
    00810                 break;
    00811             }
    00812             LastSubsection = LastSubsection->NextSubsection;
    00813         }
    00814
    00815         ASSERT (PteOffset < LastSubsection->PtesInSubsection);
    00816         LastPte = &LastSubsection->SubsectionBase[PteOffset];
    00817     }
    00818
    00819     //
    00820     // Up the map view count so the control area cannot be deleted
    00821     // out from under the call.
    00822     //
    00823
    00824     ControlArea->NumberOfMappedViews += 1;
    00825
    00826     UNLOCK_PFN (OldIrql);
    00827
    00828     CurrentThread = PsGetCurrentThread();
    00829
    00830     //
    00831     // Indicate that disk verify errors should be returned as exceptions.
    00832     //
    00833
    00834     OldClusterState = CurrentThread->ForwardClusterOnly;
    00835     CurrentThread->ForwardClusterOnly = TRUE;
    00836
    00837     //
    00838     // Preacquire the file if we are going to synchronize the flush.
    00839     //
    00840
    00841     if (AcquireFile == 0) {
    00842
    00843         //
    00844         // Flush the PTEs from the specified section.
    00845         //
    00846
    00847         status = MiFlushSectionInternal (PointerPte,
    00848                                          LastPte,
    00849                                          Subsection,
    00850                                          LastSubsection,
    00851                                          TRUE,
    00852                                          TRUE,
    00853                                          IoStatus);
    00854     }
    00855     else {
    00856
    00857         ConsecutiveFileLockFailures = 0;
    00858
    00859         do {
    00860
    00861             FsRtlAcquireFileForCcFlush (ControlArea->FilePointer);
    00862
    00863             //
    00864             // Flush the PTEs from the specified section.
    00865             //
    00866
    00867             status = MiFlushSectionInternal (PointerPte,
    00868                                              LastPte,
    00869                                              Subsection,
    00870                                              LastSubsection,
    00871                                              TRUE,
    00872                                              TRUE,
    00873                                              IoStatus);
    00874
    00875             //
    00876             // Release the file we acquired.
    00877             //
    00878
    00879             FsRtlReleaseFileForCcFlush (ControlArea->FilePointer);
    00880
    00881             //
    00882             // Only try the request more than once if the filesystem told us
    00883             // it had a deadlock.
    00884             //
    00885
    00886             if (status != STATUS_FILE_LOCK_CONFLICT) {
    00887                 break;
    00888             }
    00889
    00890             ConsecutiveFileLockFailures += 1;
    00891             KeDelayExecutionThread (KernelMode, FALSE, &MmShortTime);
    00892
    00893         } while (ConsecutiveFileLockFailures < 5);
    00894     }
    00895
    00896     CurrentThread->ForwardClusterOnly = OldClusterState;
    00897
    00898     LOCK_PFN (OldIrql);
    00899
    00900     ASSERT ((LONG)ControlArea->NumberOfMappedViews >= 1);
    00901     ControlArea->NumberOfMappedViews -= 1;
    00902
    00903     //
    00904     // Check to see if the control area should be deleted.  This
    00905     // will release the PFN lock.
    00906     //
    00907
    00908     MiCheckControlArea (ControlArea, NULL, OldIrql);
    00909
    00910     return status;
    00911
    00912 }

NTSTATUS MmFlushVirtualMemory | ( | IN PEPROCESS | Process,
|  | IN OUT PVOID * | BaseAddress,
|  | IN OUT PSIZE_T | RegionSize,
|  | OUT PIO_STATUS_BLOCK | IoStatus
| ) |

Definition at line 297 of file flushsec.c.

References ASSERT, _MMVAD::ControlArea, _MMVAD::EndingVpn, FALSE, _CONTROL_AREA::FilePointer, FsRtlAcquireFileForCcFlush(), FsRtlReleaseFileForCcFlush(), KeAttachProcess(), KeDelayExecutionThread(), KeDetachProcess(), KernelMode, LOCK_WS, LOCK_WS_AND_ADDRESS_SPACE, MI_IS_SESSION_ADDRESS, MI_IS_SYSTEM_CACHE_ADDRESS, MI_VA_TO_VPN, MI_VPN_TO_VA_ENDING, MiDoesPdeExistAndMakeValid(), MiDoesPpeExistAndMakeValid, MiFlushAcquire(), MiFlushDirtyBitsToPfn(), MiFlushRelease(), MiFlushSectionInternal(), MiGetPdeAddress, MiGetPpeAddress, MiGetProtoPteAddress, MiGetPteAddress, MiGetSystemCacheSubsection(), MiGetVirtualAddressMappedByPte, MiIsPteOnPdeBoundary, MiLocateAddress(), MiLocateSubsection(), MmShortTime, _SUBSECTION::NextSubsection, NTSTATUS(), NULL, PAGE_ALIGN, PAGE_SIZE, PAGED_CODE, PsGetCurrentProcess, _SUBSECTION::PtesInSubsection, Status, _SUBSECTION::SubsectionBase, TRUE, _MMVAD::u, UNLOCK_WS, and UNLOCK_WS_AND_ADDRESS_SPACE.

Referenced by NtFlushVirtualMemory().

    00306                    :
    00307
    00308     This function flushes a range of virtual address which map
    00309     a data file back into the data file if they have been modified.
    00310
    00311     Note that the modification is this process's view of the pages,
    00312     on certain implementations (like the Intel 386), the modify
    00313     bit is captured in the PTE and not forced to the PFN database
    00314     until the page is removed from the working set.  This means
    00315     that pages which have been modified by another process will
    00316     not be flushed to the data file.
    00317
    00318 Arguments:
    00319
    00320     Process - Supplies a pointer to a process object.
    00321
    00322     BaseAddress - Supplies a pointer to a variable that will receive
    00323          the base address of the flushed region.  The initial value
    00324          of this argument is the base address of the region of the
    00325          pages to flush.
    00326
    00327     RegionSize - Supplies a pointer to a variable that will receive
    00328          the actual size in bytes of the flushed region of pages.
    00329          The initial value of this argument is rounded up to the
    00330          next host-page-size boundary.
    00331
    00332          If this value is specified as zero, the mapped range from
    00333          the base address to the end of the range is flushed.
    00334
    00335     IoStatus - Returns the value of the IoStatus for the last attempted
    00336          I/O operation.
    00337
    00338 Return Value:
    00339
    00340     Returns the NT status
    00341
    00342 --*/
    00343
    00344 {
    00345     PMMVAD Vad;
    00346     PVOID EndingAddress;
    00347     PVOID Va;
    00348     PEPROCESS CurrentProcess;
    00349     BOOLEAN SystemCache;
    00350     PCONTROL_AREA ControlArea;
    00351     PMMPTE PointerPte;
    00352     PMMPTE PointerPde;
    00353     PMMPTE PointerPpe;
    00354     PMMPTE LastPte;
    00355     PMMPTE FinalPte;
    00356     PSUBSECTION Subsection;
    00357     PSUBSECTION LastSubsection;
    00358     NTSTATUS Status;
    00359     ULONG ConsecutiveFileLockFailures;
    00360     ULONG Waited;
    00361     LOGICAL EntireRestOfVad;
    00362     LOGICAL Attached;
    00363
    00364     PAGED_CODE();
    00365
    00366     Attached = FALSE;
    00367
    00368     //
    00369     // Determine if the specified base address is within the system
    00370     // cache and if so, don't attach, the working set mutex is still
    00371     // required to "lock" paged pool pages (proto PTEs) into the
    00372     // working set.
    00373     //
    00374
    00375     EndingAddress = (PVOID)(((ULONG_PTR)*BaseAddress + *RegionSize - 1) |
    00376                                                             (PAGE_SIZE - 1));
    00377     *BaseAddress = PAGE_ALIGN (*BaseAddress);
    00378
    00379     if (MI_IS_SESSION_ADDRESS (*BaseAddress)) {
    00380
    00381         //
    00382         // Nothing in session space needs flushing.
    00383         //
    00384
    00385         return STATUS_NOT_MAPPED_VIEW;
    00386     }
    00387
    00388     CurrentProcess = PsGetCurrentProcess ();
    00389
    00390     if (!MI_IS_SYSTEM_CACHE_ADDRESS(*BaseAddress)) {
    00391
    00392         SystemCache = FALSE;
    00393
    00394         //
    00395         // Attach to the specified process.
    00396         //
    00397
    00398         if (PsGetCurrentProcess() != Process) {
    00399             KeAttachProcess (&Process->Pcb);
    00400             Attached = TRUE;
    00401         }
    00402
    00403         LOCK_WS_AND_ADDRESS_SPACE (Process);
    00404
    00405         //
    00406         // Make sure the address space was not deleted, if so, return an error.
    00407         //
    00408
    00409         if (Process->AddressSpaceDeleted != 0) {
    00410             Status = STATUS_PROCESS_IS_TERMINATING;
    00411             goto ErrorReturn;
    00412         }
    00413
    00414         Vad = MiLocateAddress (*BaseAddress);
    00415
    00416         if (Vad == (PMMVAD)NULL) {
    00417
    00418             //
    00419             // No Virtual Address Descriptor located for Base Address.
    00420             //
    00421
    00422             Status = STATUS_NOT_MAPPED_VIEW;
    00423             goto ErrorReturn;
    00424         }
    00425
    00426         if (*RegionSize == 0) {
    00427             EndingAddress = MI_VPN_TO_VA_ENDING (Vad->EndingVpn);
    00428             EntireRestOfVad = TRUE;
    00429         }
    00430         else {
    00431             EntireRestOfVad = FALSE;
    00432         }
    00433
    00434         if ((Vad->u.VadFlags.PrivateMemory == 1) ||
    00435             (MI_VA_TO_VPN (EndingAddress) > Vad->EndingVpn)) {
    00436
    00437             //
    00438             // This virtual address descriptor does not refer to a Segment
    00439             // object.
    00440             //
    00441
    00442             Status = STATUS_NOT_MAPPED_VIEW;
    00443             goto ErrorReturn;
    00444         }
    00445
    00446         //
    00447         // Make sure this VAD maps a data file (not an image file).
    00448         //
    00449
    00450         ControlArea = Vad->ControlArea;
    00451
    00452         if ((ControlArea->FilePointer == NULL) ||
    00453              (Vad->u.VadFlags.ImageMap == 1)) {
    00454
    00455             //
    00456             // This virtual address descriptor does not refer to a Segment
    00457             // object.
    00458             //
    00459
    00460             Status = STATUS_NOT_MAPPED_DATA;
    00461             goto ErrorReturn;
    00462         }
    00463
    00464     } else {
    00465
    00466         SystemCache = TRUE;
    00467         Process = CurrentProcess;
    00468         LOCK_WS (Process);
    00469     }
    00470
    00471     PointerPpe = MiGetPpeAddress (*BaseAddress);
    00472     PointerPde = MiGetPdeAddress (*BaseAddress);
    00473     PointerPte = MiGetPteAddress (*BaseAddress);
    00474     LastPte = MiGetPteAddress (EndingAddress);
    00475     *RegionSize = (PCHAR)EndingAddress - (PCHAR)*BaseAddress + 1;
    00476
    00477 retry:
    00478
    00479     while (!MiDoesPpeExistAndMakeValid (PointerPpe, Process, FALSE, &Waited)) {
    00480
    00481         //
    00482         // This page directory parent entry is empty, go to the next one.
    00483         //
    00484
    00485         PointerPpe += 1;
    00486         PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
    00487         PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
    00488         Va = MiGetVirtualAddressMappedByPte (PointerPte);
    00489
    00490         if (PointerPte > LastPte) {
    00491             break;
    00492         }
    00493     }
    00494
    00495     Waited = 0;
    00496
    00497     if (PointerPte <= LastPte) {
    00498         while (!MiDoesPdeExistAndMakeValid(PointerPde, Process, FALSE, &Waited)) {
    00499
    00500             //
    00501             // No page table page exists for this address.
    00502             //
    00503
    00504             PointerPde += 1;
    00505
    00506             PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
    00507
    00508             if (PointerPte > LastPte) {
    00509                 break;
    00510             }
    00511
    00512 #if defined (_WIN64)
    00513             if (MiIsPteOnPdeBoundary (PointerPde)) {
    00514                 PointerPpe = MiGetPteAddress (PointerPde);
    00515                 goto retry;
    00516             }
    00517 #endif
    00518
    00519             Va = MiGetVirtualAddressMappedByPte (PointerPte);
    00520         }
    00521
    00522         //
    00523         // If the PFN lock (and accordingly the WS mutex) was
    00524         // released and reacquired we must retry the operation.
    00525         //
    00526
    00527         if (PointerPte <= LastPte && Waited != 0) {
    00528             goto retry;
    00529         }
    00530     }
    00531
    00532     MiFlushDirtyBitsToPfn (PointerPte, LastPte, Process, SystemCache);
    00533
    00534     if (SystemCache) {
    00535
    00536         //
    00537         // No VADs exist for the system cache.
    00538         //
    00539
    00540         Subsection = MiGetSystemCacheSubsection (*BaseAddress,
    00541                                                  Process,
    00542                                                  &PointerPte);
    00543         LastSubsection = MiGetSystemCacheSubsection (EndingAddress,
    00544                                                      Process,
    00545                                                      &FinalPte);
    00546         UNLOCK_WS (Process);
    00547
    00548         //
    00549         // Flush the PTEs from the specified section.
    00550         //
    00551
    00552         Status = MiFlushSectionInternal (PointerPte,
    00553                                          FinalPte,
    00554                                          Subsection,
    00555                                          LastSubsection,
    00556                                          FALSE,
    00557                                          TRUE,
    00558                                          IoStatus);
    00559     }
    00560     else {
    00561
    00562         //
    00563         // Protect against the section being prematurely deleted.
    00564         //
    00565
    00566         MiFlushAcquire (ControlArea);
    00567
    00568         PointerPte = MiGetProtoPteAddress (Vad, MI_VA_TO_VPN (*BaseAddress));
    00569         Subsection = MiLocateSubsection (Vad, MI_VA_TO_VPN(*BaseAddress));
    00570         LastSubsection = MiLocateSubsection (Vad, MI_VA_TO_VPN(EndingAddress));
    00571
    00572         //
    00573         // The last subsection is NULL if the section is not fully
    00574         // committed.  Only allow the flush if the caller said do the whole
    00575         // thing, otherwise it's an error.
    00576         //
    00577
    00578         if (LastSubsection == NULL) {
    00579
    00580             if (EntireRestOfVad == FALSE) {
    00581
    00582                 //
    00583                 // Caller can only specify the range that is committed or zero
    00584                 // to indicate the entire range.
    00585                 //
    00586
    00587                 UNLOCK_WS_AND_ADDRESS_SPACE (Process);
    00588                 if (Attached == TRUE) {
    00589                     KeDetachProcess();
    00590                 }
    00591                 MiFlushRelease (ControlArea);
    00592                 return STATUS_NOT_MAPPED_VIEW;
    00593             }
    00594
    00595             LastSubsection = Subsection;
    00596             while (LastSubsection->NextSubsection) {
    00597                 LastSubsection = LastSubsection->NextSubsection;
    00598             }
    00599             FinalPte = LastSubsection->SubsectionBase + LastSubsection->PtesInSubsection - 1;
    00600         }
    00601         else {
    00602             FinalPte = MiGetProtoPteAddress (Vad, MI_VA_TO_VPN (EndingAddress));
    00603         }
    00604
    00605         UNLOCK_WS_AND_ADDRESS_SPACE (Process);
    00606         if (Attached == TRUE) {
    00607             KeDetachProcess();
    00608         }
    00609
    00610         //
    00611         // Preacquire the file to synchronize the flush.
    00612         //
    00613
    00614         ConsecutiveFileLockFailures = 0;
    00615
    00616         do {
    00617
    00618             FsRtlAcquireFileForCcFlush (ControlArea->FilePointer);
    00619
    00620             //
    00621             // Flush the PTEs from the specified section.
    00622             //
    00623
    00624             Status = MiFlushSectionInternal (PointerPte,
    00625                                              FinalPte,
    00626                                              Subsection,
    00627                                              LastSubsection,
    00628                                              TRUE,
    00629                                              TRUE,
    00630                                              IoStatus);
    00631
    00632             //
    00633             // Release the file we acquired.
    00634             //
    00635
    00636             FsRtlReleaseFileForCcFlush (ControlArea->FilePointer);
    00637
    00638             //
    00639             // Only try the request more than once if the filesystem told us
    00640             // it had a deadlock.
    00641             //
    00642
    00643             if (Status != STATUS_FILE_LOCK_CONFLICT) {
    00644                 break;
    00645             }
    00646
    00647             ConsecutiveFileLockFailures += 1;
    00648             KeDelayExecutionThread (KernelMode, FALSE, &MmShortTime);
    00649
    00650         } while (ConsecutiveFileLockFailures < 5);
    00651
    00652         MiFlushRelease (ControlArea);
    00653     }
    00654
    00655     return Status;
    00656
    00657 ErrorReturn:
    00658     ASSERT (SystemCache == FALSE);
    00659     UNLOCK_WS_AND_ADDRESS_SPACE (Process);
    00660     if (Attached == TRUE) {
    00661         KeDetachProcess();
    00662     }
    00663     return Status;
    00664
    00665 }

BOOLEAN MmPurgeSection | ( | IN PSECTION_OBJECT_POINTERS | SectionObjectPointer,
|  | IN PLARGE_INTEGER | Offset,
|  | IN SIZE_T | RegionSize,
|  | IN ULONG | IgnoreCacheViews
| ) |

Definition at line 1548 of file flushsec.c.

References ASSERT, BYTE_OFFSET, DbgPrint, DISPATCH_LEVEL, FALSE, _FILE_OBJECT::FileName, _CONTROL_AREA::FilePointer, FreePageList, KeBugCheckEx(), KeEnterCriticalRegion, KeLeaveCriticalRegion, KernelMode, KeWaitForSingleObject(), LOCK_PFN, MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE, MI_PFN_ELEMENT, MI_SET_PFN_DELETED, MI_WRITE_INVALID_PTE, MiCancelWriteOfMappedPfn(), MiCanFileBeTruncatedInternal(), MiCheckControlArea(), MiCheckProtoPtePageState(), MiDecrementShareCount(), MiInsertPageInList(), MiIsPteOnPdeBoundary, MiMakeSystemAddressValidPfn(), MiReleasePageFileSpace(), MiUnlinkPageFromList(), MM_DBG_FLUSH_SECTION, MmMappedFileIoComplete, MmPageLocationList, _CONTROL_AREA::ModifiedWriteCount, _SUBSECTION::NextSubsection, NULL, _CONTROL_AREA::NumberOfMappedViews, _CONTROL_AREA::NumberOfPfnReferences, _CONTROL_AREA::NumberOfSystemCacheViews, Offset, _MMPFN::OriginalPte, PAGE_SHIFT, PAGE_SIZE, _MMPFN::PteAddress, _MMPFN::PteFrame, _SUBSECTION::PtesInSubsection, _SUBSECTION::SubsectionBase, TRUE, _MMPTE::u, _CONTROL_AREA::u, _MMPFN::u3, UNLOCK_PFN, UNLOCK_PFN_AND_THEN_WAIT, and WrPageOut.

Referenced by CcPurgeCacheSection().

    01557                    :
    01558
    01559     This function determines if any views of the specified section
    01560     are mapped, and if not, purges valid pages (even modified ones)
    01561     from the specified section and returns any used pages to the free
    01562     list.  This is accomplished by examining the prototype PTEs
    01563     from the specified offset to the end of the section, and if
    01564     any prototype PTEs are in the transition state, putting the
    01565     prototype PTE back into its original state and putting the
    01566     physical page on the free list.
    01567
    01568     NOTE:
    01569
    01570     If there is an I/O operation ongoing for one of the pages,
    01571     that page is eliminated from the segment and allowed to "float"
    01572     until the i/o is complete.  Once the share count goes to zero
    01573     the page will be added to the free page list.
    01574
    01575 Arguments:
    01576
    01577     SectionObjectPointer - Supplies a pointer to the section objects.
    01578
    01579     Offset - Supplies the offset into the section in which to begin
    01580              purging pages.  If this argument is not present, then the
    01581              whole section is purged without regard to the region size
    01582              argument.
    01583
    01584
    01585     RegionSize - Supplies the size of the region to purge.  If this
    01586                  is specified as zero and Offset is specified, the
    01587                  region from Offset to the end of the file is purged.
    01588
    01589                  Note: The largest value acceptable for RegionSize is
    01590                  0xFFFF0000;
    01591
    01592     IgnoreCacheViews - Supplies FALSE if mapped views in the system
    01593                  cache should cause the function to return FALSE.
    01594                  This is the normal case.
    01595                  Supplies TRUE if mapped views should be ignored
    01596                  and the flush should occur.  NOTE THAT IF TRUE
    01597                  IS SPECIFIED AND ANY DATA PURGED IS CURRENTLY MAPPED
    01598                  AND VALID A BUGCHECK WILL OCCUR!!
    01599
    01600 Return Value:
    01601
    01602     Returns TRUE if either no section exists for the file object or
    01603     the section is not mapped and the purge was done, FALSE otherwise.
    01604
    01605     Note that FALSE is returned if during the purge operation, a page
    01606     could not be purged due to a non-zero reference count.
    01607
    01608 --*/
    01609
    01610 {
    01611     PCONTROL_AREA ControlArea;
    01612     PMMPTE PointerPte;
    01613     PMMPTE LastPte;
    01614     PMMPTE FinalPte;
    01615     MMPTE PteContents;
    01616     PMMPFN Pfn1;
    01617     KIRQL OldIrql;
    01618     ULONG PteOffset;
    01619     PSUBSECTION Subsection;
    01620     PSUBSECTION LastSubsection;
    01621     LARGE_INTEGER LocalOffset;
    01622     BOOLEAN DeleteSegment = FALSE;
    01623     BOOLEAN LockHeld;
    01624     BOOLEAN ReturnValue;
    01625     PFN_NUMBER PageFrameIndex;
    01626 #if DBG
    01627     PFN_NUMBER LastLocked = 0;
    01628 #endif //DBG
    01629
    01630     //
    01631     // This is needed in case a page is on the mapped page writer list -
    01632     // the PFN lock will need to be released and APCs disabled.
    01633     //
    01634
    01635     ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
    01636
    01637     //
    01638     //  Capture caller's file size, since we may modify it.
    01639     //
    01640
    01641     if (ARGUMENT_PRESENT(Offset)) {
    01642
    01643         LocalOffset = *Offset;
    01644         Offset = &LocalOffset;
    01645     }
    01646
    01647     //
    01648     //  See if we can truncate this file to where the caller wants
    01649     //  us to.
    01650     //
    01651
    01652     if (!MiCanFileBeTruncatedInternal(SectionObjectPointer, Offset, TRUE, &OldIrql)) {
    01653         return FALSE;
    01654     }
    01655
    01656     //
    01657     // PFN LOCK IS NOW HELD!
    01658     //
    01659
    01660     ControlArea = (PCONTROL_AREA)(SectionObjectPointer->DataSectionObject);
    01661     if (ControlArea == NULL) {
    01662         UNLOCK_PFN (OldIrql);
    01663         return TRUE;
    01664
    01665     //
    01666     //  Even though MiCanFileBeTruncatedInternal returned TRUE, there could
    01667     //  still be a system cache mapped view.  We cannot truncate if
    01668     //  the Cache Manager has a view mapped.
    01669     //
    01670
    01671     } else if ((IgnoreCacheViews == FALSE) &&
    01672             (ControlArea->NumberOfSystemCacheViews != 0)) {
    01673         UNLOCK_PFN (OldIrql);
    01674         return FALSE;
    01675     }
    01676
    01677     //
    01678     // Prevent races when the control area is being deleted as the clean
    01679     // path releases the PFN lock midway through.  File objects may still have
    01680     // section object pointers and data section objects that point at this
    01681     // control area, hence the purge can be issued.
    01682     //
    01683     // Check for this and fail the purge as the control area (and the section
    01684     // object pointers/data section objects) will be going away momentarily.
    01685     // Note that even though drivers have these data section objects, no one
    01686     // currently has an open section for this control area and no one is
    01687     // allowed to open one until the clean path finishes.
    01688     //
    01689
    01690     if (ControlArea->u.Flags.BeingDeleted == 1) {
    01691         UNLOCK_PFN (OldIrql);
    01692         return FALSE;
    01693     }
    01694
    01695     //
    01696     // Purge the section - locate the subsection which
    01697     // contains the PTEs.
    01698     //
    01699
    01700     ASSERT (ControlArea->u.Flags.GlobalOnlyPerSession == 0);
    01701
    01702     Subsection = (PSUBSECTION)(ControlArea + 1);
    01703
    01704     if (!ARGUMENT_PRESENT (Offset)) {
    01705
    01706         //
    01707         // If the offset is not specified, flush the complete file ignoring
    01708         // the region size.
    01709         //
    01710
    01711         PointerPte = &Subsection->SubsectionBase[0];
    01712         RegionSize = 0;
    01713
    01714     } else {
    01715
    01716         PteOffset = (ULONG)(Offset->QuadPart >> PAGE_SHIFT);
    01717
    01718         //
    01719         // Make sure the PTEs are not in the extended part of the
    01720         // segment.
    01721         //
    01722
    01723         while (PteOffset >= Subsection->PtesInSubsection) {
    01724             PteOffset -= Subsection->PtesInSubsection;
    01725             Subsection = Subsection->NextSubsection;
    01726             if (Subsection == NULL) {
    01727
    01728                 //
    01729                 // The offset must be equal to the size of
    01730                 // the section, don't purge anything just return.
    01731                 //
    01732
    01733                 //ASSERT (PteOffset == 0);
    01734                 UNLOCK_PFN (OldIrql);
    01735                 return TRUE;
    01736             }
    01737         }
    01738
    01739         ASSERT (PteOffset < Subsection->PtesInSubsection);
    01740         PointerPte = &Subsection->SubsectionBase[PteOffset];
    01741     }
    01742
    01743
    01744     //
    01745     // Locate the address of the last prototype PTE to be flushed.
    01746     //
    01747
    01748     if (RegionSize == 0) {
    01749
    01750         //
    01751         // Flush to end of section.
    01752         //
    01753
    01754         LastSubsection = Subsection;
    01755         while (LastSubsection->NextSubsection != NULL) {
    01756             LastSubsection = LastSubsection->NextSubsection;
    01757         }
    01758
    01759         //
    01760         // Set the final PTE to 1 beyond the last page.
    01761         //
    01762
    01763         FinalPte = &LastSubsection->SubsectionBase
    01764                             [LastSubsection->PtesInSubsection];
    01765     } else {
    01766
    01767         //
    01768         // Calculate the end of the region.
    01769         //
    01770
    01771         PteOffset +=
    01772             (ULONG) (((RegionSize + BYTE_OFFSET(Offset->LowPart)) - 1) >> PAGE_SHIFT);
    01773
    01774         LastSubsection = Subsection;
    01775
    01776         while (PteOffset >= LastSubsection->PtesInSubsection) {
    01777             PteOffset -= LastSubsection->PtesInSubsection;
    01778             if (LastSubsection->NextSubsection == NULL) {
    01779                 PteOffset = LastSubsection->PtesInSubsection - 1;
    01780                 break;
    01781             }
    01782             LastSubsection = LastSubsection->NextSubsection;
    01783         }
    01784
    01785         ASSERT (PteOffset < LastSubsection->PtesInSubsection);
    01786
    01787         //
    01788         // Point final PTE to 1 beyond the end.
    01789         //
    01790
    01791         FinalPte = &LastSubsection->SubsectionBase[PteOffset + 1];
    01792     }
    01793
    01794     //
    01795     // Increment the number of mapped views to
    01796     // prevent the section from being deleted while the purge is
    01797     // in progress.
    01798     //
    01799
    01800     ControlArea->NumberOfMappedViews += 1;
    01801
    01802     //
    01803     // Set being purged so no one can map a view
    01804     // while the purge is going on.
    01805     //
    01806
    01807     ControlArea->u.Flags.BeingPurged = 1;
    01808     ControlArea->u.Flags.WasPurged = 1;
    01809
    01810     UNLOCK_PFN (OldIrql);
    01811     LockHeld = FALSE;
    01812     ReturnValue = TRUE;
    01813
    01814     for (;;) {
    01815
    01816         if (LastSubsection != Subsection) {
    01817
    01818             //
    01819             // Flush to the last PTE in this subsection.
    01820             //
    01821
    01822             LastPte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
    01823         } else {
    01824
    01825             //
    01826             // Flush to the end of the range.
    01827             //
    01828
    01829             LastPte = FinalPte;
    01830         }
    01831
    01832         //
    01833         // If the page table page containing the PTEs is not
    01834         // resident, then no PTEs can be in the valid or transition
    01835         // state!  Skip over the PTEs.
    01836         //
    01837
    01838         if (!MiCheckProtoPtePageState(PointerPte, LockHeld)) {
    01839             PointerPte = (PMMPTE)(((ULONG_PTR)PointerPte | (PAGE_SIZE - 1)) + 1);
    01840         }
    01841
    01842         while (PointerPte < LastPte) {
    01843
    01844             //
    01845             // If the page table page containing the PTEs is not
    01846             // resident, then no PTEs can be in the valid or transition
    01847             // state!  Skip over the PTEs.
    01848             //
    01849
    01850             if (MiIsPteOnPdeBoundary(PointerPte)) {
    01851                 if (!MiCheckProtoPtePageState(PointerPte, LockHeld)) {
    01852                     PointerPte = (PMMPTE)((PCHAR)PointerPte + PAGE_SIZE);
    01853                     continue;
    01854                 }
    01855             }
    01856
    01857             PteContents = *PointerPte;
    01858
    01859             if (PteContents.u.Hard.Valid == 1) {
    01860
    01861                 //
    01862                 // A valid PTE was found, it must be mapped in the
    01863                 // system cache.  Just exit the loop and return FALSE
    01864                 // and let the caller fix this.
    01865                 //
    01866
    01867                 ReturnValue = FALSE;
    01868                 break;
    01869             }
    01870
    01871             if ((PteContents.u.Soft.Prototype == 0) &&
    01872                      (PteContents.u.Soft.Transition == 1)) {
    01873
    01874                 if (!LockHeld) {
    01875                     LockHeld = TRUE;
    01876                     LOCK_PFN (OldIrql);
    01877                     MiMakeSystemAddressValidPfn (PointerPte);
    01878                     continue;
    01879                 }
    01880
    01881                 PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE(&PteContents);
    01882                 Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
    01883
    01884                 if ((Pfn1->OriginalPte.u.Soft.Prototype != 1) ||
    01885                     (Pfn1->OriginalPte.u.Hard.Valid != 0) ||
    01886                     (Pfn1->PteAddress != PointerPte)) {
    01887
    01888                     //
    01889                     // The pool containing the prototype PTEs has been
    01890                     // corrupted.  Pool corruption like this is fatal.
    01891                     //
    01892
    01893                     KeBugCheckEx (POOL_CORRUPTION_IN_FILE_AREA,
    01894                                   0x2,
    01895                                   (ULONG_PTR)PointerPte,
    01896                                   (ULONG_PTR)Pfn1->PteAddress,
    01897                                   (ULONG_PTR)PteContents.u.Long);
    01898                 }
    01899
    01900 #if DBG
    01901                 if ((Pfn1->u3.e2.ReferenceCount != 0) &&
    01902                     (Pfn1->u3.e1.WriteInProgress == 0)) {
    01903
    01904                     //
    01905                     // There must be an I/O in progress on this
    01906                     // page.
    01907                     //
    01908
    01909                     if (MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE(&PteContents) != LastLocked) {
    01910                         UNLOCK_PFN (OldIrql);
    01911
    01912 #if DBG
    01913                         if (MmDebug & MM_DBG_FLUSH_SECTION) {
    01914                             DbgPrint("MM:PURGE - page %lx locked, file:%Z\n",
    01915                                     PageFrameIndex,
    01916                                     &ControlArea->FilePointer->FileName
    01917                                 );
    01918                         }
    01919 #endif
    01920                         LastLocked = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents);
    01921                         //DbgBreakPoint();
    01922                         LOCK_PFN (OldIrql);
    01923                         MiMakeSystemAddressValidPfn (PointerPte);
    01924                         continue;
    01925                     }
    01926                 }
    01927 #endif //DBG
    01928
    01929                 //
    01930                 // If the modified page writer has page locked for I/O
    01931                 // wait for the I/O's to be completed and the pages
    01932                 // to be unlocked.  The eliminates a race condition
    01933                 // when the modified page writer locks the pages, then
    01934                 // a purge occurs and completes before the mapped
    01935                 // writer thread runs.
    01936                 //
    01937
    01938                 if (Pfn1->u3.e1.WriteInProgress == 1) {
    01939
    01940                     //
    01941                     // A 3 or more thread deadlock can occur where:
    01942                     //
    01943                     // 1.  The mapped page writer thread has issued a write
    01944                     //     and is in the filesystem code waiting for a resource.
    01945                     //
    01946                     // 2.  Thread 2 owns the resource above but is waiting for
    01947                     //     the filesystem's quota mutex.
    01948                     //
    01949                     // 3.  Thread 3 owns the quota mutex and is right here
    01950                     //     doing a purge from the cache manager when he notices
    01951                     //     the page to be purged is either already being written
    01952                     //     or is in the mapped page writer list.  If it is
    01953                     //     already being written everything will unjam.  If it
    01954                     //     is still on the mapped page writer list awaiting
    01955                     //     processing, then it must be cancelled - otherwise
    01956                     //     if this thread were to wait, deadlock can occur.
    01957                     //
    01958                     // The alternative to all this is for the filesystems to
    01959                     // always release the quota mutex before purging but the
    01960                     // filesystem overhead to do this is substantial.
    01961                     //
    01962
    01963                     if (MiCancelWriteOfMappedPfn (PageFrameIndex) == TRUE) {
    01964
    01965                         //
    01966                         // Stopping any failed writes (even deliberately
    01967                         // cancelled ones) automatically cause a delay.  A
    01968                         // successful stop also results in the PFN lock
    01969                         // being released and reacquired.  So loop back to
    01970                         // the top now as the world may have changed.
    01971                         //
    01972
    01973                         MiMakeSystemAddressValidPfn (PointerPte);
    01974                         continue;
    01975                     }
    01976
    01977                     ASSERT (ControlArea->ModifiedWriteCount != 0);
    01978                     ASSERT (Pfn1->u3.e2.ReferenceCount != 0);
    01979
    01980                     ControlArea->u.Flags.SetMappedFileIoComplete = 1;
    01981
    01982                     KeEnterCriticalRegion();
    01983                     UNLOCK_PFN_AND_THEN_WAIT(OldIrql);
    01984
    01985                     KeWaitForSingleObject(&MmMappedFileIoComplete,
    01986                                           WrPageOut,
    01987                                           KernelMode,
    01988                                           FALSE,
    01989                                           (PLARGE_INTEGER)NULL);
    01990                     LOCK_PFN (OldIrql);
    01991                     KeLeaveCriticalRegion();
    01992                     MiMakeSystemAddressValidPfn (PointerPte);
    01993                     continue;
    01994                 }
    01995
    01996                 if (Pfn1->u3.e1.ReadInProgress == 1) {
    01997
    01998                     //
    01999                     // The page currently is being read in from the
    02000                     // disk.  Treat this just like a valid PTE and
    02001                     // return false.
    02002                     //
    02003
    02004                     ReturnValue = FALSE;
    02005                     break;
    02006                 }
    02007
    02008                 ASSERT (!((Pfn1->OriginalPte.u.Soft.Prototype == 0) &&
    02009                     (Pfn1->OriginalPte.u.Soft.Transition == 1)));
    02010
    02011                 MI_WRITE_INVALID_PTE (PointerPte, Pfn1->OriginalPte);
    02012
    02013                 ASSERT (Pfn1->OriginalPte.u.Hard.Valid == 0);
    02014
    02015                 ControlArea->NumberOfPfnReferences -= 1;
    02016                 ASSERT ((LONG)ControlArea->NumberOfPfnReferences >= 0);
    02017
    02018                 MiUnlinkPageFromList (Pfn1);
    02019
    02020                 MI_SET_PFN_DELETED (Pfn1);
    02021
    02022                 MiDecrementShareCount (Pfn1->PteFrame);
    02023
    02024                 //
    02025                 // If the reference count for the page is zero, insert
    02026                 // it into the free page list, otherwise leave it alone
    02027                 // and when the reference count is decremented to zero
    02028                 // the page will go to the free list.
    02029                 //
    02030
    02031                 if (Pfn1->u3.e2.ReferenceCount == 0) {
    02032                     MiReleasePageFileSpace (Pfn1->OriginalPte);
    02033                     MiInsertPageInList (MmPageLocationList[FreePageList],
    02034                                         MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents));
    02035                 }
    02036             }
    02037             PointerPte += 1;
    02038
    02039             if ((MiIsPteOnPdeBoundary(PointerPte)) && (LockHeld)) {
    02040
    02041                 //
    02042                 // Unlock PFN so large requests will not block other
    02043                 // threads on MP systems.
    02044                 //
    02045
    02046                 UNLOCK_PFN (OldIrql);
    02047                 LockHeld = FALSE;
    02048             }
    02049
    02050         } //end while
    02051
    02052         if (LockHeld) {
    02053             UNLOCK_PFN (OldIrql);
    02054             LockHeld = FALSE;
    02055         }
    02056
    02057         if ((LastSubsection != Subsection) && (ReturnValue)) {
    02058
    02059             //
    02060             // Get the next subsection in the list.
    02061             //
    02062
    02063             Subsection = Subsection->NextSubsection;
    02064             PointerPte = Subsection->SubsectionBase;
    02065
    02066         } else {
    02067
    02068             //
    02069             // The last range has been flushed, exit the top FOR loop
    02070             // and return.
    02071             //
    02072
    02073             break;
    02074         }
    02075     }  //end for
    02076
    02077     LOCK_PFN (OldIrql);
    02078
    02079     ASSERT ((LONG)ControlArea->NumberOfMappedViews >= 1);
    02080     ControlArea->NumberOfMappedViews -= 1;
    02081
    02082     ControlArea->u.Flags.BeingPurged = 0;
    02083
    02084     //
    02085     // Check to see if the control area should be deleted.  This
    02086     // will release the PFN lock.
    02087     //
    02088
    02089     MiCheckControlArea (ControlArea, NULL, OldIrql);
    02090     return ReturnValue;
    02091 }

NTSTATUS NtFlushVirtualMemory | ( | IN HANDLE | ProcessHandle,
|  | IN OUT PVOID * | BaseAddress,
|  | IN OUT PSIZE_T | RegionSize,
|  | OUT PIO_STATUS_BLOCK | IoStatus
| ) |

Definition at line 55 of file flushsec.c.

References EXCEPTION_EXECUTE_HANDLER, KernelMode, KPROCESSOR_MODE, MmFlushVirtualMemory(), NT_SUCCESS, NTSTATUS(), NULL, ObDereferenceObject, ObReferenceObjectByHandle(), PAGE_ALIGN, PAGED_CODE, ProbeForWriteIoStatus, ProbeForWritePointer, ProbeForWriteUlong_ptr, PsProcessType, and Status.

    00064                    :
    00065
    00066     This function flushes a range of virtual address which map
    00067     a data file back into the data file if they have been modified.
    00068
    00069 Arguments:
    00070
    00071     ProcessHandle - Supplies an open handle to a process object.
    00072
    00073     BaseAddress - Supplies a pointer to a variable that will receive
    00074          the base address the flushed region.  The initial value
    00075          of this argument is the base address of the region of the
    00076          pages to flush.
    00077
    00078     RegionSize - Supplies a pointer to a variable that will receive
    00079          the actual size in bytes of the flushed region of pages.
    00080          The initial value of this argument is rounded up to the
    00081          next host-page-size boundary.
    00082
    00083          If this value is specified as zero, the mapped range from
    00084          the base address to the end of the range is flushed.
    00085
    00086     IoStatus - Returns the value of the IoStatus for the last attempted
    00087          I/O operation.
    00088
    00089 Return Value:
    00090
    00091     Returns the status
    00092
    00093     TBS
    00094
    00095
    00096 --*/
    00097
    00098 {
    00099     PEPROCESS Process;
    00100     KPROCESSOR_MODE PreviousMode;
    00101     NTSTATUS Status;
    00102     PVOID CapturedBase;
    00103     SIZE_T CapturedRegionSize;
    00104     IO_STATUS_BLOCK TemporaryIoStatus;
    00105
    00106     PAGED_CODE();
    00107
    00108     PreviousMode = KeGetPreviousMode();
    00109     if (PreviousMode != KernelMode) {
    00110
    00111         //
    00112         // Establish an exception handler, probe the specified addresses
    00113         // for write access and capture the initial values.
    00114         //
    00115
    00116         try {
    00117
    00118             ProbeForWritePointer (BaseAddress);
    00119             ProbeForWriteUlong_ptr (RegionSize);
    00120             ProbeForWriteIoStatus (IoStatus);
    00121
    00122             //
    00123             // Capture the base address.
    00124             //
    00125
    00126             CapturedBase = *BaseAddress;
    00127
    00128             //
    00129             // Capture the region size.
    00130             //
    00131
    00132             CapturedRegionSize = *RegionSize;
    00133
    00134         } except (EXCEPTION_EXECUTE_HANDLER) {
    00135
    00136             //
    00137             // If an exception occurs during the probe or capture
    00138             // of the initial values, then handle the exception and
    00139             // return the exception code as the status value.
    00140             //
    00141
    00142             return GetExceptionCode();
    00143         }
    00144
    00145     } else {
    00146
    00147         //
    00148         // Capture the base address.
    00149         //
    00150
    00151         CapturedBase = *BaseAddress;
    00152
    00153         //
    00154         // Capture the region size.
    00155         //
    00156
    00157         CapturedRegionSize = *RegionSize;
    00158
    00159     }
    00160
    00161     //
    00162     // Make sure the specified starting and ending addresses are
    00163     // within the user part of the virtual address space.
    00164     //
    00165
    00166     if (CapturedBase > MM_HIGHEST_USER_ADDRESS) {
    00167
    00168         //
    00169         // Invalid base address.
    00170         //
    00171
    00172         return STATUS_INVALID_PARAMETER_2;
    00173     }
    00174
    00175     if (((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (ULONG_PTR)CapturedBase) <
    00176                                                         CapturedRegionSize) {
    00177
    00178         //
    00179         // Invalid region size;
    00180         //
    00181
    00182         return STATUS_INVALID_PARAMETER_2;
    00183
    00184     }
    00185
    00186     Status = ObReferenceObjectByHandle ( ProcessHandle,
    00187                                          PROCESS_VM_OPERATION,
    00188                                          PsProcessType,
    00189                                          PreviousMode,
    00190                                          (PVOID *)&Process,
    00191                                          NULL );
    00192     if (!NT_SUCCESS(Status)) {
    00193         return Status;
    00194     }
    00195
    00196     Status = MmFlushVirtualMemory (Process,
    00197                                    &CapturedBase,
    00198                                    &CapturedRegionSize,
    00199                                    &TemporaryIoStatus);
    00200
    00201     ObDereferenceObject (Process);
    00202
    00203     //
    00204     // Establish an exception handler and write the size and base
    00205     // address.
    00206     //
    00207
    00208     try {
    00209
    00210         *RegionSize = CapturedRegionSize;
    00211         *BaseAddress = PAGE_ALIGN (CapturedBase);
    00212         *IoStatus = TemporaryIoStatus;
    00213
    00214     } except (EXCEPTION_EXECUTE_HANDLER) {
    00215     }
    00216
    00217     return Status;
    00218
    00219 }

## Variable Documentation

POBJECT_TYPE IoFileObjectType

| Definition at line 52 of file flushsec.c.