Kernel: Share committed COW pages between whole VMObject lineage

When cloning an AnonymousVMObject, committed COW pages are shared
between the parent and child object. Whicever object COW's first will
take the shared committed page, and if the other object ends up doing
a COW as well, it will notice that the page is no longer shared by
two objects and simple remap it as read/write.

When a child is COW'ed again, while still having shared committed
pages with its own parent, the grandchild object will now join in the
sharing pool with its parent and grandparent. This means that the first
2 of 3 objects that COW will draw from the shared committed pages, and
3rd will remap read/write.

Previously, we would "fork" the shared committed pages when cloning,
which could lead to a situation where the grandparent held on to 1 of
the 3 needed shared committed pages. If both the child and grandchild
COW'ed, they wouldn't have enough pages, and since the grandparent
maintained an extra +1 ref count on the page, it wasn't possible to
to remap read/write.
This commit is contained in:
Andreas Kling 2021-07-30 12:54:13 +02:00
parent bccdc08487
commit 2c0df5e7e7

View file

@ -39,17 +39,16 @@ RefPtr<VMObject> AnonymousVMObject::try_clone()
if (!MM.commit_user_physical_pages(new_cow_pages_needed))
return {};
// Create or replace the committed cow pages. When cloning a previously
// cloned vmobject, we want to essentially "fork", leaving us and the
// new clone with one set of shared committed cow pages, and the original
// one would keep the one it still has. This ensures that the original
// one and this one, as well as the clone have sufficient resources
// to cow all pages as needed
m_shared_committed_cow_pages = try_create<CommittedCowPages>(new_cow_pages_needed);
if (!m_shared_committed_cow_pages) {
MM.uncommit_user_physical_pages(new_cow_pages_needed);
return {};
if (m_shared_committed_cow_pages) {
// We already have shared committed COW pages with another object.
// That sharing pool grows and the clone joins it.
m_shared_committed_cow_pages->m_committed_pages += new_cow_pages_needed;
} else {
m_shared_committed_cow_pages = try_create<CommittedCowPages>(new_cow_pages_needed);
if (!m_shared_committed_cow_pages) {
MM.uncommit_user_physical_pages(new_cow_pages_needed);
return {};
}
}
// Both original and clone become COW. So create a COW map for ourselves