FAILED: patch "[PATCH] mm/memory-failure: fix hugetlb_lock AA deadlock in" failed to apply to 6.12-stable tree
Subsystems (5)
[email protected]
maintainerThe patch below does not apply to the 6.12-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.12.y
git checkout FETCH_HEAD
git cherry-pick -x 3c2d42b8ee345b17a4ba56b0f6492d1ff4c1178e
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026061513-catalog-deviant-3f62@gregkh' --subject-prefix 'PATCH 6.12.y' HEAD^..
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 3c2d42b8ee345b17a4ba56b0f6492d1ff4c1178e Mon Sep 17 00:00:00 2001
From: Wupeng Ma <[email protected]>
Date: Fri, 22 May 2026 09:03:05 +0800
Subject: [PATCH] mm/memory-failure: fix hugetlb_lock AA deadlock in
get_huge_page_for_hwpoison
Two concurrent madvise(MADV_HWPOISON) calls on the same hugetlb page can trigger a recursive spinlock self-deadlock (AA deadlock) on hugetlb_lock when racing with a concurrent unmap:
thread#0 thread#1
-------- --------
madvise(folio, MADV_HWPOISON)
-> poisons the folio successfully
madvise(folio, MADV_HWPOISON) unmap(folio)
try_memory_failure_hugetlb
get_huge_page_for_hwpoison
spin_lock_irq(&hugetlb_lock) <- held
__get_huge_page_for_hwpoison
hugetlb_update_hwpoison()
-> MF_HUGETLB_FOLIO_PRE_POISONED
goto out:
folio_put()
refcount: 1 -> 0
free_huge_folio()
spin_lock_irqsave(&hugetlb_lock)
-> AA DEADLOCK!The out: path in __get_huge_page_for_hwpoison() calls folio_put() to drop the GUP reference while the hugetlb_lock is still held by the hugetlb.c wrapper get_huge_page_for_hwpoison(). If concurrent unmap has released the page table mapping reference, folio_put() drops the folio refcount to zero, triggering free_huge_folio() which attempts to re-acquire the non-recursive hugetlb_lock.
Fix this by moving hugetlb_lock acquisition from the hugetlb.c wrapper into get_huge_page_for_hwpoison(). Place spin_unlock_irq() before the folio_put() at the out: label so the folio is always released outside the lock.
include/linux/hugetlb.h +0 -8
@@ -153,8 +153,6 @@ long hugetlb_unreserve_pages(struct inode *inode, long start, long end, long freed);bool folio_isolate_hugetlb (struct folio *folio,struct list_head *list);int get_hwpoison_hugetlb_folio (struct folio *folio,bool *hugetlb,bool unpoison); -int get_huge_page_for_hwpoison (unsigned long pfn,int flags, -bool *migratable_cleared);void folio_putback_hugetlb (struct folio *folio);void move_hugetlb_state (struct folio *old_folio,struct folio *new_folio,int reason);void hugetlb_fix_reserve_counts (struct inode *inode);
@@ -421,12 +419,6 @@ static inline int get_hwpoison_hugetlb_folio(struct folio *folio, bool *hugetlb,return 0 ; } -static inline int get_huge_page_for_hwpoison (unsigned long pfn,int flags, -bool *migratable_cleared) -{ -return 0 ; -} -static inline void folio_putback_hugetlb (struct folio *folio) { }
include/linux/mm.h +0 -8
@@ -4975,8 +4975,6 @@ extern int soft_offline_page(unsigned long pfn, int flags);*/ extern const struct attribute_group memory_failure_attr_group;extern void memory_failure_queue (unsigned long pfn,int flags); -extern int __get_huge_page_for_hwpoison (unsigned long pfn,int flags, -bool *migratable_cleared);void num_poisoned_pages_inc (unsigned long pfn);void num_poisoned_pages_sub (unsigned long pfn,long i);#else
@@ -4984,12 +4982,6 @@ static inline void memory_failure_queue(unsigned long pfn, int flags) { } -static inline int __get_huge_page_for_hwpoison (unsigned long pfn,int flags, -bool *migratable_cleared) -{ -return 0 ; -} -static inline void num_poisoned_pages_inc (unsigned long pfn) { }
mm/hugetlb.c +0 -11
@@ -7161,17 +7161,6 @@ int get_hwpoison_hugetlb_folio(struct folio *folio, bool *hugetlb, bool unpoisonreturn ret; } -int get_huge_page_for_hwpoison (unsigned long pfn,int flags, -bool *migratable_cleared) -{ -int ret; - -spin_lock_irq (&hugetlb_lock); - ret =__get_huge_page_for_hwpoison (pfn, flags, migratable_cleared); -spin_unlock_irq (&hugetlb_lock); -return ret; -} -/** * folio_putback_hugetlb - unisolate a hugetlb folio * @folio: the isolated hugetlb folio
mm/memory-failure.c +10 -9
@@ -1966,20 +1966,19 @@ void folio_clear_hugetlb_hwpoison(struct folio *folio)folio_free_raw_hwp (folio, true); } -/* -* Called from hugetlb code with hugetlb_lock held. -*/ -int __get_huge_page_for_hwpoison (unsigned long pfn,int flags, +static int get_huge_page_for_hwpoison (unsigned long pfn,int flags,bool *migratable_cleared) {struct page *page =pfn_to_page (pfn); -struct folio *folio =page_folio (page); +struct folio *folio;bool count_increased = false;int ret, rc; +spin_lock_irq (&hugetlb_lock); + folio =page_folio (page);if (!folio_test_hugetlb (folio)) { ret =MF_HUGETLB_NON_HUGEPAGE ; - goto out; + goto out_unlock; }else if (flags &MF_COUNT_INCREASED ) { ret =MF_HUGETLB_IN_USED ; count_increased = true;
@@ -1995,13 +1994,13 @@ int __get_huge_page_for_hwpoison(unsigned long pfn, int flags, }else { ret =MF_HUGETLB_RETRY ;if (!(flags &MF_NO_RETRY )) - goto out; + goto out_unlock; } rc =hugetlb_update_hwpoison (folio, page);if (rc >=MF_HUGETLB_FOLIO_PRE_POISONED ) { ret = rc; - goto out; + goto out_unlock; }/*
@@ -2013,8 +2012,10 @@ int __get_huge_page_for_hwpoison(unsigned long pfn, int flags, *migratable_cleared = true; } +spin_unlock_irq (&hugetlb_lock);return ret; -out: +out_unlock: +spin_unlock_irq (&hugetlb_lock);if (count_increased)folio_put (folio);return ret;
Sasha Levin
maintainerPATCH 1/1 mm/memory-failure: fix hugetlb_lock AA deadlock in get_huge_page_for_hwpoison
From: Wupeng Ma <[email protected]>
[ Upstream commit 3c2d42b8ee345b17a4ba56b0f6492d1ff4c1178e ]
Two concurrent madvise(MADV_HWPOISON) calls on the same hugetlb page can trigger a recursive spinlock self-deadlock (AA deadlock) on hugetlb_lock when racing with a concurrent unmap:
thread#0 thread#1
-------- --------
madvise(folio, MADV_HWPOISON)
-> poisons the folio successfully
madvise(folio, MADV_HWPOISON) unmap(folio)
try_memory_failure_hugetlb
get_huge_page_for_hwpoison
spin_lock_irq(&hugetlb_lock) <- held
__get_huge_page_for_hwpoison
hugetlb_update_hwpoison()
-> MF_HUGETLB_FOLIO_PRE_POISONED
goto out:
folio_put()
refcount: 1 -> 0
free_huge_folio()
spin_lock_irqsave(&hugetlb_lock)
-> AA DEADLOCK!The out: path in __get_huge_page_for_hwpoison() calls folio_put() to drop the GUP reference while the hugetlb_lock is still held by the hugetlb.c wrapper get_huge_page_for_hwpoison(). If concurrent unmap has released the page table mapping reference, folio_put() drops the folio refcount to zero, triggering free_huge_folio() which attempts to re-acquire the non-recursive hugetlb_lock.
Fix this by moving hugetlb_lock acquisition from the hugetlb.c wrapper into get_huge_page_for_hwpoison(). Place spin_unlock_irq() before the folio_put() at the out: label so the folio is always released outside the lock.
--- include/linux/hugetlb.h | 8 -------- include/linux/mm.h | 8 -------- mm/hugetlb.c | 11 ----------- mm/memory-failure.c | 19 ++++++++++--------- 4 files changed, 10 insertions(+), 36 deletions(-)
include/linux/hugetlb.h +0 -8
@@ -155,8 +155,6 @@ long hugetlb_unreserve_pages(struct inode *inode, long start, long end, long freed);bool isolate_hugetlb (struct folio *folio,struct list_head *list);int get_hwpoison_hugetlb_folio (struct folio *folio,bool *hugetlb,bool unpoison); -int get_huge_page_for_hwpoison (unsigned long pfn,int flags, -bool *migratable_cleared);void folio_putback_active_hugetlb (struct folio *folio);void move_hugetlb_state (struct folio *old_folio,struct folio *new_folio,int reason);void hugetlb_fix_reserve_counts (struct inode *inode);
@@ -430,12 +428,6 @@ static inline int get_hwpoison_hugetlb_folio(struct folio *folio, bool *hugetlb,return 0 ; } -static inline int get_huge_page_for_hwpoison (unsigned long pfn,int flags, -bool *migratable_cleared) -{ -return 0 ; -} -static inline void folio_putback_active_hugetlb (struct folio *folio) { }
include/linux/mm.h +0 -8
@@ -3995,8 +3995,6 @@ extern int soft_offline_page(unsigned long pfn, int flags);*/ extern const struct attribute_group memory_failure_attr_group;extern void memory_failure_queue (unsigned long pfn,int flags); -extern int __get_huge_page_for_hwpoison (unsigned long pfn,int flags, -bool *migratable_cleared);void num_poisoned_pages_inc (unsigned long pfn);void num_poisoned_pages_sub (unsigned long pfn,long i);#else
@@ -4004,12 +4002,6 @@ static inline void memory_failure_queue(unsigned long pfn, int flags) { } -static inline int __get_huge_page_for_hwpoison (unsigned long pfn,int flags, -bool *migratable_cleared) -{ -return 0 ; -} -static inline void num_poisoned_pages_inc (unsigned long pfn) { }
mm/hugetlb.c +0 -11
@@ -7457,17 +7457,6 @@ int get_hwpoison_hugetlb_folio(struct folio *folio, bool *hugetlb, bool unpoisonreturn ret; } -int get_huge_page_for_hwpoison (unsigned long pfn,int flags, -bool *migratable_cleared) -{ -int ret; - -spin_lock_irq (&hugetlb_lock); - ret =__get_huge_page_for_hwpoison (pfn, flags, migratable_cleared); -spin_unlock_irq (&hugetlb_lock); -return ret; -} -void folio_putback_active_hugetlb (struct folio *folio) {spin_lock_irq (&hugetlb_lock);
mm/memory-failure.c +10 -9
@@ -2020,20 +2020,19 @@ void folio_clear_hugetlb_hwpoison(struct folio *folio)folio_free_raw_hwp (folio, true); } -/* -* Called from hugetlb code with hugetlb_lock held. -*/ -int __get_huge_page_for_hwpoison (unsigned long pfn,int flags, +static int get_huge_page_for_hwpoison (unsigned long pfn,int flags,bool *migratable_cleared) {struct page *page =pfn_to_page (pfn); -struct folio *folio =page_folio (page); +struct folio *folio;bool count_increased = false;int ret, rc; +spin_lock_irq (&hugetlb_lock); + folio =page_folio (page);if (!folio_test_hugetlb (folio)) { ret =MF_HUGETLB_NON_HUGEPAGE ; - goto out; + goto out_unlock; }else if (flags &MF_COUNT_INCREASED ) { ret =MF_HUGETLB_IN_USED ; count_increased = true;
@@ -2049,13 +2048,13 @@ int __get_huge_page_for_hwpoison(unsigned long pfn, int flags, }else { ret =MF_HUGETLB_RETRY ;if (!(flags &MF_NO_RETRY )) - goto out; + goto out_unlock; } rc =hugetlb_update_hwpoison (folio, page);if (rc >=MF_HUGETLB_FOLIO_PRE_POISONED ) { ret = rc; - goto out; + goto out_unlock; }/*
@@ -2067,8 +2066,10 @@ int __get_huge_page_for_hwpoison(unsigned long pfn, int flags, *migratable_cleared = true; } +spin_unlock_irq (&hugetlb_lock);return ret; -out: +out_unlock: +spin_unlock_irq (&hugetlb_lock);if (count_increased)folio_put (folio);return ret;
Signature · 1 line
2.53.0