We don't have "huge" version of PTE_SWP_UFFD_WP_SPECIAL, instead when necessary
we split the thp if the huge page is uffd wr-protected previously.

However split the thp is not enough, because file-backed thp is handled totally
differently comparing to anonymous thps - rather than doing a real split, the
thp pmd will simply got dropped in __split_huge_pmd_locked().

That is definitely not enough if e.g. when there is a thp covers range [0, 2M)
but we want to wr-protect small page resides in [4K, 8K) range, because after
__split_huge_pmd() returns, there will be a none pmd.

Here we leverage the previously introduced change_protection_prepare() macro so
that we'll populate the pmd with a pgtable page.  Then change_pte_range() will
do all the rest for us, e.g., install the uffd-wp swap special pte marker at
any pte that we'd like to wr-protect, under the protection of pgtable lock.

Signed-off-by: Peter Xu <pet...@redhat.com>
---
 mm/mprotect.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/mm/mprotect.c b/mm/mprotect.c
index 6b63e3544b47..51c954afa406 100644
--- a/mm/mprotect.c
+++ b/mm/mprotect.c
@@ -296,8 +296,16 @@ static inline unsigned long change_pmd_range(struct 
vm_area_struct *vma,
                }
 
                if (is_swap_pmd(*pmd) || pmd_trans_huge(*pmd) || 
pmd_devmap(*pmd)) {
-                       if (next - addr != HPAGE_PMD_SIZE) {
+                       if (next - addr != HPAGE_PMD_SIZE ||
+                           /* Uffd wr-protecting a file-backed memory range */
+                           unlikely(!vma_is_anonymous(vma) &&
+                                    (cp_flags & MM_CP_UFFD_WP))) {
                                __split_huge_pmd(vma, pmd, addr, false, NULL);
+                               /*
+                                * For file-backed, the pmd could have been
+                                * gone; still provide a pte pgtable if needed.
+                                */
+                               change_protection_prepare(vma, pmd, addr, 
cp_flags);
                        } else {
                                int nr_ptes = change_huge_pmd(vma, pmd, addr,
                                                              newprot, 
cp_flags);
-- 
2.26.2

Reply via email to