[PATCH] ACPI: APEI: GHES: Cache EFI memory attributes
Rui Qi
qirui.001 at bytedance.com
Thu Oct 22 04:52:06 PDT 2026
GHES copies error status blocks through a fixmap because the physical
address of the firmware buffer may only be known from IRQ, timer or
other atomic notification contexts. On arm64, each fixmap update asks
the architecture code to derive the mapping attributes from the EFI
memory map.
efi_mem_desc_lookup() scans the EFI descriptor map. A multi-page CPER
record can therefore repeat the same descriptor search for adjacent
pages while ghes_notify_lock_irq is held and interrupts are disabled.
GHES records are bounded, so this is not normally a hot path, but
corrected error storms on systems with fragmented EFI memory maps can
amplify that latency.
Return the end of the matching EFI descriptor through the APEI memory
attribute hook and let GHES reuse the computed page protection while
copying pages covered by the same descriptor.
Signed-off-by: Rui Qi <qirui.001 at bytedance.com>
---
arch/arm64/include/asm/acpi.h | 7 ++++---
arch/arm64/kernel/acpi.c | 11 ++++++++---
arch/riscv/include/asm/acpi.h | 5 ++++-
arch/x86/include/asm/acpi.h | 5 ++++-
drivers/acpi/apei/ghes.c | 25 +++++++++++++++++++++----
5 files changed, 41 insertions(+), 12 deletions(-)
diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h
index 8a54ca6ba602..055205f52f9f 100644
--- a/arch/arm64/include/asm/acpi.h
+++ b/arch/arm64/include/asm/acpi.h
@@ -66,7 +66,7 @@ static inline unsigned int arch_get_idle_state_flags(u32 arch_flags)
/* Basic configuration for ACPI */
#ifdef CONFIG_ACPI
-pgprot_t __acpi_get_mem_attribute(phys_addr_t addr);
+pgprot_t __acpi_get_mem_attribute(phys_addr_t addr, phys_addr_t *end);
/* ACPI table mapping after acpi_permanent_mmap is set */
void __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size);
@@ -155,9 +155,10 @@ static __always_inline const char *acpi_get_enable_method(int cpu)
* for compatibility.
*/
#define acpi_disable_cmcff 1
-static inline pgprot_t arch_apei_get_mem_attribute(phys_addr_t addr)
+static inline pgprot_t arch_apei_get_mem_attribute(phys_addr_t addr,
+ phys_addr_t *end)
{
- return __acpi_get_mem_attribute(addr);
+ return __acpi_get_mem_attribute(addr, end);
}
#endif /* CONFIG_ACPI_APEI */
diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c
index 681aa2bbc399..b3097f6e0ea8 100644
--- a/arch/arm64/kernel/acpi.c
+++ b/arch/arm64/kernel/acpi.c
@@ -270,7 +270,7 @@ static pgprot_t __acpi_get_writethrough_mem_attribute(void)
return __pgprot(PROT_NORMAL_NC);
}
-pgprot_t __acpi_get_mem_attribute(phys_addr_t addr)
+pgprot_t __acpi_get_mem_attribute(phys_addr_t addr, phys_addr_t *end)
{
/*
* According to "Table 8 Map: EFI memory types to AArch64 memory
@@ -280,9 +280,14 @@ pgprot_t __acpi_get_mem_attribute(phys_addr_t addr)
* of a memory region.
*/
- u64 attr;
+ efi_memory_desc_t md;
+ u64 attr = 0;
- attr = efi_mem_attributes(addr);
+ if (!efi_mem_desc_lookup(addr, &md)) {
+ attr = md.attribute;
+ if (end)
+ *end = md.phys_addr + (md.num_pages << EFI_PAGE_SHIFT);
+ }
if (attr & EFI_MEMORY_WB)
return PAGE_KERNEL;
if (attr & EFI_MEMORY_WC)
diff --git a/arch/riscv/include/asm/acpi.h b/arch/riscv/include/asm/acpi.h
index c11fc0fa9bee..882ed5433214 100644
--- a/arch/riscv/include/asm/acpi.h
+++ b/arch/riscv/include/asm/acpi.h
@@ -121,8 +121,11 @@ static inline void acpi_map_cpus_to_nodes(void) { }
* RISC-V does not have CMC, so always disable it.
*/
#define acpi_disable_cmcff 1
-static inline pgprot_t arch_apei_get_mem_attribute(phys_addr_t addr)
+static inline pgprot_t arch_apei_get_mem_attribute(phys_addr_t addr,
+ phys_addr_t *end)
{
+ if (end)
+ *end = (phys_addr_t)-1;
return PAGE_KERNEL;
}
#endif /* CONFIG_ACPI_APEI */
diff --git a/arch/x86/include/asm/acpi.h b/arch/x86/include/asm/acpi.h
index c4e6459bd56b..a3f1f1b0a9f7 100644
--- a/arch/x86/include/asm/acpi.h
+++ b/arch/x86/include/asm/acpi.h
@@ -227,7 +227,8 @@ extern int x86_acpi_numa_init(void);
struct cper_ia_proc_ctx;
#ifdef CONFIG_ACPI_APEI
-static inline pgprot_t arch_apei_get_mem_attribute(phys_addr_t addr)
+static inline pgprot_t arch_apei_get_mem_attribute(phys_addr_t addr,
+ phys_addr_t *end)
{
/*
* We currently have no way to look up the EFI memory map
@@ -242,6 +243,8 @@ static inline pgprot_t arch_apei_get_mem_attribute(phys_addr_t addr)
* is active, the ACPI information will not be encrypted,
* so return PAGE_KERNEL_NOENC until we know differently.
*/
+ if (end)
+ *end = (phys_addr_t)-1;
return PAGE_KERNEL_NOENC;
}
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index 663dc2e601cf..b4cd95d3e697 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -176,13 +176,12 @@ static struct gen_pool *ghes_estatus_pool;
static struct ghes_estatus_cache __rcu *ghes_estatus_caches[GHES_ESTATUS_CACHES_SIZE];
static atomic_t ghes_estatus_cache_alloced;
-static void __iomem *ghes_map(u64 pfn, enum fixed_addresses fixmap_idx)
+static void __iomem *ghes_map(u64 pfn, enum fixed_addresses fixmap_idx,
+ pgprot_t prot)
{
phys_addr_t paddr;
- pgprot_t prot;
paddr = PFN_PHYS(pfn);
- prot = arch_apei_get_mem_attribute(paddr);
__set_fixmap(fixmap_idx, paddr, prot);
return (void __iomem *) __fix_to_virt(fixmap_idx);
@@ -345,10 +344,28 @@ static void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len,
void __iomem *vaddr;
u64 offset;
u32 trunk;
+ phys_addr_t prot_end = 0;
+ pgprot_t prot = __pgprot(0);
while (len > 0) {
offset = paddr - (paddr & PAGE_MASK);
- vaddr = ghes_map(PHYS_PFN(paddr), fixmap_idx);
+ /*
+ * Cache the page protection to avoid an EFI descriptor
+ * lookup on every page. prot is valid for all addresses
+ * below prot_end.
+ */
+ if (paddr >= prot_end) {
+ prot = arch_apei_get_mem_attribute(paddr, &prot_end);
+ /*
+ * If the EFI lookup fails, *end is not written and
+ * prot_end stays at a stale value <= paddr. Restore
+ * the invariant prot_end > paddr so the cached prot
+ * remains valid for the current page.
+ */
+ if (prot_end <= paddr)
+ prot_end = paddr + PAGE_SIZE;
+ }
+ vaddr = ghes_map(PHYS_PFN(paddr), fixmap_idx, prot);
trunk = PAGE_SIZE - offset;
trunk = min(trunk, len);
if (from_phys)
--
2.20.1
More information about the linux-riscv
mailing list