* [PATCH v5] kexec: keep the next kernel off hardware-poisoned pages
@ 2026-08-10 13:32 Breno Leitao
2026-08-10 14:01 ` Pratyush Yadav
` (3 more replies)
0 siblings, 4 replies; 10+ messages in thread
From: Breno Leitao @ 2026-08-10 13:32 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Baoquan He, Pasha Tatashin,
Pratyush Yadav, Miaohe Lin, Naoya Horiguchi
Cc: linux-mm, linux-kernel, kexec, rmikey, riel, kernel-team,
Kiryl Shutsemau, Breno Leitao
Memory failures (such as unrecoverable ECCs errors) are getting more and
more common. The kernel knows how to handle it while running, marking it
as poisoned (and SIGBUS user tasks).
Poisoned memory is removed from the buddy allocator, but, not from
other places. A current problem is that kexec will load new kernel
on top of a bad/poisoned memory, which is undesirable.
If the next kernel's image, initrd or purgatory lands on poisoned frame,
the relocation copy writes to the bad memory and the machine checks
during the kexec.
Skip hardware-poisoned frames when placing segments: check them in the
kexec_file hole finder so it lays the next kernel down on good memory,
and reject a poisoned destination in sanity_check_segment_list() for
the kexec_load path, which cannot relocate.
The two hole finders walk in opposite directions, so each asks for the
end of the poison it has to clear: the top-down walk for the first
poisoned page in the window, the bottom-up walk for the last. A poisoned
hugetlb folio counts in full, as hugetlb keeps the flag on the folio and
the poisoned subpages on its raw hwpoison list.
Suggested-by: Kiryl Shutsemau <kas@kernel.org>
Signed-off-by: Breno Leitao <leitao@debian.org>
---
Changes in v5:
- Return -EHWPOISON instead of -EADDRNOTAVAIL
- Leverage is_page_hwpoison() instead of per-page check
- Link to v4: https://patch.msgid.link/20260807-kexec_posioned-v4-1-70d57f14625d@debian.org
Changes in v4:
- Anchor the top-down hole finder on the first poisoned page in the
window and the bottom-up one on the last, so each jumps clear of the
poison in one step. New range_first_hwpoison(). (Kiryl Shutsemau)
- Count a poisoned hugetlb folio in full: the flag lives on the folio,
not on the subpages, so the per-pfn scan missed poisoned tail pages.
(Kiryl Shutsemau)
- Link to v3: https://patch.msgid.link/20260803-kexec_posioned-v3-1-83aa6ede0351@debian.org
Changes in v3:
- Return the address of the last poisoned page in the range, or
PHYS_ADDR_MAX when it is clean, instead of a bool plus an output
parameter. Renamed to range_last_hwpoison(). (Pratyush Yadav)
- Add cond_resched() to the scan loop, as a segment can span half of
memory. (Sashiko)
- Link to v2: https://patch.msgid.link/20260730-kexec_posioned-v2-1-f92d18551f64@debian.org
Changes in v2:
- Change from pfn_to_page() to pfn_to_online_page(). (Miaohe Lin)
- Return the poisoned address once we find a hit, to avoid the O(n^2)
rescan. (Sashiko)
- Link to v1: https://patch.msgid.link/20260728-kexec_posioned-v1-1-160c81d180fe@debian.org
To: Andrew Morton <akpm@linux-foundation.org>
To: David Hildenbrand <david@kernel.org>
To: Lorenzo Stoakes <ljs@kernel.org>
To: "Liam R. Howlett" <liam@infradead.org>
To: Vlastimil Babka <vbabka@kernel.org>
To: Mike Rapoport <rppt@kernel.org>
To: Suren Baghdasaryan <surenb@google.com>
To: Michal Hocko <mhocko@suse.com>
To: Baoquan He <baoquan.he@linux.dev>
To: Pasha Tatashin <pasha.tatashin@soleen.com>
To: Pratyush Yadav <pratyush@kernel.org>
To: Miaohe Lin <linmiaohe@huawei.com>
To: Naoya Horiguchi <nao.horiguchi@gmail.com>
Cc: linux-mm@kvack.org
Cc: linux-kernel@vger.kernel.org
Cc: kexec@lists.infradead.org
---
include/linux/mm.h | 14 ++++++++++++++
kernel/kexec_core.c | 10 ++++++++++
kernel/kexec_file.c | 18 ++++++++++++++++++
mm/memory-failure.c | 41 +++++++++++++++++++++++++++++++++++++++++
4 files changed, 83 insertions(+)
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 7fabe6c66b4b7..41b923901b193 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -5192,6 +5192,8 @@ extern const struct attribute_group memory_failure_attr_group;
extern void memory_failure_queue(unsigned long pfn, int flags);
void num_poisoned_pages_inc(unsigned long pfn);
void num_poisoned_pages_sub(unsigned long pfn, long i);
+phys_addr_t range_first_hwpoison(phys_addr_t start, unsigned long size);
+phys_addr_t range_last_hwpoison(phys_addr_t start, unsigned long size);
#else
static inline void memory_failure_queue(unsigned long pfn, int flags)
{
@@ -5204,6 +5206,18 @@ static inline void num_poisoned_pages_inc(unsigned long pfn)
static inline void num_poisoned_pages_sub(unsigned long pfn, long i)
{
}
+
+static inline phys_addr_t range_first_hwpoison(phys_addr_t start,
+ unsigned long size)
+{
+ return PHYS_ADDR_MAX;
+}
+
+static inline phys_addr_t range_last_hwpoison(phys_addr_t start,
+ unsigned long size)
+{
+ return PHYS_ADDR_MAX;
+}
#endif
#if defined(CONFIG_MEMORY_FAILURE) && defined(CONFIG_MEMORY_HOTPLUG)
diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c
index dc770b9a6d053..7ee8c9f078f6b 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 -EHWPOISON;
+ }
+
/*
* The destination addresses are searched from system RAM rather than
* being allocated from the buddy allocator, so they are not guaranteed
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index 59fb9d71e9d86..9ba6cc01af929 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -475,6 +475,7 @@ static int locate_mem_hole_top_down(unsigned long start, unsigned long end,
{
struct kimage *image = kbuf->image;
unsigned long temp_start, temp_end;
+ phys_addr_t poison;
temp_end = min(end, kbuf->buf_max);
temp_start = temp_end - kbuf->memsz + 1;
@@ -504,6 +505,15 @@ static int locate_mem_hole_top_down(unsigned long start, unsigned long end,
continue;
}
+ poison = range_first_hwpoison(temp_start, kbuf->memsz);
+ if (poison != PHYS_ADDR_MAX) {
+ /* we hit a poisoned page */
+ if (poison < kbuf->memsz)
+ return 0;
+ temp_start = poison - kbuf->memsz;
+ continue;
+ }
+
/* We found a suitable memory range */
break;
} while (1);
@@ -520,6 +530,7 @@ static int locate_mem_hole_bottom_up(unsigned long start, unsigned long end,
{
struct kimage *image = kbuf->image;
unsigned long temp_start, temp_end;
+ phys_addr_t poison;
temp_start = max(start, kbuf->buf_min);
@@ -546,6 +557,13 @@ static int locate_mem_hole_bottom_up(unsigned long start, unsigned long end,
continue;
}
+ poison = range_last_hwpoison(temp_start, kbuf->memsz);
+ if (poison != PHYS_ADDR_MAX) {
+ /* we hit a poisoned page */
+ temp_start = poison + PAGE_SIZE;
+ continue;
+ }
+
/* We found a suitable memory range */
break;
} while (1);
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index a8b03e2920ba8..f3875680e7955 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -96,6 +96,47 @@ 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);
+
+ cond_resched();
+
+ if (!page || !is_page_hwpoison(page))
+ continue;
+
+ if (first)
+ return PFN_PHYS(pfn);
+
+ poison = PFN_PHYS(pfn);
+ }
+
+ return poison;
+}
+
+phys_addr_t range_first_hwpoison(phys_addr_t start, unsigned long size)
+{
+ return range_hwpoison(start, size, true);
+}
+
+phys_addr_t range_last_hwpoison(phys_addr_t start, unsigned long size)
+{
+ return range_hwpoison(start, size, false);
+}
+
/**
* MF_ATTR_RO - Create sysfs entry for each memory failure statistics.
* @_name: name of the file in the per NUMA sysfs directory.
---
base-commit: c5e32e86ca02b003f86e095d379b38148999293d
change-id: 20260727-kexec_posioned-72bb0a4143a0
Best regards,
--
Breno Leitao <leitao@debian.org>
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH v5] kexec: keep the next kernel off hardware-poisoned pages
2026-08-10 13:32 [PATCH v5] kexec: keep the next kernel off hardware-poisoned pages Breno Leitao
@ 2026-08-10 14:01 ` Pratyush Yadav
2026-08-10 14:42 ` Bradley Morgan
` (2 subsequent siblings)
3 siblings, 0 replies; 10+ messages in thread
From: Pratyush Yadav @ 2026-08-10 14:01 UTC (permalink / raw)
To: Breno Leitao
Cc: Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Baoquan He, Pasha Tatashin,
Pratyush Yadav, Miaohe Lin, Naoya Horiguchi, linux-mm,
linux-kernel, kexec, rmikey, riel, kernel-team, Kiryl Shutsemau
On Mon, Aug 10 2026, Breno Leitao wrote:
> Memory failures (such as unrecoverable ECCs errors) are getting more and
> more common. The kernel knows how to handle it while running, marking it
> as poisoned (and SIGBUS user tasks).
>
> Poisoned memory is removed from the buddy allocator, but, not from
> other places. A current problem is that kexec will load new kernel
> on top of a bad/poisoned memory, which is undesirable.
>
> If the next kernel's image, initrd or purgatory lands on poisoned frame,
> the relocation copy writes to the bad memory and the machine checks
> during the kexec.
>
> Skip hardware-poisoned frames when placing segments: check them in the
> kexec_file hole finder so it lays the next kernel down on good memory,
> and reject a poisoned destination in sanity_check_segment_list() for
> the kexec_load path, which cannot relocate.
>
> The two hole finders walk in opposite directions, so each asks for the
> end of the poison it has to clear: the top-down walk for the first
> poisoned page in the window, the bottom-up walk for the last. A poisoned
> hugetlb folio counts in full, as hugetlb keeps the flag on the folio and
> the poisoned subpages on its raw hwpoison list.
>
> Suggested-by: Kiryl Shutsemau <kas@kernel.org>
> Signed-off-by: Breno Leitao <leitao@debian.org>
Reviewed-by: Pratyush Yadav <pratyush@kernel.org>
[...]
--
Regards,
Pratyush Yadav
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v5] kexec: keep the next kernel off hardware-poisoned pages
2026-08-10 13:32 [PATCH v5] kexec: keep the next kernel off hardware-poisoned pages Breno Leitao
2026-08-10 14:01 ` Pratyush Yadav
@ 2026-08-10 14:42 ` Bradley Morgan
2026-08-10 14:59 ` Kiryl Shutsemau
2026-08-10 16:32 ` Mike Rapoport
3 siblings, 0 replies; 10+ messages in thread
From: Bradley Morgan @ 2026-08-10 14:42 UTC (permalink / raw)
To: leitao
Cc: akpm, baoquan.he, david, kas, kernel-team, kexec, liam, linmiaohe,
linux-kernel, linux-mm, ljs, mhocko, nao.horiguchi,
pasha.tatashin, pratyush, riel, rmikey, rppt, surenb, vbabka
hi, I feel this looks right to me.
Reviewed-by: Bradley Morgan <include@grrlz.net>
Thanks!
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v5] kexec: keep the next kernel off hardware-poisoned pages
2026-08-10 13:32 [PATCH v5] kexec: keep the next kernel off hardware-poisoned pages Breno Leitao
2026-08-10 14:01 ` Pratyush Yadav
2026-08-10 14:42 ` Bradley Morgan
@ 2026-08-10 14:59 ` Kiryl Shutsemau
2026-08-10 16:32 ` Mike Rapoport
3 siblings, 0 replies; 10+ messages in thread
From: Kiryl Shutsemau @ 2026-08-10 14:59 UTC (permalink / raw)
To: Breno Leitao
Cc: Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Baoquan He, Pasha Tatashin,
Pratyush Yadav, Miaohe Lin, Naoya Horiguchi, linux-mm,
linux-kernel, kexec, rmikey, riel, kernel-team
On Mon, Aug 10, 2026 at 06:32:04AM -0700, Breno Leitao wrote:
> Memory failures (such as unrecoverable ECCs errors) are getting more and
> more common. The kernel knows how to handle it while running, marking it
> as poisoned (and SIGBUS user tasks).
>
> Poisoned memory is removed from the buddy allocator, but, not from
> other places. A current problem is that kexec will load new kernel
> on top of a bad/poisoned memory, which is undesirable.
>
> If the next kernel's image, initrd or purgatory lands on poisoned frame,
> the relocation copy writes to the bad memory and the machine checks
> during the kexec.
>
> Skip hardware-poisoned frames when placing segments: check them in the
> kexec_file hole finder so it lays the next kernel down on good memory,
> and reject a poisoned destination in sanity_check_segment_list() for
> the kexec_load path, which cannot relocate.
>
> The two hole finders walk in opposite directions, so each asks for the
> end of the poison it has to clear: the top-down walk for the first
> poisoned page in the window, the bottom-up walk for the last. A poisoned
> hugetlb folio counts in full, as hugetlb keeps the flag on the folio and
> the poisoned subpages on its raw hwpoison list.
>
> Suggested-by: Kiryl Shutsemau <kas@kernel.org>
> Signed-off-by: Breno Leitao <leitao@debian.org>
Reviewed-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
--
Kiryl Shutsemau / Kirill A. Shutemov
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v5] kexec: keep the next kernel off hardware-poisoned pages
2026-08-10 13:32 [PATCH v5] kexec: keep the next kernel off hardware-poisoned pages Breno Leitao
` (2 preceding siblings ...)
2026-08-10 14:59 ` Kiryl Shutsemau
@ 2026-08-10 16:32 ` Mike Rapoport
2026-08-11 11:17 ` Breno Leitao
3 siblings, 1 reply; 10+ messages in thread
From: Mike Rapoport @ 2026-08-10 16:32 UTC (permalink / raw)
To: Breno Leitao
Cc: Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Suren Baghdasaryan,
Michal Hocko, Baoquan He, Pasha Tatashin, Pratyush Yadav,
Miaohe Lin, Naoya Horiguchi, linux-mm, linux-kernel, kexec,
rmikey, riel, kernel-team, Kiryl Shutsemau
Hi Breno,
On Mon, Aug 10, 2026 at 06:32:04AM -0700, Breno Leitao wrote:
> Memory failures (such as unrecoverable ECCs errors) are getting more and
> more common. The kernel knows how to handle it while running, marking it
> as poisoned (and SIGBUS user tasks).
>
> Poisoned memory is removed from the buddy allocator, but, not from
> other places. A current problem is that kexec will load new kernel
> on top of a bad/poisoned memory, which is undesirable.
>
> If the next kernel's image, initrd or purgatory lands on poisoned frame,
> the relocation copy writes to the bad memory and the machine checks
What does the machine check here? ;-)
> during the kexec.
>
> Skip hardware-poisoned frames when placing segments: check them in the
> kexec_file hole finder so it lays the next kernel down on good memory,
> and reject a poisoned destination in sanity_check_segment_list() for
> the kexec_load path, which cannot relocate.
>
> The two hole finders walk in opposite directions, so each asks for the
> end of the poison it has to clear: the top-down walk for the first
> poisoned page in the window, the bottom-up walk for the last. A poisoned
> hugetlb folio counts in full, as hugetlb keeps the flag on the folio and
> the poisoned subpages on its raw hwpoison list.
I had hard time parsing these two paragraphs. Can you please add more human
touch to them?
> Suggested-by: Kiryl Shutsemau <kas@kernel.org>
> Signed-off-by: Breno Leitao <leitao@debian.org>
>
> diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c
> index dc770b9a6d053..7ee8c9f078f6b 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.
Would cause machine-check exception?
> + */
> + for (i = 0; i < nr_segments; i++) {
> + if (range_first_hwpoison(image->segment[i].mem,
> + image->segment[i].memsz) != PHYS_ADDR_MAX)
> + return -EHWPOISON;
> + }
> +
> /*
> * The destination addresses are searched from system RAM rather than
> * being allocated from the buddy allocator, so they are not guaranteed
> diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
> index 59fb9d71e9d86..9ba6cc01af929 100644
> --- a/kernel/kexec_file.c
> +++ b/kernel/kexec_file.c
> @@ -475,6 +475,7 @@ static int locate_mem_hole_top_down(unsigned long start, unsigned long end,
> {
> struct kimage *image = kbuf->image;
> unsigned long temp_start, temp_end;
> + phys_addr_t poison;
>
> temp_end = min(end, kbuf->buf_max);
> temp_start = temp_end - kbuf->memsz + 1;
> @@ -504,6 +505,15 @@ static int locate_mem_hole_top_down(unsigned long start, unsigned long end,
> continue;
> }
>
> + poison = range_first_hwpoison(temp_start, kbuf->memsz);
> + if (poison != PHYS_ADDR_MAX) {
> + /* we hit a poisoned page */
> + if (poison < kbuf->memsz)
> + return 0;
Won't we break out on the next iteration boundaries check? I.e.
if (temp_start < start || temp_start < kbuf->buf_min)
return 0;
> + temp_start = poison - kbuf->memsz;
> + continue;
> + }
> +
> /* We found a suitable memory range */
> break;
> } while (1);
> @@ -520,6 +530,7 @@ static int locate_mem_hole_bottom_up(unsigned long start, unsigned long end,
> {
> struct kimage *image = kbuf->image;
> unsigned long temp_start, temp_end;
> + phys_addr_t poison;
>
> temp_start = max(start, kbuf->buf_min);
>
> @@ -546,6 +557,13 @@ static int locate_mem_hole_bottom_up(unsigned long start, unsigned long end,
> continue;
> }
>
> + poison = range_last_hwpoison(temp_start, kbuf->memsz);
> + if (poison != PHYS_ADDR_MAX) {
> + /* we hit a poisoned page */
> + temp_start = poison + PAGE_SIZE;
> + continue;
> + }
> +
> /* We found a suitable memory range */
> break;
> } while (1);
> diff --git a/mm/memory-failure.c b/mm/memory-failure.c
> index a8b03e2920ba8..f3875680e7955 100644
> --- a/mm/memory-failure.c
> +++ b/mm/memory-failure.c
> @@ -96,6 +96,47 @@ 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);
> +
> + cond_resched();
cond_resched() for every pfn is too much, isn't it?
> +
> + if (!page || !is_page_hwpoison(page))
> + continue;
> +
> + if (first)
> + return PFN_PHYS(pfn);
> +
> + poison = PFN_PHYS(pfn);
> + }
> +
> + return poison;
> +}
> +
> +phys_addr_t range_first_hwpoison(phys_addr_t start, unsigned long size)
> +{
> + return range_hwpoison(start, size, true);
> +}
> +
> +phys_addr_t range_last_hwpoison(phys_addr_t start, unsigned long size)
> +{
> + return range_hwpoison(start, size, false);
> +}
> +
> /**
> * MF_ATTR_RO - Create sysfs entry for each memory failure statistics.
> * @_name: name of the file in the per NUMA sysfs directory.
>
> ---
> base-commit: c5e32e86ca02b003f86e095d379b38148999293d
> change-id: 20260727-kexec_posioned-72bb0a4143a0
>
> Best regards,
> --
> Breno Leitao <leitao@debian.org>
>
--
Sincerely yours,
Mike.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v5] kexec: keep the next kernel off hardware-poisoned pages
2026-08-10 16:32 ` Mike Rapoport
@ 2026-08-11 11:17 ` Breno Leitao
2026-08-11 11:36 ` Pratyush Yadav
2026-08-11 13:34 ` Mike Rapoport
0 siblings, 2 replies; 10+ messages in thread
From: Breno Leitao @ 2026-08-11 11:17 UTC (permalink / raw)
To: Mike Rapoport
Cc: Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Suren Baghdasaryan,
Michal Hocko, Baoquan He, Pasha Tatashin, Pratyush Yadav,
Miaohe Lin, Naoya Horiguchi, linux-mm, linux-kernel, kexec,
rmikey, riel, kernel-team, Kiryl Shutsemau
On Mon, Aug 10, 2026 at 07:32:41PM +0300, Mike Rapoport wrote:
> Hi Breno,
>
> On Mon, Aug 10, 2026 at 06:32:04AM -0700, Breno Leitao wrote:
> > Memory failures (such as unrecoverable ECCs errors) are getting more and
> > more common. The kernel knows how to handle it while running, marking it
> > as poisoned (and SIGBUS user tasks).
> >
> > Poisoned memory is removed from the buddy allocator, but, not from
> > other places. A current problem is that kexec will load new kernel
> > on top of a bad/poisoned memory, which is undesirable.
> >
> > If the next kernel's image, initrd or purgatory lands on poisoned frame,
> > the relocation copy writes to the bad memory and the machine checks
>
> What does the machine check here? ;-)
Not sure I got your question right. Did you mean:
1) that there is no machine check exception when *writing* to poisoned
memory, or
2) just that "the machine checks" is a lousy way to write it?
For 1) I think you are right, and I had not thought it through. The MCE
(or a recurrent multi-bit ECC) would come from consuming the error, so
a load or an instruction fetch, and a store may well pass silently and
leave the poison sitting there.
The read back is what gets us, though.
So the sentence should hang on the read, not on the copy. Would
something like makes more sense?
If the next kernel's image, initrd or purgatory lands on a
poisoned frame, the relocation copy puts them on memory that is
known bad.
The error is consumed on the first read back, whether
that is purgatory checksumming the segments or the new kernel
running from them, and that is what we want to avoid.
> > Skip hardware-poisoned frames when placing segments: check them in the
> > kexec_file hole finder so it lays the next kernel down on good memory,
> > and reject a poisoned destination in sanity_check_segment_list() for
> > the kexec_load path, which cannot relocate.
> >
> > The two hole finders walk in opposite directions, so each asks for the
> > end of the poison it has to clear: the top-down walk for the first
> > poisoned page in the window, the bottom-up walk for the last. A poisoned
> > hugetlb folio counts in full, as hugetlb keeps the flag on the folio and
> > the poisoned subpages on its raw hwpoison list.
>
> I had hard time parsing these two paragraphs. Can you please add more human
> touch to them?
Sure, but that would cost more. :-)
What about something like:
Skip hardware-poisoned frames that were detected by machine
failure subssytem earlier when placing kexec segments.
To do so, add a helper that reports the first or the last poisoned page
in a range: memory is walked top-down by locate_mem_hole_top_down() and
bottom-up by locate_mem_hole_bottom_up(), so each direction needs a
different answer to jump clear of the poison.
kexec_load() gets its destinations from userspace and cannot move them,
so there sanity_check_segment_list() just rejects
a a segument/memory block that happens to have a posioned page.
is_page_hwpoison() also covers hugetlb, where the flag sits on the folio
and the bad subpages on its raw hwpoison list, so a poisoned hugetlb
folio is skipped as a whole.
> > + poison = range_first_hwpoison(temp_start, kbuf->memsz);
> > + if (poison != PHYS_ADDR_MAX) {
> > + /* we hit a poisoned page */
> > + if (poison < kbuf->memsz)
> > + return 0;
>
> Won't we break out on the next iteration boundaries check? I.e.
>
> if (temp_start < start || temp_start < kbuf->buf_min)
> return 0;
Kind-of. Sashiko keeps raising this underflow in the function, on every
revision since v2.
It dismisses it on this hunk because of the check, but reports it as
a real one on the two "temp_start = temp_start - PAGE_SIZE" paths above,
which do the same subtraction with nothing guarding them.
Happy to remove it from here and send that as a separate patch.
> > + for (pfn = PHYS_PFN(start); pfn <= end_pfn; pfn++) {
> > +
> > + cond_resched();
>
> cond_resched() for every pfn is too much, isn't it?
It is what the other pfn walkers do: the kpageflags read loop in
fs/proc/page.c and read_page_owner() in mm/page_owner.c both call it
once per pfn.
But I honestly don't have a strong opinion here, though, happy to batch
it if you prefer. Would this one look better?:
if (!(pfn % MAX_ORDER_NR_PAGES))
cond_resched();
Thanks for the review,
--breno
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v5] kexec: keep the next kernel off hardware-poisoned pages
2026-08-11 11:17 ` Breno Leitao
@ 2026-08-11 11:36 ` Pratyush Yadav
2026-08-11 13:34 ` Mike Rapoport
1 sibling, 0 replies; 10+ messages in thread
From: Pratyush Yadav @ 2026-08-11 11:36 UTC (permalink / raw)
To: Breno Leitao
Cc: Mike Rapoport, Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Suren Baghdasaryan,
Michal Hocko, Baoquan He, Pasha Tatashin, Pratyush Yadav,
Miaohe Lin, Naoya Horiguchi, linux-mm, linux-kernel, kexec,
rmikey, riel, kernel-team, Kiryl Shutsemau
On Tue, Aug 11 2026, Breno Leitao wrote:
> On Mon, Aug 10, 2026 at 07:32:41PM +0300, Mike Rapoport wrote:
>> Hi Breno,
>>
>> On Mon, Aug 10, 2026 at 06:32:04AM -0700, Breno Leitao wrote:
[...]
>> > + for (pfn = PHYS_PFN(start); pfn <= end_pfn; pfn++) {
>> > +
>> > + cond_resched();
>>
>> cond_resched() for every pfn is too much, isn't it?
>
> It is what the other pfn walkers do: the kpageflags read loop in
> fs/proc/page.c and read_page_owner() in mm/page_owner.c both call it
> once per pfn.
>
> But I honestly don't have a strong opinion here, though, happy to batch
> it if you prefer. Would this one look better?:
>
> if (!(pfn % MAX_ORDER_NR_PAGES))
> cond_resched();
>
Now that we are looking at this, Sashiko also complains about a race
with memory hotunplug.
Can the page pointer become invalid here due to concurrent memory hotplug?
The loop retrieves the struct page pointer using pfn_to_online_page() and
then yields the CPU with cond_resched(). Since this iteration runs without
holding get_online_mems(), could a concurrent memory hot-unplug event offline
and remove the memory section while the thread is sleeping?
If the vmemmap backing the struct page is freed and its page tables torn
down, dereferencing the pointer in is_page_hwpoison(page) upon waking
could cause a use-after-free regression.
Kind of makes sense at first glance but I didn't go and look. Perhaps it
is better to do the cond_resched() at the end of the loop?
--
Regards,
Pratyush Yadav
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v5] kexec: keep the next kernel off hardware-poisoned pages
2026-08-11 11:17 ` Breno Leitao
2026-08-11 11:36 ` Pratyush Yadav
@ 2026-08-11 13:34 ` Mike Rapoport
2026-08-11 14:21 ` Kiryl Shutsemau
1 sibling, 1 reply; 10+ messages in thread
From: Mike Rapoport @ 2026-08-11 13:34 UTC (permalink / raw)
To: Breno Leitao
Cc: Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Suren Baghdasaryan,
Michal Hocko, Baoquan He, Pasha Tatashin, Pratyush Yadav,
Miaohe Lin, Naoya Horiguchi, linux-mm, linux-kernel, kexec,
rmikey, riel, kernel-team, Kiryl Shutsemau
On Tue, Aug 11, 2026 at 04:17:41AM -0700, Breno Leitao wrote:
> On Mon, Aug 10, 2026 at 07:32:41PM +0300, Mike Rapoport wrote:
> > Hi Breno,
> >
> > On Mon, Aug 10, 2026 at 06:32:04AM -0700, Breno Leitao wrote:
> > > Memory failures (such as unrecoverable ECCs errors) are getting more and
> > > more common. The kernel knows how to handle it while running, marking it
> > > as poisoned (and SIGBUS user tasks).
> > >
> > > Poisoned memory is removed from the buddy allocator, but, not from
> > > other places. A current problem is that kexec will load new kernel
> > > on top of a bad/poisoned memory, which is undesirable.
> > >
> > > If the next kernel's image, initrd or purgatory lands on poisoned frame,
> > > the relocation copy writes to the bad memory and the machine checks
> >
> > What does the machine check here? ;-)
>
> Not sure I got your question right. Did you mean:
I meant that "and the machine checks during the kexec" reads as machine
checks for something and that something is missing.
"machine check exceptions" would have been clearer :)
> 1) that there is no machine check exception when *writing* to poisoned
> memory, or
>
> 2) just that "the machine checks" is a lousy way to write it?
>
> For 1) I think you are right, and I had not thought it through. The MCE
> (or a recurrent multi-bit ECC) would come from consuming the error, so
> a load or an instruction fetch, and a store may well pass silently and
> leave the poison sitting there.
>
> The read back is what gets us, though.
>
> So the sentence should hang on the read, not on the copy. Would
> something like makes more sense?
>
> If the next kernel's image, initrd or purgatory lands on a
> poisoned frame, the relocation copy puts them on memory that is
> known bad.
>
> The error is consumed on the first read back, whether
> that is purgatory checksumming the segments or the new kernel
> running from them, and that is what we want to avoid.
I wouldn't overload the sentence, just
The error happens on the first from a bad page and that's what we
want to avoid.
looks enough to me.
> > > Skip hardware-poisoned frames when placing segments: check them in the
> > > kexec_file hole finder so it lays the next kernel down on good memory,
> > > and reject a poisoned destination in sanity_check_segment_list() for
> > > the kexec_load path, which cannot relocate.
> > >
> > > The two hole finders walk in opposite directions, so each asks for the
> > > end of the poison it has to clear: the top-down walk for the first
> > > poisoned page in the window, the bottom-up walk for the last. A poisoned
> > > hugetlb folio counts in full, as hugetlb keeps the flag on the folio and
> > > the poisoned subpages on its raw hwpoison list.
> >
> > I had hard time parsing these two paragraphs. Can you please add more human
> > touch to them?
>
> Sure, but that would cost more. :-)
>
> What about something like:
>
> Skip hardware-poisoned frames that were detected by machine
^ memory
> failure subssytem earlier when placing kexec segments.
^ subsystem
>
> To do so, add a helper that reports the first or the last poisoned page
> in a range: memory is walked top-down by locate_mem_hole_top_down() and
> bottom-up by locate_mem_hole_bottom_up(), so each direction needs a
> different answer to jump clear of the poison.
^ stay
> kexec_load() gets its destinations from userspace and cannot move them,
> so there sanity_check_segment_list() just rejects
> a a segument/memory block that happens to have a posioned page.
^ single a
>
> is_page_hwpoison() also covers hugetlb, where the flag sits on the folio
> and the bad subpages on its raw hwpoison list, so a poisoned hugetlb
> folio is skipped as a whole.
I don't think we care here about the list of bad subpages:
is_page_hwpoison() also covers hugetlb, so a poisoned hugetlb
folio is skipped as a whole.
>
> > > + poison = range_first_hwpoison(temp_start, kbuf->memsz);
> > > + if (poison != PHYS_ADDR_MAX) {
> > > + /* we hit a poisoned page */
> > > + if (poison < kbuf->memsz)
> > > + return 0;
> >
> > Won't we break out on the next iteration boundaries check? I.e.
> >
> > if (temp_start < start || temp_start < kbuf->buf_min)
> > return 0;
>
> Kind-of. Sashiko keeps raising this underflow in the function, on every
> revision since v2.
>
> It dismisses it on this hunk because of the check, but reports it as
> a real one on the two "temp_start = temp_start - PAGE_SIZE" paths above,
> which do the same subtraction with nothing guarding them.
>
> Happy to remove it from here and send that as a separate patch.
Let's make it a separate patch please and drop the if (poison <
kbuf->memsz) here.
> > > + for (pfn = PHYS_PFN(start); pfn <= end_pfn; pfn++) {
> > > +
> > > + cond_resched();
> >
> > cond_resched() for every pfn is too much, isn't it?
>
> It is what the other pfn walkers do: the kpageflags read loop in
> fs/proc/page.c and read_page_owner() in mm/page_owner.c both call it
> once per pfn.
I think it depends on the pfn walker, some of them cond_resched() once per
"block"
> But I honestly don't have a strong opinion here, though, happy to batch
> it if you prefer. Would this one look better?:
>
> if (!(pfn % MAX_ORDER_NR_PAGES))
> cond_resched();
Can't say I know the magic number here, but I think it's better to batch.
We had a related discussion with Muchun a short while ago:
https://lore.kernel.org/all/ak97z4tryYAGJgb_@kernel.org/
> Thanks for the review,
> --breno
--
Sincerely yours,
Mike.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v5] kexec: keep the next kernel off hardware-poisoned pages
2026-08-11 13:34 ` Mike Rapoport
@ 2026-08-11 14:21 ` Kiryl Shutsemau
2026-08-11 14:51 ` Rik van Riel
0 siblings, 1 reply; 10+ messages in thread
From: Kiryl Shutsemau @ 2026-08-11 14:21 UTC (permalink / raw)
To: Mike Rapoport
Cc: Breno Leitao, Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Suren Baghdasaryan,
Michal Hocko, Baoquan He, Pasha Tatashin, Pratyush Yadav,
Miaohe Lin, Naoya Horiguchi, linux-mm, linux-kernel, kexec,
rmikey, riel, kernel-team
On Tue, Aug 11, 2026 at 04:34:02PM +0300, Mike Rapoport wrote:
> On Tue, Aug 11, 2026 at 04:17:41AM -0700, Breno Leitao wrote:
> > > > + for (pfn = PHYS_PFN(start); pfn <= end_pfn; pfn++) {
> > > > +
> > > > + cond_resched();
> > >
> > > cond_resched() for every pfn is too much, isn't it?
> >
> > It is what the other pfn walkers do: the kpageflags read loop in
> > fs/proc/page.c and read_page_owner() in mm/page_owner.c both call it
> > once per pfn.
>
> I think it depends on the pfn walker, some of them cond_resched() once per
> "block"
>
> > But I honestly don't have a strong opinion here, though, happy to batch
> > it if you prefer. Would this one look better?:
> >
> > if (!(pfn % MAX_ORDER_NR_PAGES))
> > cond_resched();
>
> Can't say I know the magic number here, but I think it's better to batch.
Hm. I thought cond_resched() deal with this internally, no?
We call it in pretty tight loops, like clear_contig_highpages() or
copy_folio_from_user().
I think adding external batching might be a bad move.
If calling cond_resched() often is a problem, something is wrong with
cond_resched().
--
Kiryl Shutsemau / Kirill A. Shutemov
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v5] kexec: keep the next kernel off hardware-poisoned pages
2026-08-11 14:21 ` Kiryl Shutsemau
@ 2026-08-11 14:51 ` Rik van Riel
0 siblings, 0 replies; 10+ messages in thread
From: Rik van Riel @ 2026-08-11 14:51 UTC (permalink / raw)
To: Kiryl Shutsemau, Mike Rapoport
Cc: Breno Leitao, Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Suren Baghdasaryan,
Michal Hocko, Baoquan He, Pasha Tatashin, Pratyush Yadav,
Miaohe Lin, Naoya Horiguchi, linux-mm, linux-kernel, kexec,
rmikey, kernel-team
On Tue, 2026-08-11 at 15:21 +0100, Kiryl Shutsemau wrote:
> On Tue, Aug 11, 2026 at 04:34:02PM +0300, Mike Rapoport wrote:
> > On Tue, Aug 11, 2026 at 04:17:41AM -0700, Breno Leitao wrote:
> > > > >
> > > But I honestly don't have a strong opinion here, though, happy to
> > > batch
> > > it if you prefer. Would this one look better?:
> > >
> > > if (!(pfn % MAX_ORDER_NR_PAGES))
> > > cond_resched();
> >
> > Can't say I know the magic number here, but I think it's better to
> > batch.
>
> Hm. I thought cond_resched() deal with this internally, no?
>
> We call it in pretty tight loops, like clear_contig_highpages() or
> copy_folio_from_user().
>
> I think adding external batching might be a bad move.
>
cond_resched() should be really cheap, indeed.
It gets compiled out completely with PREEMPT_LAZY
or PREEMPT_FULL.
--
All Rights Reversed.
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-08-11 14:52 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-10 13:32 [PATCH v5] kexec: keep the next kernel off hardware-poisoned pages Breno Leitao
2026-08-10 14:01 ` Pratyush Yadav
2026-08-10 14:42 ` Bradley Morgan
2026-08-10 14:59 ` Kiryl Shutsemau
2026-08-10 16:32 ` Mike Rapoport
2026-08-11 11:17 ` Breno Leitao
2026-08-11 11:36 ` Pratyush Yadav
2026-08-11 13:34 ` Mike Rapoport
2026-08-11 14:21 ` Kiryl Shutsemau
2026-08-11 14:51 ` Rik van Riel
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox