Re: [PATCH v8 02/15] memory-failure: Prevent hugetlb freeing during unpoisoning

[email protected] Mon, 3 Aug 2026 23:40:25 -0700
Newsgroups gmane.linux.file-systems,gmane.linux.kernel.mm
Message-ID <[email protected]>

On 7/31/2026 1:07 PM, Matthew Wilcox (Oracle) wrote:
> If we fail to get a reference on the hugetlb folio, then it might be=20
> freed as we operate on it. Prevent the freeing and the attendant races=20
> around manipulation of the raw_hwp list by holding the hugetlb_lock,=20
> which is also held by the hugetlb
>=20
>=20
> If we fail to get a reference on the hugetlb folio, then it might be
> freed as we operate on it.  Prevent the freeing and the attendant races
> around manipulation of the raw_hwp list by holding the hugetlb_lock,
> which is also held by the hugetlb code when freeing hugetlb folios.
>=20
> Fixes: ac5fcde0a96a ("mm, hwpoison: make unpoison aware of raw error info=
 in hwpoisoned hugepage")
> Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
> Reviewed-by: Gregory Price (Meta) <[email protected]>
> ---
>   include/linux/hugetlb.h | 19 +++++++++++++++++++
>   mm/memory-failure.c     |  6 +++++-
>   2 files changed, 24 insertions(+), 1 deletion(-)
>=20
> diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
> index 2abaf99321e9..50eab2c23299 100644
> --- a/include/linux/hugetlb.h
> +++ b/include/linux/hugetlb.h
> @@ -110,6 +110,17 @@ extern struct resv_map *resv_map_alloc(void);
>   void resv_map_release(struct kref *ref);
>  =20
>   extern spinlock_t hugetlb_lock;
> +
> +static inline void hugetlb_lock_irq(void)
> +{
> +	spin_lock_irq(&hugetlb_lock);
> +}
> +
> +static inline void hugetlb_unlock_irq(void)
> +{
> +	spin_unlock_irq(&hugetlb_lock);
> +}
> +
>   extern int hugetlb_max_hstate __read_mostly;
>   #define for_each_hstate(h) \
>   	for ((h) =3D hstates; (h) < &hstates[hugetlb_max_hstate]; (h)++)
> @@ -279,6 +290,14 @@ unsigned int arch_hugetlb_cma_order(void);
>  =20
>   #else /* !CONFIG_HUGETLB_PAGE */
>  =20
> +static inline void hugetlb_lock_irq(void)
> +{
> +}
> +
> +static inline void hugetlb_unlock_irq(void)
> +{
> +}
> +
>   static inline void hugetlb_dup_vma_private(struct vm_area_struct *vma)
>   {
>   }
> diff --git a/mm/memory-failure.c b/mm/memory-failure.c
> index 944e6e1d4971..1dd0e7b99bb1 100644
> --- a/mm/memory-failure.c
> +++ b/mm/memory-failure.c
> @@ -2725,13 +2725,17 @@ int unpoison_memory(unsigned long pfn)
>  =20
>   	ghp =3D get_hwpoison_page(p, MF_UNPOISON);
>   	if (!ghp) {
> +		hugetlb_lock_irq();
>   		if (folio_test_hugetlb(folio)) {
>   			huge =3D true;
>   			count =3D folio_free_raw_hwp(folio, false);
> -			if (count =3D=3D 0)
> +			if (count =3D=3D 0) {
> +				hugetlb_unlock_irq();
>   				goto unlock_mutex;
> +			}
>   		}
>   		ret =3D folio_test_clear_hwpoison(folio) ? 0 : -EBUSY;
> +		hugetlb_unlock_irq();
>   	} else if (ghp < 0) {
>   		if (ghp =3D=3D -EHWPOISON) {
>   			ret =3D put_page_back_buddy(p) ? 0 : -EBUSY;
> --=20
> 2.47.3
>=20

Patch itself looks good, so Reviewed-by: Jane Chu <[email protected]>

That said, there is a pre-existing issue:
folio_free_raw_hwp() should check HPG_raw_hwp_unreliable, and fail the=20
act of unpoison just like what __update_and_free_hugetlb_folio() does -

static void __update_and_free_hugetlb_folio(struct hstate *h,
                                                 struct folio *folio)
{
         bool clear_flag =3D folio_test_hugetlb_vmemmap_optimized(folio);

         if (hstate_is_gigantic_no_runtime(h))
                 return;

         /*
          * If we don't know which subpages are hwpoisoned, we can't free
          * the hugepage, so it's leaked intentionally.
          */
         if (folio_test_hugetlb_raw_hwp_unreliable(folio))
                 return;


thanks,
-jane