mirror of
https://github.com/justinian/jsix.git
synced 2025-12-10 00:14:32 -08:00
[kernel] Improve VMA lifecycle
The vm_area objects had a number of issues I have been running into when working on srv.init: - It was impossible to map a VMA, fill it, unmap it, and hand it to another process. Unmapping the VMA in this process would cause all the pages to be freed, since it was removed from its last mapping. - If a VMA was marked with vm_flag::zero, it would be zeroed out _every time_ it was mapped into a vm_space. - The vm_area_open class was leaking its page_tree nodes. In order to fix these issues, the different VMA types all work slightly differently now: - Physical pages allocated for a VMA are now freed when the VMA is deleted, not when it is unmapped. - A knock-on effect from the first point is that vm_area_guarded is now based on vm_area_open, instead of vm_area_untracked. An untracked area cannot free its pages, since it does not track them. - The vm_area_open type now deletes its root page_tree node. And page_tree nodes will delete child nodes or free physical pages in their dtors. - vm_flag::zero has been removed; pages will need to be zeroed out further at a higher level. - vm_area also no longer deletes itself only on losing its last handle - it will only self-delete when all handles _and_ mappings are gone.
This commit is contained in:
@@ -6,5 +6,5 @@ enum j6_vm_flags {
|
|||||||
#define VM_FLAG(name, v) j6_vm_flag_ ## name = v,
|
#define VM_FLAG(name, v) j6_vm_flag_ ## name = v,
|
||||||
#include "j6/tables/vm_flags.inc"
|
#include "j6/tables/vm_flags.inc"
|
||||||
#undef VM_FLAG
|
#undef VM_FLAG
|
||||||
j6_vm_flags_MAX
|
j6_vm_flag_MAX
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
VM_FLAG( none, 0x00000000)
|
VM_FLAG( none, 0x00000000)
|
||||||
VM_FLAG( write, 0x00000001)
|
VM_FLAG( write, 0x00000001)
|
||||||
VM_FLAG( exec, 0x00000002)
|
VM_FLAG( exec, 0x00000002)
|
||||||
VM_FLAG( zero, 0x00000010)
|
|
||||||
VM_FLAG( contiguous, 0x00000020)
|
VM_FLAG( contiguous, 0x00000020)
|
||||||
VM_FLAG( large_pages, 0x00000100)
|
VM_FLAG( large_pages, 0x00000100)
|
||||||
VM_FLAG( huge_pages, 0x00000200)
|
VM_FLAG( huge_pages, 0x00000200)
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ namespace memory {
|
|||||||
/// Size of a single page frame.
|
/// Size of a single page frame.
|
||||||
constexpr size_t frame_size = 0x1000;
|
constexpr size_t frame_size = 0x1000;
|
||||||
|
|
||||||
|
/// Number of bits of addressing within a page
|
||||||
|
constexpr size_t frame_bits = 12;
|
||||||
|
|
||||||
/// Start of kernel memory.
|
/// Start of kernel memory.
|
||||||
constexpr uintptr_t kernel_offset = 0xffff800000000000ull;
|
constexpr uintptr_t kernel_offset = 0xffff800000000000ull;
|
||||||
|
|
||||||
|
|||||||
@@ -27,19 +27,20 @@ vm_area::add_to(vm_space *space)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
vm_area::remove_from(vm_space *space)
|
vm_area::remove_from(vm_space *space)
|
||||||
{
|
{
|
||||||
m_spaces.remove_swap(space);
|
m_spaces.remove_swap(space);
|
||||||
return
|
if (!m_spaces.count() &&
|
||||||
!m_spaces.count() &&
|
check_signal(j6_signal_no_handles))
|
||||||
!(m_flags && vm_flags::mmio);
|
delete this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
vm_area::on_no_handles()
|
vm_area::on_no_handles()
|
||||||
{
|
{
|
||||||
kobject::on_no_handles();
|
kobject::on_no_handles();
|
||||||
|
if (!m_spaces.count())
|
||||||
delete this;
|
delete this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,15 +67,24 @@ vm_area_fixed::vm_area_fixed(uintptr_t start, size_t size, vm_flags flags) :
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
vm_area_fixed::~vm_area_fixed() {}
|
vm_area_fixed::~vm_area_fixed()
|
||||||
|
{
|
||||||
|
if (m_flags && vm_flags::mmio)
|
||||||
|
return;
|
||||||
|
|
||||||
size_t vm_area_fixed::resize(size_t size)
|
size_t pages = memory::page_count(m_size);
|
||||||
|
frame_allocator::get().free(m_start, pages);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
vm_area_fixed::resize(size_t size)
|
||||||
{
|
{
|
||||||
// Not resizable
|
// Not resizable
|
||||||
return m_size;
|
return m_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool vm_area_fixed::get_page(uintptr_t offset, uintptr_t &phys)
|
bool
|
||||||
|
vm_area_fixed::get_page(uintptr_t offset, uintptr_t &phys)
|
||||||
{
|
{
|
||||||
if (offset > m_size)
|
if (offset > m_size)
|
||||||
return false;
|
return false;
|
||||||
@@ -83,13 +93,15 @@ bool vm_area_fixed::get_page(uintptr_t offset, uintptr_t &phys)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
vm_area_untracked::vm_area_untracked(size_t size, vm_flags flags) :
|
vm_area_untracked::vm_area_untracked(size_t size, vm_flags flags) :
|
||||||
vm_area {size, flags}
|
vm_area {size, flags}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
vm_area_untracked::~vm_area_untracked() {}
|
vm_area_untracked::~vm_area_untracked()
|
||||||
|
{
|
||||||
|
kassert(false, "An untracked VMA's pages cannot be reclaimed, leaking memory");
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
vm_area_untracked::get_page(uintptr_t offset, uintptr_t &phys)
|
vm_area_untracked::get_page(uintptr_t offset, uintptr_t &phys)
|
||||||
@@ -115,7 +127,11 @@ vm_area_open::vm_area_open(size_t size, vm_flags flags) :
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
vm_area_open::~vm_area_open() {}
|
vm_area_open::~vm_area_open()
|
||||||
|
{
|
||||||
|
// the page_tree will free its pages when deleted
|
||||||
|
delete m_mapped;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
vm_area_open::get_page(uintptr_t offset, uintptr_t &phys)
|
vm_area_open::get_page(uintptr_t offset, uintptr_t &phys)
|
||||||
@@ -128,7 +144,7 @@ vm_area_guarded::vm_area_guarded(uintptr_t start, size_t buf_pages, size_t size,
|
|||||||
m_start {start},
|
m_start {start},
|
||||||
m_pages {buf_pages},
|
m_pages {buf_pages},
|
||||||
m_next {memory::frame_size},
|
m_next {memory::frame_size},
|
||||||
vm_area_untracked {size, flags}
|
vm_area_open {size, flags}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,6 +180,5 @@ vm_area_guarded::get_page(uintptr_t offset, uintptr_t &phys)
|
|||||||
if ((offset >> 12) % (m_pages+1) == 0)
|
if ((offset >> 12) % (m_pages+1) == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return vm_area_untracked::get_page(offset, phys);
|
return vm_area_open::get_page(offset, phys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,9 +52,7 @@ public:
|
|||||||
|
|
||||||
/// Track that this area was removed frm a vm_space
|
/// Track that this area was removed frm a vm_space
|
||||||
/// \arg space The space that is removing this area
|
/// \arg space The space that is removing this area
|
||||||
/// \returns True if the removing space should free the pages
|
virtual void remove_from(vm_space *space);
|
||||||
/// mapped for this area
|
|
||||||
virtual bool remove_from(vm_space *space);
|
|
||||||
|
|
||||||
/// Change the virtual size of the memory area. This may cause
|
/// Change the virtual size of the memory area. This may cause
|
||||||
/// deallocation if the new size is smaller than the current size.
|
/// deallocation if the new size is smaller than the current size.
|
||||||
@@ -141,9 +139,8 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
/// Area split into standard-sized segments, separated by guard pages.
|
/// Area split into standard-sized segments, separated by guard pages.
|
||||||
/// Based on vm_area_untracked, can not be shared.
|
|
||||||
class vm_area_guarded :
|
class vm_area_guarded :
|
||||||
public vm_area_untracked
|
public vm_area_open
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
|
|||||||
@@ -4,10 +4,10 @@
|
|||||||
#include "kernel_memory.h"
|
#include "kernel_memory.h"
|
||||||
#include "page_tree.h"
|
#include "page_tree.h"
|
||||||
|
|
||||||
// Page tree levels map the following parts of a pagewise offset. Note the xxx
|
// Page tree levels map the following parts of an offset. Note the xxx part of
|
||||||
// are not part of the offset but represent the bits added for the actual virtual
|
// the offset but represent the bits of the actual sub-page virtual address.
|
||||||
// address. (Also note that level 0's entries are physical page addrs, the rest
|
// (Also note that level 0's entries are physical page addrs, the rest map
|
||||||
// map other page_tree nodes)
|
// other page_tree nodes)
|
||||||
//
|
//
|
||||||
// Level 0: 0000 0000 0003 fxxx 64 pages / 256 KiB
|
// Level 0: 0000 0000 0003 fxxx 64 pages / 256 KiB
|
||||||
// Level 1: 0000 0000 00fc 0xxx 4K pages / 16 MiB -- 24-bit addressing
|
// Level 1: 0000 0000 00fc 0xxx 4K pages / 16 MiB -- 24-bit addressing
|
||||||
@@ -36,6 +36,20 @@ page_tree::page_tree(uint64_t base, uint8_t level) :
|
|||||||
kutil::memset(m_entries, 0, sizeof(m_entries));
|
kutil::memset(m_entries, 0, sizeof(m_entries));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
page_tree::~page_tree()
|
||||||
|
{
|
||||||
|
if (m_level) {
|
||||||
|
for (auto &e : m_entries)
|
||||||
|
delete e.child;
|
||||||
|
} else {
|
||||||
|
auto &fa = frame_allocator::get();
|
||||||
|
for (auto &e : m_entries) {
|
||||||
|
if (e.entry & 1)
|
||||||
|
fa.free(e.entry & ~0xfffull, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
page_tree::contains(uint64_t offset, uint8_t &index) const
|
page_tree::contains(uint64_t offset, uint8_t &index) const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ public:
|
|||||||
/// \returns True if a page was found
|
/// \returns True if a page was found
|
||||||
static bool find_or_add(page_tree * &root, uint64_t offset, uintptr_t &page);
|
static bool find_or_add(page_tree * &root, uint64_t offset, uintptr_t &page);
|
||||||
|
|
||||||
|
~page_tree();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
page_tree(uint64_t base, uint8_t level);
|
page_tree(uint64_t base, uint8_t level);
|
||||||
|
|
||||||
|
|||||||
@@ -45,11 +45,8 @@ vm_space::vm_space() :
|
|||||||
|
|
||||||
vm_space::~vm_space()
|
vm_space::~vm_space()
|
||||||
{
|
{
|
||||||
for (auto &a : m_areas) {
|
for (auto &a : m_areas)
|
||||||
bool free = a.area->remove_from(this);
|
remove_area(a.area);
|
||||||
clear(*a.area, 0, memory::page_count(a.area->size()), free);
|
|
||||||
a.area->handle_release();
|
|
||||||
}
|
|
||||||
|
|
||||||
kassert(!is_kernel(), "Kernel vm_space destructor!");
|
kassert(!is_kernel(), "Kernel vm_space destructor!");
|
||||||
if (active())
|
if (active())
|
||||||
@@ -76,15 +73,21 @@ vm_space::add(uintptr_t base, vm_area *area)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
vm_space::remove_area(vm_area *area)
|
||||||
|
{
|
||||||
|
area->remove_from(this);
|
||||||
|
clear(*area, 0, memory::page_count(area->size()));
|
||||||
|
area->handle_release();
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
vm_space::remove(vm_area *area)
|
vm_space::remove(vm_area *area)
|
||||||
{
|
{
|
||||||
for (auto &a : m_areas) {
|
for (auto &a : m_areas) {
|
||||||
if (a.area == area) {
|
if (a.area == area) {
|
||||||
bool free = area->remove_from(this);
|
remove_area(area);
|
||||||
clear(*area, 0, memory::page_count(area->size()), free);
|
|
||||||
m_areas.remove(a);
|
m_areas.remove(a);
|
||||||
area->handle_release();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -277,10 +280,6 @@ vm_space::handle_fault(uintptr_t addr, fault_type fault)
|
|||||||
if (!area->get_page(offset, phys_page))
|
if (!area->get_page(offset, phys_page))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
void *mem = memory::to_virtual<void>(phys_page);
|
|
||||||
if (area->flags() && vm_flags::zero)
|
|
||||||
kutil::memset(mem, 0, memory::frame_size);
|
|
||||||
|
|
||||||
page_in(*area, offset, phys_page, 1);
|
page_in(*area, offset, phys_page, 1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,7 +107,6 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
friend class vm_area;
|
friend class vm_area;
|
||||||
friend class vm_mapper_multi;
|
|
||||||
|
|
||||||
/// Find a given VMA in this address space
|
/// Find a given VMA in this address space
|
||||||
bool find_vma(const vm_area &vma, uintptr_t &base) const;
|
bool find_vma(const vm_area &vma, uintptr_t &base) const;
|
||||||
@@ -118,6 +117,9 @@ private:
|
|||||||
/// Copy a range of mappings from the given address space
|
/// Copy a range of mappings from the given address space
|
||||||
void copy_from(const vm_space &source, const vm_area &vma);
|
void copy_from(const vm_space &source, const vm_area &vma);
|
||||||
|
|
||||||
|
/// Remove an area's mappings from this space
|
||||||
|
void remove_area(vm_area *area);
|
||||||
|
|
||||||
bool m_kernel;
|
bool m_kernel;
|
||||||
page_table *m_pml4;
|
page_table *m_pml4;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user