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

Kiryl Shutsemau <[email protected]>
Newsgroups org.infradead.lists.kexec,org.kernel.vger.linux-kernel,org.kvack.linux-mm
Message-ID <anmb9FHMEMygENiO@thinkstation>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.