aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorAndrea Arcangeli <aarcange@redhat.com>2023-06-08 13:26:25 -0400
committerAndrea Arcangeli <aarcange@redhat.com>2023-11-11 22:03:36 -0500
commit051f877967290dbacff57b3f88cb0dbcc3651565 (patch)
tree2931142ce91349098f3c127fdc3e56a50d7137f6
parent6de8dad8746037c34c3755707cb860c5b810534a (diff)
downloadaa-051f877967290dbacff57b3f88cb0dbcc3651565.tar.gz
mm: gup: introduce PageAnonGup helpers
This uses the same technique as David's PageAnonExclusive. The implementation in turn is based on upstream commit 78fbe906cc900b33ce078102e13e0e99b5b8c406 . (cherry picked from commit 78fbe906cc900b33ce078102e13e0e99b5b8c406) Signed-off-by: Andrea Arcangeli <aarcange@redhat.com>
-rw-r--r--include/linux/page-flags.h15
-rw-r--r--mm/memory.c11
-rw-r--r--mm/swapfile.c3
3 files changed, 29 insertions, 0 deletions
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index a815a99ab23e8e..2146a9d8d0b3b4 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -185,6 +185,8 @@ enum pageflags {
/* Only valid for buddy pages. Used to track pages that are reported */
PG_reported = PG_uptodate,
+
+ PG_anon_gup = PG_mappedtodisk,
};
#define PAGEFLAGS_MASK ((1UL << NR_PAGEFLAGS) - 1)
@@ -628,6 +630,19 @@ TESTPAGEFLAG_FALSE(Huge)
TESTPAGEFLAG_FALSE(HeadHuge)
#endif
+static __always_inline int PageAnonGup(struct page *page)
+{
+ VM_BUG_ON_PAGE(!PageAnon(page), page);
+ VM_BUG_ON_PAGE(PageHuge(page) && !PageHead(page), page);
+ return test_bit(PG_anon_gup, &PF_ANY(page, 1)->flags);
+}
+
+static __always_inline void SetPageAnonGup(struct page *page)
+{
+ VM_BUG_ON_PAGE(!PageAnonNoKsm(page), page);
+ VM_BUG_ON_PAGE(PageHuge(page) && !PageHead(page), page);
+ set_bit(PG_anon_gup, &PF_ANY(page, 1)->flags);
+}
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
/*
diff --git a/mm/memory.c b/mm/memory.c
index 5d8075ca21ec8a..ab25ec3dcea1f9 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -3728,6 +3728,17 @@ static vm_fault_t remove_device_exclusive_entry(struct vm_fault *vmf)
struct mmu_notifier_range range;
/*
+ * PG_anon_gup reuses PG_mappedtodisk for anon pages. A swap pte
+ * must never point at an anonymous page in the swapcache that is
+ * PG_anon_gup. Sanity check that this holds and especially, that
+ * no filesystem set PG_mappedtodisk on a page in the swapcache. Sanity
+ * check after taking the PT lock and making sure that nobody
+ * concurrently faulted in this page and set PG_anon_gup.
+ */
+ BUG_ON(!PageAnon(page) && PageMappedToDisk(page));
+ BUG_ON(PageAnon(page) && PageAnonGup(page));
+
+ /*
* We need a reference to lock the page because we don't hold
* the PTL so a racing thread can remove the device-exclusive
* entry and unmap it. If the page is free the entry must
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 3616e87c399145..77fe42c1fc644c 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -1985,6 +1985,9 @@ static int unuse_pte(struct vm_area_struct *vma, pmd_t *pmd,
goto out;
}
+ /* See do_swap_page() */
+ BUG_ON(!PageAnon(page) && PageMappedToDisk(page));
+
dec_mm_counter(vma->vm_mm, MM_SWAPENTS);
inc_mm_counter(vma->vm_mm, MM_ANONPAGES);
get_page(page);