FAILED: patch "[PATCH] mm/hwpoison: fix race between hugetlb free/demotion and" failed to apply to 5.10-stable tree
Subsystems (5)
[email protected]
maintainerThe patch below does not apply to the 5.10-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]>.
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 405ce051236cc65b30bbfe490b28ce60ae6aed85 Mon Sep 17 00:00:00 2001
From: Naoya Horiguchi <[email protected]>
Date: Thu, 21 Apr 2022 16:35:33 -0700
Subject: [PATCH] mm/hwpoison: fix race between hugetlb free/demotion and
memory_failure_hugetlb()
There is a race condition between memory_failure_hugetlb() and hugetlb free/demotion, which causes setting PageHWPoison flag on the wrong page. The one simple result is that wrong processes can be killed, but another (more serious) one is that the actual error is left unhandled, so no one prevents later access to it, and that might lead to more serious results like consuming corrupted data.
Think about the below race window:
CPU 1 CPU 2
memory_failure_hugetlb
struct page *head = compound_head(p);
hugetlb page might be freed to buddy, or even changed to another compound page.
get_hwpoison_page -- page is not what we want now...
The current code first does prechecks roughly and then reconfirms after taking refcount, but it's found that it makes code overly complicated, so move the prechecks in a single hugetlb_lock range.
A newly introduced function, try_memory_failure_hugetlb(), always takes hugetlb_lock (even for non-hugetlb pages). That can be improved, but memory_failure() is rare in principle, so should not be a big problem.
include/linux/hugetlb.h +6 -0
@@ -169,6 +169,7 @@ long hugetlb_unreserve_pages(struct inode *inode, long start, long end, long freed);bool isolate_huge_page (struct page *page,struct list_head *list);int get_hwpoison_huge_page (struct page *page,bool *hugetlb); +int get_huge_page_for_hwpoison (unsigned long pfn,int flags);void putback_active_hugepage (struct page *page);void move_hugetlb_state (struct page *oldpage,struct page *newpage,int reason);void free_huge_page (struct page *page);
@@ -378,6 +379,11 @@ static inline int get_hwpoison_huge_page(struct page *page, bool *hugetlb)return 0 ; } +static inline int get_huge_page_for_hwpoison (unsigned long pfn,int flags) +{ +return 0 ; +} +static inline void putback_active_hugepage (struct page *page) { }
include/linux/mm.h +8 -0
@@ -3197,6 +3197,14 @@ extern int sysctl_memory_failure_recovery;extern void shake_page (struct page *p);extern atomic_long_t num_poisoned_pages__read_mostly ;extern int soft_offline_page (unsigned long pfn,int flags); +#ifdef CONFIG_MEMORY_FAILURE +extern int __get_huge_page_for_hwpoison (unsigned long pfn,int flags); +#else +static inline int __get_huge_page_for_hwpoison (unsigned long pfn,int flags) +{ +return 0 ; +} +#endif #ifndef arch_memory_failurestatic inline int arch_memory_failure (unsigned long pfn,int flags)
mm/hugetlb.c +10 -0
@@ -6785,6 +6785,16 @@ int get_hwpoison_huge_page(struct page *page, bool *hugetlb)return ret; } +int get_huge_page_for_hwpoison (unsigned long pfn,int flags) +{ +int ret; + +spin_lock_irq (&hugetlb_lock); + ret =__get_huge_page_for_hwpoison (pfn, flags); +spin_unlock_irq (&hugetlb_lock); +return ret; +} +void putback_active_hugepage (struct page *page) {spin_lock_irq (&hugetlb_lock);
mm/memory-failure.c +103 -42
189 lines not on this page · raw message