Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mm/memory-failure: fix missing ->mf_stats count in hugetlb poison

When a newly poisoned subpage ends up in an already poisoned hugetlb
folio, 'num_poisoned_pages' is incremented, but the per node ->mf_stats is
not. Fix the inconsistency by designating action_result() to update them
both.

While at it, define __get_huge_page_for_hwpoison() return values in terms
of symbol names for better readibility. Also rename
folio_set_hugetlb_hwpoison() to hugetlb_update_hwpoison() since the
function does more than the conventional bit setting and the fact three
possible return values are expected.

Link: https://lkml.kernel.org/r/20260120232234.3462258-1-jane.chu@oracle.com
Fixes: 18f41fa616ee ("mm: memory-failure: bump memory failure stats to pglist_data")
Signed-off-by: Jane Chu <jane.chu@oracle.com>
Acked-by: Miaohe Lin <linmiaohe@huawei.com>
Cc: Chris Mason <clm@meta.com>
Cc: David Hildenbrand <david@kernel.org>
Cc: David Rientjes <rientjes@google.com>
Cc: Jiaqi Yan <jiaqiyan@google.com>
Cc: Liam R. Howlett <Liam.Howlett@oracle.com>
Cc: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Muchun Song <muchun.song@linux.dev>
Cc: Naoya Horiguchi <nao.horiguchi@gmail.com>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: William Roche <william.roche@oracle.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Jane Chu and committed by
Andrew Morton
a148a204 a0f3c084

