From: Anshuman Khandual anshuman.khandual@arm.com
[ Upstream commit e6112fc300702f96374f34368513d57795fc6d23 ]
split_huge_page_to_list() fails on HugeTLB pages. I was experimenting with moving 32MB contig HugeTLB pages on arm64 (with a debug patch applied) and hit the following stack trace when the kernel crashed.
[ 3732.462797] Call trace: [ 3732.462835] split_huge_page_to_list+0x3b0/0x858 [ 3732.462913] migrate_pages+0x728/0xc20 [ 3732.462999] soft_offline_page+0x448/0x8b0 [ 3732.463097] __arm64_sys_madvise+0x724/0x850 [ 3732.463197] el0_svc_handler+0x74/0x110 [ 3732.463297] el0_svc+0x8/0xc [ 3732.463347] Code: d1000400 f90b0e60 f2fbd5a2 a94982a1 (f9000420)
When unmap_and_move[_huge_page]() fails due to lack of memory, the splitting should happen only for transparent huge pages not for HugeTLB pages. PageTransHuge() returns true for both THP and HugeTLB pages. Hence the conditonal check should test PagesHuge() flag to make sure that given pages is not a HugeTLB one.
Link: http://lkml.kernel.org/r/1537798495-4996-1-git-send-email-anshuman.khandual@... Fixes: 94723aafb9 ("mm: unclutter THP migration") Signed-off-by: Anshuman Khandual anshuman.khandual@arm.com Acked-by: Michal Hocko mhocko@suse.com Acked-by: Naoya Horiguchi n-horiguchi@ah.jp.nec.com Cc: Kirill A. Shutemov kirill.shutemov@linux.intel.com Cc: Zi Yan zi.yan@cs.rutgers.edu Cc: Mike Kravetz mike.kravetz@oracle.com Cc: Vlastimil Babka vbabka@suse.cz Signed-off-by: Andrew Morton akpm@linux-foundation.org Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Signed-off-by: Sasha Levin sashal@kernel.org --- mm/migrate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mm/migrate.c b/mm/migrate.c index 2a55289ee9f1..f49eb9589d73 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -1415,7 +1415,7 @@ int migrate_pages(struct list_head *from, new_page_t get_new_page, * we encounter them after the rest of the list * is processed. */ - if (PageTransHuge(page)) { + if (PageTransHuge(page) && !PageHuge(page)) { lock_page(page); rc = split_huge_page_to_list(page, from); unlock_page(page);