summaryrefslogtreecommitdiff
path: root/Kernel/VM
diff options
context:
space:
mode:
authorAndreas Kling <awesomekling@gmail.com>2019-12-26 11:45:36 +0100
committerAndreas Kling <awesomekling@gmail.com>2019-12-26 11:45:36 +0100
commitc1f8291ce499367f52e223f44eba8d3eb884acee (patch)
tree2d3c5c7e34fccb088fc6ea539eac4575c50f7597 /Kernel/VM
parentdafd715743c710a07deb7cf2de58a0d0ab5a14a3 (diff)
downloadserenity-c1f8291ce499367f52e223f44eba8d3eb884acee.zip
Kernel: When physical page allocation fails, try to purge something
Instead of panicking right away when we run out of physical pages, we now try to find a PurgeableVMObject with some volatile pages in it. If we find one, we purge that entire object and steal one of its pages. This makes it possible for the kernel to keep going instead of dying. Very cool. :^)
Diffstat (limited to 'Kernel/VM')
-rw-r--r--Kernel/VM/MemoryManager.cpp23
-rw-r--r--Kernel/VM/PurgeableVMObject.cpp13
-rw-r--r--Kernel/VM/PurgeableVMObject.h3
3 files changed, 36 insertions, 3 deletions
diff --git a/Kernel/VM/MemoryManager.cpp b/Kernel/VM/MemoryManager.cpp
index db2a55e0f7..8f44ddc25a 100644
--- a/Kernel/VM/MemoryManager.cpp
+++ b/Kernel/VM/MemoryManager.cpp
@@ -9,6 +9,7 @@
#include <Kernel/VM/AnonymousVMObject.h>
#include <Kernel/VM/InodeVMObject.h>
#include <Kernel/VM/MemoryManager.h>
+#include <Kernel/VM/PurgeableVMObject.h>
//#define MM_DEBUG
//#define PAGE_FAULT_DEBUG
@@ -424,9 +425,25 @@ RefPtr<PhysicalPage> MemoryManager::allocate_user_physical_page(ShouldZeroFill s
kprintf("MM: no user physical regions available (?)\n");
}
- kprintf("MM: no user physical pages available\n");
- ASSERT_NOT_REACHED();
- return {};
+ for_each_vmobject([&](auto& vmobject) {
+ if (vmobject.is_purgeable()) {
+ auto& purgeable_vmobject = static_cast<PurgeableVMObject&>(vmobject);
+ int purged_page_count = purgeable_vmobject.purge_with_interrupts_disabled({});
+ if (purged_page_count) {
+ kprintf("MM: Purge saved the day! Purged %d pages from PurgeableVMObject{%p}\n", purged_page_count, &purgeable_vmobject);
+ page = find_free_user_physical_page();
+ ASSERT(page);
+ return IterationDecision::Break;
+ }
+ }
+ return IterationDecision::Continue;
+ });
+
+ if (!page) {
+ kprintf("MM: no user physical pages available\n");
+ ASSERT_NOT_REACHED();
+ return {};
+ }
}
#ifdef MM_DEBUG
diff --git a/Kernel/VM/PurgeableVMObject.cpp b/Kernel/VM/PurgeableVMObject.cpp
index d06b27a901..f671f80817 100644
--- a/Kernel/VM/PurgeableVMObject.cpp
+++ b/Kernel/VM/PurgeableVMObject.cpp
@@ -29,6 +29,19 @@ NonnullRefPtr<VMObject> PurgeableVMObject::clone()
int PurgeableVMObject::purge()
{
LOCKER(m_paging_lock);
+ return purge_impl();
+}
+
+int PurgeableVMObject::purge_with_interrupts_disabled(Badge<MemoryManager>)
+{
+ ASSERT_INTERRUPTS_DISABLED();
+ if (m_paging_lock.is_locked())
+ return 0;
+ return purge_impl();
+}
+
+int PurgeableVMObject::purge_impl()
+{
if (!m_volatile)
return 0;
int purged_page_count = 0;
diff --git a/Kernel/VM/PurgeableVMObject.h b/Kernel/VM/PurgeableVMObject.h
index d352ff7c97..c357ebc47c 100644
--- a/Kernel/VM/PurgeableVMObject.h
+++ b/Kernel/VM/PurgeableVMObject.h
@@ -10,6 +10,7 @@ public:
virtual NonnullRefPtr<VMObject> clone() override;
int purge();
+ int purge_with_interrupts_disabled(Badge<MemoryManager>);
bool was_purged() const { return m_was_purged; }
void set_was_purged(bool b) { m_was_purged = b; }
@@ -21,6 +22,8 @@ private:
explicit PurgeableVMObject(size_t);
explicit PurgeableVMObject(const PurgeableVMObject&);
+ int purge_impl();
+
PurgeableVMObject& operator=(const PurgeableVMObject&) = delete;
PurgeableVMObject& operator=(PurgeableVMObject&&) = delete;
PurgeableVMObject(PurgeableVMObject&&) = delete;