mirror of
https://github.com/reactos/reactos.git
synced 2025-08-03 13:45:50 +00:00
Documented parts of the page fault path and flagged some possible locking
issues. Implemented waiting on page i/o. svn path=/trunk/; revision=1211
This commit is contained in:
parent
fcf9dcd627
commit
17e1656941
2 changed files with 135 additions and 9 deletions
|
@ -288,15 +288,36 @@ PVOID MmAllocPage(VOID)
|
||||||
return((PVOID)offset);
|
return((PVOID)offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
NTSTATUS MmWaitForPage(PVOID Page)
|
NTSTATUS MmWaitForPage(PVOID PhysicalAddress)
|
||||||
{
|
{
|
||||||
return(STATUS_SUCCESS);
|
NTSTATUS Status;
|
||||||
|
ULONG Start;
|
||||||
|
|
||||||
|
Start = (ULONG)PhysicalAddress / PAGESIZE;
|
||||||
|
Status = KeWaitForSingleObject(&MmPageArray[Start].Event,
|
||||||
|
UserRequest,
|
||||||
|
KernelMode,
|
||||||
|
FALSE,
|
||||||
|
NULL);
|
||||||
|
return(Status);
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID MmClearWaitPage(PVOID Page)
|
VOID MmClearWaitPage(PVOID PhysicalAddress)
|
||||||
{
|
{
|
||||||
|
ULONG Start;
|
||||||
|
|
||||||
|
Start = (ULONG)PhysicalAddress / PAGESIZE;
|
||||||
|
|
||||||
|
KeClearEvent(&MmPageArray[Start].Event);
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID MmSetWaitPage(PVOID Page)
|
VOID MmSetWaitPage(PVOID PhysicalAddress)
|
||||||
{
|
{
|
||||||
|
ULONG Start;
|
||||||
|
|
||||||
|
Start = (ULONG)PhysicalAddress / PAGESIZE;
|
||||||
|
|
||||||
|
KeSetEvent(&MmPageArray[Start].Event,
|
||||||
|
IO_DISK_INCREMENT,
|
||||||
|
FALSE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* $Id: section.c,v 1.32 2000/06/25 03:59:15 dwelch Exp $
|
/* $Id: section.c,v 1.33 2000/06/26 19:41:43 dwelch Exp $
|
||||||
*
|
*
|
||||||
* COPYRIGHT: See COPYING in the top level directory
|
* COPYRIGHT: See COPYING in the top level directory
|
||||||
* PROJECT: ReactOS kernel
|
* PROJECT: ReactOS kernel
|
||||||
|
@ -264,16 +264,26 @@ NTSTATUS MmNotPresentFaultSectionView(PMADDRESS_SPACE AddressSpace,
|
||||||
DPRINT("MmSectionHandleFault(MemoryArea %x, Address %x)\n",
|
DPRINT("MmSectionHandleFault(MemoryArea %x, Address %x)\n",
|
||||||
MemoryArea,Address);
|
MemoryArea,Address);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There is a window between taking the page fault and locking the
|
||||||
|
* address space when another thread could load the page so we check
|
||||||
|
* that.
|
||||||
|
*/
|
||||||
if (MmIsPagePresent(NULL, Address))
|
if (MmIsPagePresent(NULL, Address))
|
||||||
{
|
{
|
||||||
DbgPrint("Page is already present\n");
|
DbgPrint("Page is already present\n");
|
||||||
KeBugCheck(0);
|
return(STATUS_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
PAddress = (ULONG)PAGE_ROUND_DOWN(((ULONG)Address));
|
PAddress = (ULONG)PAGE_ROUND_DOWN(((ULONG)Address));
|
||||||
Offset.QuadPart = (PAddress - (ULONG)MemoryArea->BaseAddress) +
|
Offset.QuadPart = (PAddress - (ULONG)MemoryArea->BaseAddress) +
|
||||||
MemoryArea->Data.SectionData.ViewOffset;
|
MemoryArea->Data.SectionData.ViewOffset;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is a temporary hack because we need to map PE sections
|
||||||
|
* to within a finer granularity than data file section.
|
||||||
|
*/
|
||||||
if ((MemoryArea->Data.SectionData.ViewOffset % PAGESIZE) != 0)
|
if ((MemoryArea->Data.SectionData.ViewOffset % PAGESIZE) != 0)
|
||||||
{
|
{
|
||||||
return(MmUnalignedLoadPageForSection(AddressSpace,
|
return(MmUnalignedLoadPageForSection(AddressSpace,
|
||||||
|
@ -286,29 +296,51 @@ NTSTATUS MmNotPresentFaultSectionView(PMADDRESS_SPACE AddressSpace,
|
||||||
MemoryArea->Data.SectionData.ViewOffset);
|
MemoryArea->Data.SectionData.ViewOffset);
|
||||||
DPRINT("Got offset %x\n", Offset.QuadPart);
|
DPRINT("Got offset %x\n", Offset.QuadPart);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Lock the section
|
||||||
|
*/
|
||||||
Section = MemoryArea->Data.SectionData.Section;
|
Section = MemoryArea->Data.SectionData.Section;
|
||||||
|
|
||||||
DPRINT("Section %x\n", Section);
|
|
||||||
|
|
||||||
MmLockSection(Section);
|
MmLockSection(Section);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the entry corresponding to the offset within the section
|
||||||
|
*/
|
||||||
Entry = MmGetPageEntrySection(Section, Offset.u.LowPart);
|
Entry = MmGetPageEntrySection(Section, Offset.u.LowPart);
|
||||||
|
|
||||||
DPRINT("Entry %x\n", Entry);
|
DPRINT("Entry %x\n", Entry);
|
||||||
|
|
||||||
if (Entry == 0)
|
if (Entry == 0)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* If the entry is zero (and it can't change because we have
|
||||||
|
* locked the section) then we need to load the page.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create an mdl to hold the page we are going to read data into.
|
||||||
|
*/
|
||||||
Mdl = MmCreateMdl(NULL, NULL, PAGESIZE);
|
Mdl = MmCreateMdl(NULL, NULL, PAGESIZE);
|
||||||
MmBuildMdlFromPages(Mdl);
|
MmBuildMdlFromPages(Mdl);
|
||||||
Page = MmGetMdlPageAddress(Mdl, 0);
|
Page = MmGetMdlPageAddress(Mdl, 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clear the wait state (Since we are holding the only reference to
|
||||||
|
* page this is safe)
|
||||||
|
*/
|
||||||
MmClearWaitPage(Page);
|
MmClearWaitPage(Page);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Notify any other threads that fault on the same section offset
|
||||||
|
* that a page-in is pending.
|
||||||
|
*/
|
||||||
Entry = ((ULONG)Page) | SPE_PENDING;
|
Entry = ((ULONG)Page) | SPE_PENDING;
|
||||||
MmSetPageEntrySection(Section,
|
MmSetPageEntrySection(Section,
|
||||||
Offset.u.LowPart,
|
Offset.u.LowPart,
|
||||||
Entry);
|
Entry);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Release all our locks and read in the page from disk
|
||||||
|
*/
|
||||||
MmUnlockSection(Section);
|
MmUnlockSection(Section);
|
||||||
MmUnlockAddressSpace(AddressSpace);
|
MmUnlockAddressSpace(AddressSpace);
|
||||||
DPRINT("Reading file offset %x\n", Offset.QuadPart);
|
DPRINT("Reading file offset %x\n", Offset.QuadPart);
|
||||||
|
@ -318,12 +350,24 @@ NTSTATUS MmNotPresentFaultSectionView(PMADDRESS_SPACE AddressSpace,
|
||||||
&IoStatus);
|
&IoStatus);
|
||||||
if (!NT_SUCCESS(Status))
|
if (!NT_SUCCESS(Status))
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* FIXME: What do we know in this case?
|
||||||
|
*/
|
||||||
|
|
||||||
|
MmLockAddressSpace(AddressSpace);
|
||||||
return(Status);
|
return(Status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Relock the address space and section
|
||||||
|
*/
|
||||||
MmLockAddressSpace(AddressSpace);
|
MmLockAddressSpace(AddressSpace);
|
||||||
MmLockSection(Section);
|
MmLockSection(Section);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check the entry. No one should change the status of a page
|
||||||
|
* that has a pending page-in.
|
||||||
|
*/
|
||||||
Entry1 = MmGetPageEntrySection(Section, Offset.QuadPart);
|
Entry1 = MmGetPageEntrySection(Section, Offset.QuadPart);
|
||||||
if (Entry != Entry1)
|
if (Entry != Entry1)
|
||||||
{
|
{
|
||||||
|
@ -331,51 +375,112 @@ NTSTATUS MmNotPresentFaultSectionView(PMADDRESS_SPACE AddressSpace,
|
||||||
KeBugCheck(0);
|
KeBugCheck(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mark the offset within the section as having valid, in-memory
|
||||||
|
* data
|
||||||
|
*/
|
||||||
Entry = (ULONG)Page;
|
Entry = (ULONG)Page;
|
||||||
MmSetPageEntrySection(Section,
|
MmSetPageEntrySection(Section,
|
||||||
Offset.QuadPart,
|
Offset.QuadPart,
|
||||||
Entry);
|
Entry);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the event associated with the page so other threads that
|
||||||
|
* may be waiting know that valid data is now in-memory.
|
||||||
|
*/
|
||||||
MmSetWaitPage(Page);
|
MmSetWaitPage(Page);
|
||||||
}
|
}
|
||||||
else if (Entry & SPE_PENDING)
|
else if (Entry & SPE_PENDING)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* If a page-in on that section offset is pending that wait for
|
||||||
|
* it to finish.
|
||||||
|
*/
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* Release all our locks and wait for the pending operation
|
||||||
|
* to complete
|
||||||
|
*/
|
||||||
MmUnlockSection(Section);
|
MmUnlockSection(Section);
|
||||||
MmUnlockAddressSpace(AddressSpace);
|
MmUnlockAddressSpace(AddressSpace);
|
||||||
|
/*
|
||||||
|
* FIXME: What if the event is set and cleared after we
|
||||||
|
* unlock the section but before we wait.
|
||||||
|
*/
|
||||||
Status = MmWaitForPage((PVOID)(Entry & (~SPE_PENDING)));
|
Status = MmWaitForPage((PVOID)(Entry & (~SPE_PENDING)));
|
||||||
if (!NT_SUCCESS(Status))
|
if (!NT_SUCCESS(Status))
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* FIXME: What do we do in this case? Maybe the thread
|
||||||
|
* has terminated.
|
||||||
|
*/
|
||||||
|
|
||||||
DbgPrint("Failed to wait for page\n");
|
DbgPrint("Failed to wait for page\n");
|
||||||
KeBugCheck(0);
|
KeBugCheck(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Relock the address space and section
|
||||||
|
*/
|
||||||
MmLockAddressSpace(AddressSpace);
|
MmLockAddressSpace(AddressSpace);
|
||||||
MmLockSection(Section);
|
MmLockSection(Section);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the entry for the section offset. If the entry is still
|
||||||
|
* pending that means another thread is already trying the
|
||||||
|
* page-in again so we have to wait again.
|
||||||
|
*/
|
||||||
Entry = MmGetPageEntrySection(Section,
|
Entry = MmGetPageEntrySection(Section,
|
||||||
Offset.u.LowPart);
|
Offset.u.LowPart);
|
||||||
} while (Entry & SPE_PENDING);
|
} while (Entry & SPE_PENDING);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setting the entry to null means the read failing.
|
||||||
|
* FIXME: We should retry it (unless that filesystem has gone
|
||||||
|
* entirely e.g. the network died).
|
||||||
|
*/
|
||||||
if (Entry == 0)
|
if (Entry == 0)
|
||||||
{
|
{
|
||||||
DbgPrint("Entry set to null while we slept\n");
|
DbgPrint("Entry set to null while we slept\n");
|
||||||
KeBugCheck(0);
|
KeBugCheck(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Maybe the thread did the page-in took the fault on the
|
||||||
|
* same address-space/address as we did. If so we can just
|
||||||
|
* return success.
|
||||||
|
*/
|
||||||
if (MmIsPagePresent(NULL, Address))
|
if (MmIsPagePresent(NULL, Address))
|
||||||
{
|
{
|
||||||
MmUnlockSection(Section);
|
MmUnlockSection(Section);
|
||||||
return(STATUS_SUCCESS);
|
return(STATUS_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get a reference to the page containing the data for the page.
|
||||||
|
*/
|
||||||
Page = (PVOID)Entry;
|
Page = (PVOID)Entry;
|
||||||
MmReferencePage(Page);
|
MmReferencePage(Page);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* If the section offset is already in-memory and valid then just
|
||||||
|
* take another reference to the page
|
||||||
|
*/
|
||||||
|
|
||||||
Page = (PVOID)Entry;
|
Page = (PVOID)Entry;
|
||||||
MmReferencePage(Page);
|
MmReferencePage(Page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When we reach here, we have the address space and section locked
|
||||||
|
* and have a reference to a page containing valid data for the
|
||||||
|
* section offset. Set the page and return success.
|
||||||
|
*/
|
||||||
|
|
||||||
MmSetPage(NULL,
|
MmSetPage(NULL,
|
||||||
Address,
|
Address,
|
||||||
MemoryArea->Attributes,
|
MemoryArea->Attributes,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue