move_page_tables() splits a huge PMD when the extent is smaller than
HPAGE_PMD_SIZE and the PMD can't be moved at PMD granularity.

If the split fails, the PMD stays huge and move_ptes() can't operate on
individual PTEs.

Break out of the loop on split failure, which causes mremap() to return
however much was moved so far (partial move).  This is consistent with
other allocation failures in the same loop (e.g., alloc_new_pmd(),
pte_alloc()).

Signed-off-by: Usama Arif <[email protected]>
---
 mm/mremap.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/mm/mremap.c b/mm/mremap.c
index 2be876a70cc0d..d067c9fbf140b 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -855,7 +855,13 @@ unsigned long move_page_tables(struct 
pagetable_move_control *pmc)
                        if (extent == HPAGE_PMD_SIZE &&
                            move_pgt_entry(pmc, HPAGE_PMD, old_pmd, new_pmd))
                                continue;
-                       split_huge_pmd(pmc->old, old_pmd, pmc->old_addr);
+                       /*
+                        * If split fails, the PMD stays huge and move_ptes
+                        * can't operate on it.  Break out so the caller
+                        * can handle the partial move.
+                        */
+                       if (split_huge_pmd(pmc->old, old_pmd, pmc->old_addr))
+                               break;
                } else if (IS_ENABLED(CONFIG_HAVE_MOVE_PMD) &&
                           extent == PMD_SIZE) {
                        /*
-- 
2.47.3


Reply via email to