From: Wupeng Ma <mawupeng1@huawei.com> hulk inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/8489 -------------------------------- When encountering memory faults during page migration on arm64 systems, this change ensures the faulting user process is terminated instead of causing a kernel panic. The implementation adds proper error handling for copy operations in migrate_page(). To enable this, bit 1 for uce_kernel_recovery should be enabled: - echo 1 > /proc/sys/kernel/uce_kernel_recovery Signed-off-by: Wupeng Ma <mawupeng1@huawei.com> Signed-off-by: Qi Xi <xiqi2@huawei.com> --- mm/memory-failure.c | 7 ++++-- mm/migrate.c | 58 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/mm/memory-failure.c b/mm/memory-failure.c index ad416016d1e9..908be8e32823 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -60,6 +60,7 @@ #include <linux/ratelimit.h> #include <linux/page-isolation.h> #include <linux/shmem_fs.h> +#include <linux/sched.h> #include "internal.h" #include "ras/ras_event.h" @@ -2050,9 +2051,11 @@ int soft_offline_page(struct page *page, int flags) ret = get_any_page(page, pfn, flags); put_online_mems(); - if (ret > 0) + if (ret > 0) { + current->flags |= PF_UCE_KERNEL_RECOVERY; ret = soft_offline_in_use_page(page); - else if (ret == 0) + current->flags &= ~PF_UCE_KERNEL_RECOVERY; + } else if (ret == 0) if (soft_offline_free_page(page) && try_again) { try_again = false; flags &= ~MF_COUNT_INCREASED; diff --git a/mm/migrate.c b/mm/migrate.c index f8c379a0b9b9..51ae1c0018da 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -47,6 +47,7 @@ #include <linux/page_owner.h> #include <linux/sched/mm.h> #include <linux/ptrace.h> +#include <linux/highmem.h> #include <asm/tlbflush.h> @@ -657,7 +658,7 @@ static void __copy_gigantic_page(struct page *dst, struct page *src, } } -static void copy_huge_page(struct page *dst, struct page *src) +static int __copy_huge_page(struct page *dst, struct page *src, bool mc) { int i; int nr_pages; @@ -669,7 +670,7 @@ static void copy_huge_page(struct page *dst, struct page *src) if (unlikely(nr_pages > MAX_ORDER_NR_PAGES)) { __copy_gigantic_page(dst, src, nr_pages); - return; + return 0; } } else { /* thp page */ @@ -677,10 +678,24 @@ static void copy_huge_page(struct page *dst, struct page *src) nr_pages = hpage_nr_pages(src); } + if (mc) + return copy_mc_highpages(dst, src, nr_pages); + for (i = 0; i < nr_pages; i++) { cond_resched(); copy_highpage(dst + i, src + i); } + return 0; +} + +static int copy_huge_page(struct page *dst, struct page *src) +{ + return __copy_huge_page(dst, src, false); +} + +static int copy_mc_huge_page(struct page *dst, struct page *src) +{ + return __copy_huge_page(dst, src, true); } /* @@ -756,6 +771,38 @@ void migrate_page_copy(struct page *newpage, struct page *page) } EXPORT_SYMBOL(migrate_page_copy); +static int migrate_page_copy_mc(struct page *newpage, struct page *page) +{ + int rc; + + if (PageHuge(page) || PageTransHuge(page)) + rc = copy_mc_huge_page(newpage, page); + else + rc = copy_mc_highpage(newpage, page); + + return rc; +} + +static int migrate_page_mc_extra(struct address_space *mapping, + struct page *newpage, struct page *page, + enum migrate_mode mode, int extra_count) +{ + int rc; + + rc = migrate_page_copy_mc(newpage, page); + if (rc) + return rc; + + rc = migrate_page_move_mapping(mapping, newpage, page, NULL, mode, + extra_count); + if (rc != MIGRATEPAGE_SUCCESS) + return rc; + + migrate_page_states(newpage, page); + + return rc; +} + /************************************************************ * Migration functions ***********************************************************/ @@ -774,6 +821,13 @@ int migrate_page(struct address_space *mapping, BUG_ON(PageWriteback(page)); /* Writeback must be complete */ +#ifdef CONFIG_UCE_KERNEL_RECOVERY + if (is_cow_kernel_recovery_enable() && + (mode != MIGRATE_SYNC_NO_COPY) && + current->flags & PF_UCE_KERNEL_RECOVERY) + return migrate_page_mc_extra(mapping, newpage, page, mode, 0); +#endif + rc = migrate_page_move_mapping(mapping, newpage, page, NULL, mode, 0); if (rc != MIGRATEPAGE_SUCCESS) -- 2.33.0