+56 -37
+56 -37
mm/memory-failure.c
··· 1883 return count; 1884 } 1885 1886 - static int folio_set_hugetlb_hwpoison(struct folio *folio, struct page *page) 1887 { 1888 struct llist_head *head; 1889 struct raw_hwp_page *raw_hwp; 1890 struct raw_hwp_page *p; 1891 - int ret = folio_test_set_hwpoison(folio) ? -EHWPOISON : 0; 1892 1893 /* 1894 * Once the hwpoison hugepage has lost reliable raw error info, ··· 1906 * so skip to add additional raw error info. 1907 */ 1908 if (folio_test_hugetlb_raw_hwp_unreliable(folio)) 1909 - return -EHWPOISON; 1910 head = raw_hwp_list_head(folio); 1911 llist_for_each_entry(p, head->first, node) { 1912 if (p->page == page) 1913 - return -EHWPOISON; 1914 } 1915 1916 raw_hwp = kmalloc(sizeof(struct raw_hwp_page), GFP_ATOMIC); 1917 if (raw_hwp) { 1918 raw_hwp->page = page; 1919 llist_add(&raw_hwp->node, head); 1920 - /* the first error event will be counted in action_result(). */ 1921 - if (ret) 1922 - num_poisoned_pages_inc(page_to_pfn(page)); 1923 } else { 1924 /* 1925 * Failed to save raw error info. We no longer trace all ··· 1964 1965 /* 1966 * Called from hugetlb code with hugetlb_lock held. 1967 - * 1968 - * Return values: 1969 - * 0 - free hugepage 1970 - * 1 - in-use hugepage 1971 - * 2 - not a hugepage 1972 - * -EBUSY - the hugepage is busy (try to retry) 1973 - * -EHWPOISON - the hugepage is already hwpoisoned 1974 */ 1975 int __get_huge_page_for_hwpoison(unsigned long pfn, int flags, 1976 bool *migratable_cleared) 1977 { 1978 struct page *page = pfn_to_page(pfn); 1979 struct folio *folio = page_folio(page); 1980 - int ret = 2; /* fallback to normal page handling */ 1981 bool count_increased = false; 1982 1983 - if (!folio_test_hugetlb(folio)) 1984 goto out; 1985 - 1986 - if (flags & MF_COUNT_INCREASED) { 1987 - ret = 1; 1988 count_increased = true; 1989 } else if (folio_test_hugetlb_freed(folio)) { 1990 - ret = 0; 1991 } else if (folio_test_hugetlb_migratable(folio)) { 1992 - ret = folio_try_get(folio); 1993 - if (ret) 1994 count_increased = true; 1995 } else { 1996 - ret = -EBUSY; 1997 if (!(flags & MF_NO_RETRY)) 1998 goto out; 1999 } 2000 2001 - if (folio_set_hugetlb_hwpoison(folio, page)) { 2002 - ret = -EHWPOISON; 2003 goto out; 2004 } 2005 ··· 2021 * with basic operations like hugepage allocation/free/demotion. 2022 * So some of prechecks for hwpoison (pinning, and testing/setting 2023 * PageHWPoison) should be done in single hugetlb_lock range. 2024 */ 2025 static int try_memory_failure_hugetlb(unsigned long pfn, int flags, int *hugetlb) 2026 { 2027 - int res; 2028 struct page *p = pfn_to_page(pfn); 2029 struct folio *folio; 2030 unsigned long page_flags; ··· 2039 *hugetlb = 1; 2040 retry: 2041 res = get_huge_page_for_hwpoison(pfn, flags, &migratable_cleared); 2042 - if (res == 2) { /* fallback to normal page handling */ 2043 *hugetlb = 0; 2044 return 0; 2045 - } else if (res == -EHWPOISON) { 2046 - if (flags & MF_ACTION_REQUIRED) { 2047 - folio = page_folio(p); 2048 - res = kill_accessing_process(current, folio_pfn(folio), flags); 2049 - } 2050 - action_result(pfn, MF_MSG_ALREADY_POISONED, MF_FAILED); 2051 - return res; 2052 - } else if (res == -EBUSY) { 2053 if (!(flags & MF_NO_RETRY)) { 2054 flags |= MF_NO_RETRY; 2055 goto retry; 2056 } 2057 return action_result(pfn, MF_MSG_GET_HWPOISON, MF_IGNORED); 2058 } 2059 2060 folio = page_folio(p); ··· 2074 if (migratable_cleared) 2075 folio_set_hugetlb_migratable(folio); 2076 folio_unlock(folio); 2077 - if (res == 1) 2078 folio_put(folio); 2079 return -EOPNOTSUPP; 2080 } ··· 2083 * Handling free hugepage. The possible race with hugepage allocation 2084 * or demotion can be prevented by PageHWPoison flag. 2085 */ 2086 - if (res == 0) { 2087 folio_unlock(folio); 2088 if (__page_handle_poison(p) > 0) { 2089 page_ref_inc(p);
··· 1883 return count; 1884 } 1885 1886 + #define MF_HUGETLB_FREED 0 /* freed hugepage */ 1887 + #define MF_HUGETLB_IN_USED 1 /* in-use hugepage */ 1888 + #define MF_HUGETLB_NON_HUGEPAGE 2 /* not a hugepage */ 1889 + #define MF_HUGETLB_FOLIO_PRE_POISONED 3 /* folio already poisoned */ 1890 + #define MF_HUGETLB_PAGE_PRE_POISONED 4 /* exact page already poisoned */ 1891 + #define MF_HUGETLB_RETRY 5 /* hugepage is busy, retry */ 1892 + /* 1893 + * Set hugetlb folio as hwpoisoned, update folio private raw hwpoison list 1894 + * to keep track of the poisoned pages. 1895 + */ 1896 + static int hugetlb_update_hwpoison(struct folio *folio, struct page *page) 1897 { 1898 struct llist_head *head; 1899 struct raw_hwp_page *raw_hwp; 1900 struct raw_hwp_page *p; 1901 + int ret = folio_test_set_hwpoison(folio) ? MF_HUGETLB_FOLIO_PRE_POISONED : 0; 1902 1903 /* 1904 * Once the hwpoison hugepage has lost reliable raw error info, ··· 1896 * so skip to add additional raw error info. 1897 */ 1898 if (folio_test_hugetlb_raw_hwp_unreliable(folio)) 1899 + return MF_HUGETLB_FOLIO_PRE_POISONED; 1900 head = raw_hwp_list_head(folio); 1901 llist_for_each_entry(p, head->first, node) { 1902 if (p->page == page) 1903 + return MF_HUGETLB_PAGE_PRE_POISONED; 1904 } 1905 1906 raw_hwp = kmalloc(sizeof(struct raw_hwp_page), GFP_ATOMIC); 1907 if (raw_hwp) { 1908 raw_hwp->page = page; 1909 llist_add(&raw_hwp->node, head); 1910 } else { 1911 /* 1912 * Failed to save raw error info. We no longer trace all ··· 1957 1958 /* 1959 * Called from hugetlb code with hugetlb_lock held. 1960 */ 1961 int __get_huge_page_for_hwpoison(unsigned long pfn, int flags, 1962 bool *migratable_cleared) 1963 { 1964 struct page *page = pfn_to_page(pfn); 1965 struct folio *folio = page_folio(page); 1966 bool count_increased = false; 1967 + int ret, rc; 1968 1969 + if (!folio_test_hugetlb(folio)) { 1970 + ret = MF_HUGETLB_NON_HUGEPAGE; 1971 goto out; 1972 + } else if (flags & MF_COUNT_INCREASED) { 1973 + ret = MF_HUGETLB_IN_USED; 1974 count_increased = true; 1975 } else if (folio_test_hugetlb_freed(folio)) { 1976 + ret = MF_HUGETLB_FREED; 1977 } else if (folio_test_hugetlb_migratable(folio)) { 1978 + if (folio_try_get(folio)) { 1979 + ret = MF_HUGETLB_IN_USED; 1980 count_increased = true; 1981 + } else { 1982 + ret = MF_HUGETLB_FREED; 1983 + } 1984 } else { 1985 + ret = MF_HUGETLB_RETRY; 1986 if (!(flags & MF_NO_RETRY)) 1987 goto out; 1988 } 1989 1990 + rc = hugetlb_update_hwpoison(folio, page); 1991 + if (rc >= MF_HUGETLB_FOLIO_PRE_POISONED) { 1992 + ret = rc; 1993 goto out; 1994 } 1995 ··· 2017 * with basic operations like hugepage allocation/free/demotion. 2018 * So some of prechecks for hwpoison (pinning, and testing/setting 2019 * PageHWPoison) should be done in single hugetlb_lock range. 2020 + * Returns: 2021 + * 0 - not hugetlb, or recovered 2022 + * -EBUSY - not recovered 2023 + * -EOPNOTSUPP - hwpoison_filter'ed 2024 + * -EHWPOISON - folio or exact page already poisoned 2025 + * -EFAULT - kill_accessing_process finds current->mm null 2026 */ 2027 static int try_memory_failure_hugetlb(unsigned long pfn, int flags, int *hugetlb) 2028 { 2029 + int res, rv; 2030 struct page *p = pfn_to_page(pfn); 2031 struct folio *folio; 2032 unsigned long page_flags; ··· 2029 *hugetlb = 1; 2030 retry: 2031 res = get_huge_page_for_hwpoison(pfn, flags, &migratable_cleared); 2032 + switch (res) { 2033 + case MF_HUGETLB_NON_HUGEPAGE: /* fallback to normal page handling */ 2034 *hugetlb = 0; 2035 return 0; 2036 + case MF_HUGETLB_RETRY: 2037 if (!(flags & MF_NO_RETRY)) { 2038 flags |= MF_NO_RETRY; 2039 goto retry; 2040 } 2041 return action_result(pfn, MF_MSG_GET_HWPOISON, MF_IGNORED); 2042 + case MF_HUGETLB_FOLIO_PRE_POISONED: 2043 + case MF_HUGETLB_PAGE_PRE_POISONED: 2044 + rv = -EHWPOISON; 2045 + if (flags & MF_ACTION_REQUIRED) { 2046 + folio = page_folio(p); 2047 + rv = kill_accessing_process(current, folio_pfn(folio), flags); 2048 + } 2049 + if (res == MF_HUGETLB_PAGE_PRE_POISONED) 2050 + action_result(pfn, MF_MSG_ALREADY_POISONED, MF_FAILED); 2051 + else 2052 + action_result(pfn, MF_MSG_HUGE, MF_FAILED); 2053 + return rv; 2054 + default: 2055 + WARN_ON((res != MF_HUGETLB_FREED) && (res != MF_HUGETLB_IN_USED)); 2056 + break; 2057 } 2058 2059 folio = page_folio(p); ··· 2055 if (migratable_cleared) 2056 folio_set_hugetlb_migratable(folio); 2057 folio_unlock(folio); 2058 + if (res == MF_HUGETLB_IN_USED) 2059 folio_put(folio); 2060 return -EOPNOTSUPP; 2061 } ··· 2064 * Handling free hugepage. The possible race with hugepage allocation 2065 * or demotion can be prevented by PageHWPoison flag. 2066 */ 2067 + if (res == MF_HUGETLB_FREED) { 2068 folio_unlock(folio); 2069 if (__page_handle_poison(p) > 0) { 2070 page_ref_inc(p);