[PATCH v2 0/6] efi: mm/memory-failure: keep hardware-poisoned pages out of the next kexec
Breno Leitao
leitao at debian.org
Fri Aug 21 03:06:00 PDT 2026
Problem:
========
When a page is hard-offlined due to an uncorrectable memory error (multi
bit ECC), memory_failure() sets PG_hwpoison, unmaps it, removes it from
the buddy allocator. This information is not carried to the next kernel
that is kexeced. The new kernel kexecs and trip over that bad memory
bank _again_.
Why now:
========
Several industry trends make this increasingly important:
1) DRAM is getting more expensive
2) soldered / on-package memory (LPDDR, HBM) is becoming more common, so a
failing part can no longer simply be swapped;
3) memory is kept in service far longer (at Meta, DRAM lifetime is being
drastically extended).
4) It is more and more common to kexec instead of full reboot
5) Increase of memory per system with CXL
What was done already:
======================
In order make linux deal better with the problem above, I've done
already fixed a bunch of stuff in this area, such as:
1) Panic on unrecoverable errors, instead of "printk and carry over":
https://lore.kernel.org/all/20260630-ecc_panic-v10-0-c6ed5b62eea2@debian.org/
2) Respect poisoned memory at kexec time
https://lore.kernel.org/all/20260812-kexec_posioned-v6-0-e477887086f0@debian.org/
Now, the natural follow up is to carry the poisoned memory information
to the next kexec kernel, avoiding tripping over a known "bad page".
Proposed Solution:
==================
Carry the poisoned frames to the next kernel in a new EFI configuration
table, LINUX_EFI_POISONED_MEMORY.
EFI configuration tables already survive kexec: firmware hands the EFI
system table to every kernel in the chain, so a table installed once is
seen by all successors without a new handover channel.
The table is a bitmap with one bit per 2MB of physical memory, modeled
on LINUX_EFI_UNACCEPTED_MEMORY. The stub sizes it from the EFI memory
map and installs it empty while boot services are up -- a running kernel
cannot install a configuration table, it can only flip bits -- and a
table inherited from an earlier boot is reused as-is. That is one
fixed-size allocation, 64KB per TB of RAM, with no list to grow at
runtime and no chain to trust at parse time.
The allocation is capped at 1MB, which covers 16TB at 2MB per bit. Past
that the stub doubles the unit rather than growing the table, so the
table stays bounded on any machine.
The mechanism is architecture independent, so x86 and arm64 use the same
code.
Each hard offline sets the bit for its unit. Soft-offlined pages are not
recorded: they are still functional, and were offlined predictively.
The next kernel walks the inherited bitmap early in
efi_config_parse_tables(), before memblock and the buddy allocator are
up, and memblock_reserve()s every unit whose bit is set. The bad RAM is
never handed out.
Granularity is the trade-off: one bad 4KB frame costs a whole unit --
2MB, or more on a machine large enough to have coarsened it -- in every
later kernel of the chain. In exchange, a row or column fault -- roughly a
quarter of the DRAM faults reported in [1], and potentially thousands of
4KB pages scattered over gigabytes -- collapses into a bit or two.
A bit is never cleared, which is a known limitation: it stands for a
whole unit, so an unpoison of one frame cannot tell whether the unit as
a whole is good again, and after a kexec there is no PG_hwpoison left to
consult either. Unpoisoning a frame does not hand its unit back to the
next kernel.
Memory hot-added after boot is not covered either: the bitmap spans the
RAM the EFI memory map describes, and a frame outside it is silently not
recorded.
The series is six patches:
1) add a libstub helper for the top of usable RAM
2) add the LINUX_EFI_POISONED_MEMORY table
3) build the table in the stub
4) install it from both stub entry paths
5) record poisoned frames into the table from the memory_failure() path
6) reserve the inherited frames before the allocator comes up
This was initially discussed at
https://lore.kernel.org/all/ajut_LDQGYCShApx@gmail.com/
A special thanks to Kiryl Shutsemau, for feedbacks and suggestions.
[1] https://arxiv.org/abs/2408.15302
To: Ard Biesheuvel <ardb at kernel.org>
To: Ilias Apalodimas <ilias.apalodimas at linaro.org>
To: Miaohe Lin <linmiaohe at huawei.com>
To: Naoya Horiguchi <nao.horiguchi at gmail.com>
To: Andrew Morton <akpm at linux-foundation.org>
Cc: linux-efi at vger.kernel.org
Cc: linux-kernel at vger.kernel.org
Cc: linux-mm at kvack.org
Cc: rmikey at meta.com
Cc: kas at kernel.org
Cc: riel at surriel.com
Cc: kexec at lists.infradead.org
Signed-off-by: Breno Leitao <leitao at debian.org>
---
Changes in v2:
- Replace the growable linked list of 4KB entries with a fixed-size
bitmap, one bit per 2MB, modeled on the unaccepted-memory table (Kiryl)
- Record hard offlines only, by hooking action_result() instead of
num_poisoned_pages_inc(), which also fires for soft offline (Kiryl)
- Allocate the table as EFI_ACPI_RECLAIM_MEMORY, so it is not System RAM
in the next kernel, and reuse an inherited table instead of installing
a second one
- Validate the geometry of an inherited table before using it
- Cap the table at 1MB, coarsening the unit instead of growing it
- Restrict to 64-bit, as the unaccepted-memory table effectively is
- Never clear a bit: an unpoison no longer un-records the unit
- Split the table definition and the stub installer into separate patches
- Link to v1: https://patch.msgid.link/20260717-hwpoison-kho-v1-0-9c5eda551998@debian.org
---
Breno Leitao (6):
efi/libstub: add a helper for the top of usable RAM
efi: add the LINUX_EFI_POISONED_MEMORY configuration table
efi/libstub: add the poisoned-memory EFI table
efi/libstub: install the poisoned-memory table from the stub
efi: record hardware-poisoned frames into the poisoned-memory table
efi: respect the poisoned pages coming from previous kernel
drivers/firmware/efi/Kconfig | 10 ++
drivers/firmware/efi/Makefile | 1 +
drivers/firmware/efi/efi.c | 8 +
drivers/firmware/efi/libstub/efi-stub-helper.c | 89 +++++++++++
drivers/firmware/efi/libstub/efi-stub.c | 1 +
drivers/firmware/efi/libstub/efistub.h | 8 +
drivers/firmware/efi/libstub/mem.c | 53 +++++++
drivers/firmware/efi/libstub/x86-stub.c | 2 +
drivers/firmware/efi/poison.c | 197 +++++++++++++++++++++++++
include/linux/efi.h | 21 +++
mm/memory-failure.c | 3 +
11 files changed, 393 insertions(+)
---
base-commit: b8809969e1d7a591e0f49dd464a5d04b3cf02ab1
change-id: 20260622-hwpoison-kho-fc9db2ada8ba
Best regards,
--
Breno Leitao <leitao at debian.org>
More information about the kexec
mailing list