[NTOS/MM]

- Properly chain events when reading a page from disk, aka don't wait on events allocated on the stack of another thread
 - Use proper type pointers, compilers sometimes help making things right

svn path=/trunk/; revision=72493
This commit is contained in:
Jérôme Gardou 2016-08-28 19:53:27 +00:00
parent f25358387b
commit 3bb511ea04

View file

@ -882,10 +882,10 @@ MiResolvePageFileFault(_In_ BOOLEAN StoreInstruction,
PFN_NUMBER Page; PFN_NUMBER Page;
NTSTATUS Status; NTSTATUS Status;
MMPTE TempPte = *PointerPte; MMPTE TempPte = *PointerPte;
KEVENT Event;
PMMPFN Pfn1; PMMPFN Pfn1;
ULONG PageFileIndex = TempPte.u.Soft.PageFileLow; ULONG PageFileIndex = TempPte.u.Soft.PageFileLow;
ULONG_PTR PageFileOffset = TempPte.u.Soft.PageFileHigh; ULONG_PTR PageFileOffset = TempPte.u.Soft.PageFileHigh;
ULONG Protection = TempPte.u.Soft.Protection;
/* Things we don't support yet */ /* Things we don't support yet */
ASSERT(CurrentProcess > HYDRA_PROCESS); ASSERT(CurrentProcess > HYDRA_PROCESS);
@ -911,16 +911,10 @@ MiResolvePageFileFault(_In_ BOOLEAN StoreInstruction,
ASSERT(Pfn1->u1.Event == NULL); ASSERT(Pfn1->u1.Event == NULL);
ASSERT(Pfn1->u3.e1.ReadInProgress == 0); ASSERT(Pfn1->u3.e1.ReadInProgress == 0);
ASSERT(Pfn1->u3.e1.WriteInProgress == 0); ASSERT(Pfn1->u3.e1.WriteInProgress == 0);
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Pfn1->u1.Event = &Event;
Pfn1->u3.e1.ReadInProgress = 1; Pfn1->u3.e1.ReadInProgress = 1;
/* We must write the PTE now as the PFN lock will be released while performing the IO operation */ /* We must write the PTE now as the PFN lock will be released while performing the IO operation */
TempPte.u.Soft.Transition = 1; MI_MAKE_TRANSITION_PTE(&TempPte, Page, Protection);
TempPte.u.Soft.PageFileLow = 0;
TempPte.u.Soft.Prototype = 0;
TempPte.u.Trans.PageFrameNumber = Page;
MI_WRITE_INVALID_PTE(PointerPte, TempPte); MI_WRITE_INVALID_PTE(PointerPte, TempPte);
@ -934,7 +928,6 @@ MiResolvePageFileFault(_In_ BOOLEAN StoreInstruction,
*OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); *OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
/* Nobody should have changed that while we were not looking */ /* Nobody should have changed that while we were not looking */
ASSERT(Pfn1->u1.Event == &Event);
ASSERT(Pfn1->u3.e1.ReadInProgress == 1); ASSERT(Pfn1->u3.e1.ReadInProgress == 1);
ASSERT(Pfn1->u3.e1.WriteInProgress == 0); ASSERT(Pfn1->u3.e1.WriteInProgress == 0);
@ -946,27 +939,29 @@ MiResolvePageFileFault(_In_ BOOLEAN StoreInstruction,
Pfn1->u1.ReadStatus = Status; Pfn1->u1.ReadStatus = Status;
} }
/* This is now a nice and normal PFN */
Pfn1->u1.Event = NULL;
Pfn1->u3.e1.ReadInProgress = 0;
/* And the PTE can finally be valid */ /* And the PTE can finally be valid */
MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, TempPte.u.Trans.Protection, Page); MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, Protection, Page);
MI_WRITE_VALID_PTE(PointerPte, TempPte); MI_WRITE_VALID_PTE(PointerPte, TempPte);
/* Waiters gonna wait */ Pfn1->u3.e1.ReadInProgress = 0;
KeSetEvent(&Event, IO_NO_INCREMENT, FALSE); /* Did someone start to wait on us while we proceeded ? */
if (Pfn1->u1.Event)
{
/* Tell them we're done */
KeSetEvent(Pfn1->u1.Event, IO_NO_INCREMENT, FALSE);
}
return Status; return Status;
} }
NTSTATUS NTSTATUS
NTAPI NTAPI
MiResolveTransitionFault(IN PVOID FaultingAddress, MiResolveTransitionFault(IN BOOLEAN StoreInstruction,
IN PVOID FaultingAddress,
IN PMMPTE PointerPte, IN PMMPTE PointerPte,
IN PEPROCESS CurrentProcess, IN PEPROCESS CurrentProcess,
IN KIRQL OldIrql, IN KIRQL OldIrql,
OUT PVOID *InPageBlock) OUT PKEVENT **InPageBlock)
{ {
PFN_NUMBER PageFrameIndex; PFN_NUMBER PageFrameIndex;
PMMPFN Pfn1; PMMPFN Pfn1;
@ -999,11 +994,11 @@ MiResolveTransitionFault(IN PVOID FaultingAddress,
ASSERT(Pfn1->u4.InPageError == 0); ASSERT(Pfn1->u4.InPageError == 0);
/* See if we should wait before terminating the fault */ /* See if we should wait before terminating the fault */
if (Pfn1->u3.e1.ReadInProgress == 1) if ((Pfn1->u3.e1.ReadInProgress == 1)
|| ((Pfn1->u3.e1.WriteInProgress == 1) && StoreInstruction))
{ {
DPRINT1("The page is currently being read!\n"); DPRINT1("The page is currently in a page transition !\n");
ASSERT(Pfn1->u1.Event != NULL); *InPageBlock = &Pfn1->u1.Event;
*InPageBlock = Pfn1->u1.Event;
if (PointerPte == Pfn1->PteAddress) if (PointerPte == Pfn1->PteAddress)
{ {
DPRINT1("And this if for this particular PTE.\n"); DPRINT1("And this if for this particular PTE.\n");
@ -1061,7 +1056,7 @@ MiResolveTransitionFault(IN PVOID FaultingAddress,
} }
} }
/* Build the transition PTE -- maybe a macro? */ /* Build the final PTE */
ASSERT(PointerPte->u.Hard.Valid == 0); ASSERT(PointerPte->u.Hard.Valid == 0);
ASSERT(PointerPte->u.Trans.Prototype == 0); ASSERT(PointerPte->u.Trans.Prototype == 0);
ASSERT(PointerPte->u.Trans.Transition == 1); ASSERT(PointerPte->u.Trans.Transition == 1);
@ -1107,7 +1102,7 @@ MiResolveProtoPteFault(IN BOOLEAN StoreInstruction,
PMMPFN Pfn1; PMMPFN Pfn1;
PFN_NUMBER PageFrameIndex; PFN_NUMBER PageFrameIndex;
NTSTATUS Status; NTSTATUS Status;
PVOID InPageBlock = NULL; PKEVENT* InPageBlock = NULL;
ULONG Protection; ULONG Protection;
/* Must be called with an invalid, prototype PTE, with the PFN lock held */ /* Must be called with an invalid, prototype PTE, with the PFN lock held */
@ -1256,7 +1251,8 @@ MiResolveProtoPteFault(IN BOOLEAN StoreInstruction,
{ {
/* Resolve the transition fault */ /* Resolve the transition fault */
ASSERT(OldIrql != MM_NOIRQL); ASSERT(OldIrql != MM_NOIRQL);
Status = MiResolveTransitionFault(Address, Status = MiResolveTransitionFault(StoreInstruction,
Address,
PointerProtoPte, PointerProtoPte,
Process, Process,
OldIrql, OldIrql,
@ -1543,22 +1539,38 @@ MiDispatchFault(IN BOOLEAN StoreInstruction,
/* Is this a transition PTE */ /* Is this a transition PTE */
if (TempPte.u.Soft.Transition) if (TempPte.u.Soft.Transition)
{ {
PVOID InPageBlock = NULL; PKEVENT* InPageBlock = NULL;
PKEVENT PreviousPageEvent;
KEVENT CurrentPageEvent;
/* Lock the PFN database */ /* Lock the PFN database */
LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
/* Resolve */ /* Resolve */
Status = MiResolveTransitionFault(Address, PointerPte, Process, LockIrql, &InPageBlock); Status = MiResolveTransitionFault(StoreInstruction, Address, PointerPte, Process, LockIrql, &InPageBlock);
ASSERT(NT_SUCCESS(Status)); ASSERT(NT_SUCCESS(Status));
if (InPageBlock != NULL)
{
/* Another thread is reading or writing this page. Put us into the waiting queue. */
KeInitializeEvent(&CurrentPageEvent, NotificationEvent, FALSE);
PreviousPageEvent = *InPageBlock;
*InPageBlock = &CurrentPageEvent;
}
/* And now release the lock and leave*/ /* And now release the lock and leave*/
KeReleaseQueuedSpinLock(LockQueuePfnLock, LockIrql); KeReleaseQueuedSpinLock(LockQueuePfnLock, LockIrql);
if (InPageBlock != NULL) if (InPageBlock != NULL)
{ {
/* The page is being paged in by another process */ KeWaitForSingleObject(&CurrentPageEvent, WrPageIn, KernelMode, FALSE, NULL);
KeWaitForSingleObject(InPageBlock, WrPageIn, KernelMode, FALSE, NULL);
/* Let's the chain go on */
if (PreviousPageEvent)
{
KeSetEvent(PreviousPageEvent, IO_NO_INCREMENT, FALSE);
}
} }
ASSERT(OldIrql == KeGetCurrentIrql()); ASSERT(OldIrql == KeGetCurrentIrql());