The virtual memory fork code can now handle ENOMEM conditions.
(forkbombs now don't panic the system!)
This commit is contained in:
parent
e0e0cadf4a
commit
0f099c65ca
|
@ -510,65 +510,65 @@ namespace Sortix
|
||||||
return Unmap<true, false>(mapto);
|
return Unmap<true, false>(mapto);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an exact copy of the current address space.
|
void ForkCleanup(size_t i, size_t level)
|
||||||
|
{
|
||||||
|
PML* destpml = FORKPML + level;
|
||||||
|
if ( !i ) { return; }
|
||||||
|
for ( size_t n = 0; n < i-1; n++ )
|
||||||
|
{
|
||||||
|
addr_t entry = destpml->entry[i];
|
||||||
|
if ( !(entry & PML_FORK ) ) { continue; }
|
||||||
|
addr_t phys = entry & PML_ADDRESS;
|
||||||
|
if ( 1 < level )
|
||||||
|
{
|
||||||
|
addr_t destaddr = (addr_t) (FORKPML + level-1);
|
||||||
|
MapKernel(phys, destaddr);
|
||||||
|
InvalidatePage(destaddr);
|
||||||
|
ForkCleanup(ENTRIES+1UL, level-1);
|
||||||
|
}
|
||||||
|
Page::Put(phys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Copying every frame is endlessly useless in many uses. It'd be
|
// TODO: Copying every frame is endlessly useless in many uses. It'd be
|
||||||
// nice to upgrade this to a copy-on-write algorithm.
|
// nice to upgrade this to a copy-on-write algorithm.
|
||||||
addr_t Fork()
|
bool Fork(size_t level, size_t pmloffset)
|
||||||
{
|
{
|
||||||
addr_t newtoppmladdr = Page::Get();
|
PML* destpml = FORKPML + level;
|
||||||
if ( newtoppmladdr == 0 ) { return 0; }
|
for ( size_t i = 0; i < ENTRIES; i++ )
|
||||||
|
|
||||||
// This is either bad code or very clever code and probably is both.
|
|
||||||
size_t positionstack[TOPPMLLEVEL+1];
|
|
||||||
positionstack[TOPPMLLEVEL] = 0;
|
|
||||||
size_t level = TOPPMLLEVEL;
|
|
||||||
size_t pmloffset = 0;
|
|
||||||
bool failure = false;
|
|
||||||
|
|
||||||
// This call always succeeds.
|
|
||||||
MapKernel(newtoppmladdr, (addr_t) (FORKPML + level));
|
|
||||||
InvalidatePage((addr_t) (FORKPML + level));
|
|
||||||
|
|
||||||
// Recurse over the PMLs and fork what should be forked.
|
|
||||||
while ( positionstack[TOPPMLLEVEL] < ENTRIES )
|
|
||||||
{
|
{
|
||||||
const size_t pos = positionstack[level];
|
addr_t entry = (PMLS[level] + pmloffset)->entry[i];
|
||||||
|
|
||||||
if ( pos == ENTRIES )
|
// Link the entry if it isn't supposed to be forked.
|
||||||
|
if ( !(entry & PML_FORK ) )
|
||||||
{
|
{
|
||||||
(positionstack[++level])++;
|
destpml->entry[i] = entry;
|
||||||
pmloffset /= ENTRIES;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
addr_t entry = (PMLS[level] + pmloffset)->entry[pos];
|
|
||||||
|
|
||||||
// If the entry should be forked, fork it!
|
|
||||||
if ( (entry & PML_FORK) && (entry & PML_PRESENT) )
|
|
||||||
{
|
|
||||||
// Pop the physical address of somewhere unused.
|
|
||||||
addr_t phys = Page::Get();
|
addr_t phys = Page::Get();
|
||||||
|
if ( unlikely(!phys) ) { ForkCleanup(i, level); return false; }
|
||||||
|
|
||||||
if ( unlikely(phys == 0) )
|
addr_t flags = entry & PML_FLAGS;
|
||||||
{
|
destpml->entry[i] = phys | flags;
|
||||||
// Oh no. Out of memory! We'll have to undo everything
|
|
||||||
// we just did. Argh!
|
|
||||||
failure = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map the destination page.
|
// Map the destination page.
|
||||||
addr_t destaddr = (addr_t) (FORKPML + level-1);
|
addr_t destaddr = (addr_t) (FORKPML + level-1);
|
||||||
MapKernel(phys, destaddr);
|
MapKernel(phys, destaddr);
|
||||||
InvalidatePage(destaddr);
|
InvalidatePage(destaddr);
|
||||||
|
|
||||||
// Set its entry in the owner.
|
size_t offset = pmloffset * ENTRIES + i;
|
||||||
addr_t flags = entry & PML_FLAGS;
|
|
||||||
(FORKPML + level)->entry[pos] = phys | flags;
|
|
||||||
|
|
||||||
if ( level == 1 )
|
if ( 1 < level )
|
||||||
{
|
{
|
||||||
size_t offset = pmloffset * ENTRIES + pos;
|
if ( !Fork(level-1, offset) )
|
||||||
|
{
|
||||||
|
Page::Put(phys);
|
||||||
|
ForkCleanup(i, level);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Determine the source page's address.
|
// Determine the source page's address.
|
||||||
const void* src = (const void*) (offset * 4096UL);
|
const void* src = (const void*) (offset * 4096UL);
|
||||||
|
@ -576,34 +576,36 @@ namespace Sortix
|
||||||
// Determine the destination page's address.
|
// Determine the destination page's address.
|
||||||
void* dest = (void*) (FORKPML + level - 1);
|
void* dest = (void*) (FORKPML + level - 1);
|
||||||
|
|
||||||
Maxsi::Memory::Copy(dest, src, sizeof(PML));
|
Maxsi::Memory::Copy(dest, src, 4096UL);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Fork the PML recursively!
|
|
||||||
pmloffset = pmloffset * ENTRIES + pos;
|
|
||||||
positionstack[--level] = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this entry should be linked, link it.
|
return true;
|
||||||
else
|
|
||||||
{
|
|
||||||
FORKPML[level].entry[pos] = entry;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
positionstack[level]++;
|
bool Fork(addr_t dir, size_t level, size_t pmloffset)
|
||||||
|
{
|
||||||
|
PML* destpml = FORKPML + level;
|
||||||
|
|
||||||
|
// This call always succeeds.
|
||||||
|
MapKernel(dir, (addr_t) destpml);
|
||||||
|
InvalidatePage((addr_t) destpml);
|
||||||
|
|
||||||
|
return Fork(level, pmloffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !failure )
|
// Create an exact copy of the current address space.
|
||||||
|
addr_t Fork()
|
||||||
{
|
{
|
||||||
|
addr_t dir = Page::Get();
|
||||||
|
if ( dir == 0 ) { return 0; }
|
||||||
|
if ( !Fork(dir, TOPPMLLEVEL, 0) ) { Page::Put(dir); return 0; }
|
||||||
|
|
||||||
// Now, the new top pml needs to have its fractal memory fixed.
|
// Now, the new top pml needs to have its fractal memory fixed.
|
||||||
const addr_t flags = PML_PRESENT | PML_WRITABLE;
|
const addr_t flags = PML_PRESENT | PML_WRITABLE;
|
||||||
addr_t mapto;
|
addr_t mapto;
|
||||||
addr_t childaddr;
|
addr_t childaddr;
|
||||||
|
|
||||||
(FORKPML + TOPPMLLEVEL)->entry[ENTRIES-1] = newtoppmladdr | flags;
|
(FORKPML + TOPPMLLEVEL)->entry[ENTRIES-1] = dir | flags;
|
||||||
childaddr = (FORKPML + TOPPMLLEVEL)->entry[ENTRIES-2] & PML_ADDRESS;
|
childaddr = (FORKPML + TOPPMLLEVEL)->entry[ENTRIES-2] & PML_ADDRESS;
|
||||||
|
|
||||||
for ( size_t i = TOPPMLLEVEL-1; i > 0; i-- )
|
for ( size_t i = TOPPMLLEVEL-1; i > 0; i-- )
|
||||||
|
@ -611,22 +613,10 @@ namespace Sortix
|
||||||
mapto = (addr_t) (FORKPML + i);
|
mapto = (addr_t) (FORKPML + i);
|
||||||
MapKernel(childaddr, mapto);
|
MapKernel(childaddr, mapto);
|
||||||
InvalidatePage(mapto);
|
InvalidatePage(mapto);
|
||||||
(FORKPML + i)->entry[ENTRIES-1] = newtoppmladdr | flags;
|
(FORKPML + i)->entry[ENTRIES-1] = dir | flags;
|
||||||
childaddr = (FORKPML + i)->entry[ENTRIES-2] & PML_ADDRESS;
|
childaddr = (FORKPML + i)->entry[ENTRIES-2] & PML_ADDRESS;
|
||||||
}
|
}
|
||||||
|
return dir;
|
||||||
return newtoppmladdr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The fork failed, so'll have to clean up the new address space and
|
|
||||||
// free all the pages we forked so far. It'd be nice to detect that
|
|
||||||
// this would happen early on, but it seems to impractical or
|
|
||||||
// inefficient. Let's just do the dirty work and clean up.
|
|
||||||
|
|
||||||
// TODO: Fix this error condition by deleting the new pages.
|
|
||||||
Panic("Out of memory during fork. This isn't supported yet.");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue