[PATCH v4] kexec: keep the next kernel off hardware-poisoned pages

Kiryl Shutsemau kas at kernel.org
Mon Aug 10 02:49:16 PDT 2026


On Fri, Aug 07, 2026 at 07:05:09AM -0700, Breno Leitao wrote:
> diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c
> index dc770b9a6d053..e097e980b1439 100644
> --- a/kernel/kexec_core.c
> +++ b/kernel/kexec_core.c
> @@ -212,6 +212,16 @@ int sanity_check_segment_list(struct kimage *image)
>  	}
>  #endif
>  
> +	/*
> +	 * Reject destinations that land on hardware-poisoned memory: the
> +	 * relocation copy would machine-check on the bad frame.
> +	 */
> +	for (i = 0; i < nr_segments; i++) {
> +		if (range_first_hwpoison(image->segment[i].mem,
> +					 image->segment[i].memsz) != PHYS_ADDR_MAX)
> +			return -EADDRNOTAVAIL;

Other -EADDRNOTAVAIL usage indicate error on user side. But this is not
a user fault. Maybe -EHWPOISON instead.

> +	}
> +
>  	/*
>  	 * The destination addresses are searched from system RAM rather than
>  	 * being allocated from the buddy allocator, so they are not guaranteed

...

> diff --git a/mm/memory-failure.c b/mm/memory-failure.c
> index a8b03e2920ba8..c485e205fb633 100644
> --- a/mm/memory-failure.c
> +++ b/mm/memory-failure.c
> @@ -96,6 +96,78 @@ void num_poisoned_pages_sub(unsigned long pfn, long i)
>  		memblk_nr_poison_sub(pfn, i);
>  }
>  
> +/*
> + * Return the first or the last hardware-poisoned online page in [start,
> + * start + size), or PHYS_ADDR_MAX if the range is clean.
> + */
> +static phys_addr_t range_hwpoison(phys_addr_t start, unsigned long size,
> +				  bool first)
> +{
> +	phys_addr_t poison = PHYS_ADDR_MAX;
> +	unsigned long pfn, end_pfn;
> +
> +	if (!size || !atomic_long_read(&num_poisoned_pages))
> +		return poison;
> +
> +	end_pfn = PHYS_PFN(start + size - 1);
> +	for (pfn = PHYS_PFN(start); pfn <= end_pfn; pfn++) {
> +		struct page *page = pfn_to_online_page(pfn);
> +		struct folio *folio;
> +
> +		cond_resched();
> +
> +		if (!page)
> +			continue;
> +
> +		folio = page_folio(page);
> +		if (folio_test_hugetlb(folio)) {
> +			/*
> +			 * hugetlbfs is a bit special, given the poison
> +			 * information is at the folio, not at the page
> +			 */
> +			unsigned long folio_end;
> +
> +			/*
> +			 * No hugetlb_lock: the scan is racy either way, a frame
> +			 * can be poisoned right after it. Just don't let a folio
> +			 * dissolved under us walk the scan backwards.
> +			 */
> +			folio_end = folio_pfn(folio) + folio_nr_pages(folio) - 1;
> +			folio_end = max(folio_end, pfn);
> +
> +			if (folio_test_hwpoison(folio)) {
> +				if (first)
> +					return PFN_PHYS(pfn);
> +				poison = PFN_PHYS(min(folio_end, end_pfn));
> +			}
> +			/* skip all the pfns that belong to hugetlb */
> +			pfn = folio_end;
> +			continue;
> +		}
> +
> +		if (!PageHWPoison(page))
> +			/* page is good, let's go to the next one */
> +			continue;

If you don't care about re-using clean part of poisoned hugetlb folio,
use is_page_hwpoison(page).

This would do:

		if (!page || !is_page_hwpoison(page))
			continue;

You would spin a bit on the same folio, but shouldn't be a big deal.

> +
> +		if (first)
> +			return PFN_PHYS(pfn);
> +
> +		poison = PFN_PHYS(pfn);
> +	}
> +
> +	return poison;
> +}
-- 
  Kiryl Shutsemau / Kirill A. Shutemov



More information about the kexec mailing list