Kernel: Stop leaking leftover committed cow pages from forked processes

Since both the parent process and child process hold a reference to the
COW committed set, once the child process exits, the committed COW
pages are effectively leaked, only being slowly re-claimed each time
the parent process writes to one of them, realizing it's no longer
shared, and uncommitting it.
In order to mitigate this we now hold a weak reference the parent
VMObject from which the pages are cloned, and we use it on destruction
when available to drop the reference to the committed set from it as
well.
This commit is contained in:
Idan Horowitz 2022-07-10 16:40:48 +03:00
parent c1fe844da4
commit 1d96c30488
2 changed files with 18 additions and 5 deletions

View file

@ -124,7 +124,8 @@ ErrorOr<NonnullRefPtr<AnonymousVMObject>> AnonymousVMObject::try_create_for_phys
ErrorOr<NonnullRefPtr<AnonymousVMObject>> AnonymousVMObject::try_create_with_shared_cow(AnonymousVMObject const& other, NonnullRefPtr<SharedCommittedCowPages> shared_committed_cow_pages, FixedArray<RefPtr<PhysicalPage>>&& new_physical_pages)
{
auto vmobject = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) AnonymousVMObject(other, move(shared_committed_cow_pages), move(new_physical_pages))));
auto weak_parent = TRY(other.try_make_weak_ptr<AnonymousVMObject>());
auto vmobject = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) AnonymousVMObject(move(weak_parent), move(shared_committed_cow_pages), move(new_physical_pages))));
TRY(vmobject->ensure_cow_map());
@ -159,14 +160,25 @@ AnonymousVMObject::AnonymousVMObject(FixedArray<RefPtr<PhysicalPage>>&& new_phys
{
}
AnonymousVMObject::AnonymousVMObject(AnonymousVMObject const& other, NonnullRefPtr<SharedCommittedCowPages> shared_committed_cow_pages, FixedArray<RefPtr<PhysicalPage>>&& new_physical_pages)
AnonymousVMObject::AnonymousVMObject(WeakPtr<AnonymousVMObject> other, NonnullRefPtr<SharedCommittedCowPages> shared_committed_cow_pages, FixedArray<RefPtr<PhysicalPage>>&& new_physical_pages)
: VMObject(move(new_physical_pages))
, m_cow_parent(move(other))
, m_shared_committed_cow_pages(move(shared_committed_cow_pages))
, m_purgeable(other.m_purgeable)
, m_purgeable(m_cow_parent.strong_ref()->m_purgeable)
{
}
AnonymousVMObject::~AnonymousVMObject() = default;
AnonymousVMObject::~AnonymousVMObject()
{
if (!m_shared_committed_cow_pages || m_shared_committed_cow_pages->is_empty())
return;
auto cow_parent = m_cow_parent.strong_ref();
if (!cow_parent)
return;
SpinlockLocker lock(cow_parent->m_lock);
if (cow_parent->m_shared_committed_cow_pages == m_shared_committed_cow_pages)
cow_parent->m_shared_committed_cow_pages.clear();
}
size_t AnonymousVMObject::purge()
{

View file

@ -46,7 +46,7 @@ private:
explicit AnonymousVMObject(FixedArray<RefPtr<PhysicalPage>>&&, AllocationStrategy, Optional<CommittedPhysicalPageSet>);
explicit AnonymousVMObject(PhysicalAddress, FixedArray<RefPtr<PhysicalPage>>&&);
explicit AnonymousVMObject(FixedArray<RefPtr<PhysicalPage>>&&);
explicit AnonymousVMObject(AnonymousVMObject const&, NonnullRefPtr<SharedCommittedCowPages>, FixedArray<RefPtr<PhysicalPage>>&&);
explicit AnonymousVMObject(WeakPtr<AnonymousVMObject>, NonnullRefPtr<SharedCommittedCowPages>, FixedArray<RefPtr<PhysicalPage>>&&);
virtual StringView class_name() const override { return "AnonymousVMObject"sv; }
@ -82,6 +82,7 @@ private:
CommittedPhysicalPageSet m_committed_pages;
};
WeakPtr<AnonymousVMObject> m_cow_parent;
RefPtr<SharedCommittedCowPages> m_shared_committed_cow_pages;
bool m_purgeable { false };