From nobody Mon Jun 8 12:17:15 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 8B084352008; Fri, 29 May 2026 09:51:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780048281; cv=none; b=U7jpc4YC0RdonAbxOMD2+eCFOGzRXBIHIRbBuN/j0DX4lw85TjRpZOFc2M12lEGQcjDs5Kus67PTyfEUl9L6xiNgcG00yYHDDvAkKPBmgxjlYcQxLe0Ul0zxi+ow/F1UJAeQWrc9PDXvg5+eXpx71LNQ3FYzuyTEz3Yzxw4hJ78= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780048281; c=relaxed/simple; bh=gn6E4YUkBoMhlc2fzVF1rkrenull8ItDqZ35JVAlmpE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=L+e77blOEmUnY3t1gjkfLJzaewibRtBd76MAnncoiPJmZe9WtosyarMEVUPsEH1xLkifzSkeSFWAyqU6L0/DBSMyN6K70Qwj8Flpe+YSV174QbilcvOhvtwHiX+WS7BJRGvGarlpYcNOJuTonhvRh2rlr5mjXergTW16qnlNVbk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=q96/kYoz; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="q96/kYoz" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id DEB4232E5; Fri, 29 May 2026 02:51:13 -0700 (PDT) Received: from e134710.arm.com (e134710.arm.com [10.33.10.82]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 815533F905; Fri, 29 May 2026 02:51:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1780048278; bh=gn6E4YUkBoMhlc2fzVF1rkrenull8ItDqZ35JVAlmpE=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=q96/kYozvwR4cSpcfyIg1Y2epdEN4CXd2I6EmQJ3tlaTt11k6JM6b02uS7h5hvjEp lxW8AHjcetLYHEt8daat1PBMLmMvtqJP7T49/lHAxjLI3VJUGPIv3hCZ4ML0JUenUi lte7aorxs+wC/cXb/ErdTtMfi32HZDKeui8tO8Dw= From: Ahmed Tiba Date: Fri, 29 May 2026 10:50:41 +0100 Subject: [PATCH v5 01/10] ACPI: APEI: GHES: share macros via a private header Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-1-2e0500d42642@arm.com> References: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-0-2e0500d42642@arm.com> In-Reply-To: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-0-2e0500d42642@arm.com> To: will@kernel.org, jic23@kernel.org, xueshuai@linux.alibaba.com, saket.dumbre@intel.com, mchehab@kernel.org, dave@stgolabs.net, djbw@kernel.org, bp@alien8.de, tony.luck@intel.com, guohanjun@huawei.com, lenb@kernel.org, skhan@linuxfoundation.org, vishal.l.verma@intel.com, rafael@kernel.org, corbet@lwn.net, ira.weiny@intel.com, dave.jiang@intel.com, krzk+dt@kernel.org, robh@kernel.org, catalin.marinas@arm.com, alison.schofield@intel.com, conor+dt@kernel.org Cc: Ahmed Tiba , linux-arm-kernel@lists.infradead.org, Michael.Zhao2@arm.com, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-cxl@vger.kernel.org, Dmitry.Lamerov@arm.com, devicetree@vger.kernel.org, linux-acpi@vger.kernel.org, linux-edac@vger.kernel.org, acpica-devel@lists.linux.dev X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1780048269; l=10841; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=gn6E4YUkBoMhlc2fzVF1rkrenull8ItDqZ35JVAlmpE=; b=jgtGkbo+0W3nQoBUrxCyfd77zgGPJf/3Dvek7YEgGlmglXx/Z5A6dw6Q/CbIa5Ow9et9dPFO8 SAGmL/owT9QC1EUJzj2KGczmDahd9HUGHmCDFTy3w5srF728pXrEZTo X-Developer-Key: i=ahmed.tiba@arm.com; a=ed25519; pk=xVOtd+Qklh/4tuM3tB+BEZD4jj5a6W59C3KCNX6v7OE= Carve the CPER helper macros out of ghes.c and place them in a private header so they can be shared with upcoming helper files. This is a mechanical include change with no functional differences. Signed-off-by: Ahmed Tiba Reviewed-by: Jonathan Cameron --- drivers/acpi/apei/ghes.c | 94 +++++++++---------------------------------- include/acpi/ghes_cper.h | 102 +++++++++++++++++++++++++++++++++++++++++++= ++++ 2 files changed, 120 insertions(+), 76 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 3236a3ce79d6..4f67f46410c4 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -49,6 +49,7 @@ =20 #include #include +#include #include #include #include @@ -57,40 +58,6 @@ =20 #include "apei-internal.h" =20 -#define GHES_PFX "GHES: " - -#define GHES_ESTATUS_MAX_SIZE 65536 -#define GHES_ESOURCE_PREALLOC_MAX_SIZE 65536 - -#define GHES_ESTATUS_POOL_MIN_ALLOC_ORDER 3 - -/* This is just an estimation for memory pool allocation */ -#define GHES_ESTATUS_CACHE_AVG_SIZE 512 - -#define GHES_ESTATUS_CACHES_SIZE 4 - -#define GHES_ESTATUS_IN_CACHE_MAX_NSEC 10000000000ULL -/* Prevent too many caches are allocated because of RCU */ -#define GHES_ESTATUS_CACHE_ALLOCED_MAX (GHES_ESTATUS_CACHES_SIZE * 3 / 2) - -#define GHES_ESTATUS_CACHE_LEN(estatus_len) \ - (sizeof(struct ghes_estatus_cache) + (estatus_len)) -#define GHES_ESTATUS_FROM_CACHE(estatus_cache) \ - ((struct acpi_hest_generic_status *) \ - ((struct ghes_estatus_cache *)(estatus_cache) + 1)) - -#define GHES_ESTATUS_NODE_LEN(estatus_len) \ - (sizeof(struct ghes_estatus_node) + (estatus_len)) -#define GHES_ESTATUS_FROM_NODE(estatus_node) \ - ((struct acpi_hest_generic_status *) \ - ((struct ghes_estatus_node *)(estatus_node) + 1)) - -#define GHES_VENDOR_ENTRY_LEN(gdata_len) \ - (sizeof(struct ghes_vendor_record_entry) + (gdata_len)) -#define GHES_GDATA_FROM_VENDOR_ENTRY(vendor_entry) \ - ((struct acpi_hest_generic_data *) \ - ((struct ghes_vendor_record_entry *)(vendor_entry) + 1)) - /* * NMI-like notifications vary by architecture, before the compiler can p= rune * unused static functions it needs a value for these enums. @@ -102,25 +69,6 @@ =20 static ATOMIC_NOTIFIER_HEAD(ghes_report_chain); =20 -static inline bool is_hest_type_generic_v2(struct ghes *ghes) -{ - return ghes->generic->header.type =3D=3D ACPI_HEST_TYPE_GENERIC_ERROR_V2; -} - -/* - * A platform may describe one error source for the handling of synchronous - * errors (e.g. MCE or SEA), or for handling asynchronous errors (e.g. SCI - * or External Interrupt). On x86, the HEST notifications are always - * asynchronous, so only SEA on ARM is delivered as a synchronous - * notification. - */ -static inline bool is_hest_sync_notify(struct ghes *ghes) -{ - u8 notify_type =3D ghes->generic->notify.type; - - return notify_type =3D=3D ACPI_HEST_NOTIFY_SEA; -} - /* * This driver isn't really modular, however for the time being, * continuing to use module_param is the easiest way to remain @@ -165,12 +113,6 @@ static DEFINE_MUTEX(ghes_devs_mutex); */ static DEFINE_SPINLOCK(ghes_notify_lock_irq); =20 -struct ghes_vendor_record_entry { - struct work_struct work; - int error_severity; - char vendor_record[]; -}; - static struct gen_pool *ghes_estatus_pool; =20 static struct ghes_estatus_cache __rcu *ghes_estatus_caches[GHES_ESTATUS_C= ACHES_SIZE]; @@ -266,7 +208,7 @@ static void ghes_ack_error(struct acpi_hest_generic_v2 = *gv2) apei_write(val, &gv2->read_ack_register); } =20 -static struct ghes *ghes_new(struct acpi_hest_generic *generic) +struct ghes *ghes_new(struct acpi_hest_generic *generic) { struct ghes *ghes; unsigned int error_block_length; @@ -313,7 +255,7 @@ static struct ghes *ghes_new(struct acpi_hest_generic *= generic) return ERR_PTR(rc); } =20 -static void ghes_fini(struct ghes *ghes) +void ghes_fini(struct ghes *ghes) { kfree(ghes->estatus); apei_unmap_generic_address(&ghes->generic->error_status_address); @@ -363,8 +305,8 @@ static void ghes_copy_tofrom_phys(void *buffer, u64 pad= dr, u32 len, } =20 /* Check the top-level record header has an appropriate size. */ -static int __ghes_check_estatus(struct ghes *ghes, - struct acpi_hest_generic_status *estatus) +int __ghes_check_estatus(struct ghes *ghes, + struct acpi_hest_generic_status *estatus) { u32 len =3D cper_estatus_len(estatus); u32 max_len =3D min(ghes->generic->error_block_length, @@ -389,9 +331,9 @@ static int __ghes_check_estatus(struct ghes *ghes, } =20 /* Read the CPER block, returning its address, and header in estatus. */ -static int __ghes_peek_estatus(struct ghes *ghes, - struct acpi_hest_generic_status *estatus, - u64 *buf_paddr, enum fixed_addresses fixmap_idx) +int __ghes_peek_estatus(struct ghes *ghes, + struct acpi_hest_generic_status *estatus, + u64 *buf_paddr, enum fixed_addresses fixmap_idx) { struct acpi_hest_generic *g =3D ghes->generic; int rc; @@ -400,7 +342,7 @@ static int __ghes_peek_estatus(struct ghes *ghes, if (rc) { *buf_paddr =3D 0; pr_warn_ratelimited(FW_WARN GHES_PFX -"Failed to read error status block address for hardware error source: %d.\= n", + "Failed to read error status block address for hardware error sour= ce: %d.\n", g->header.source_id); return -EIO; } @@ -417,9 +359,9 @@ static int __ghes_peek_estatus(struct ghes *ghes, return 0; } =20 -static int __ghes_read_estatus(struct acpi_hest_generic_status *estatus, - u64 buf_paddr, enum fixed_addresses fixmap_idx, - size_t buf_len) +int __ghes_read_estatus(struct acpi_hest_generic_status *estatus, + u64 buf_paddr, enum fixed_addresses fixmap_idx, + size_t buf_len) { ghes_copy_tofrom_phys(estatus, buf_paddr, buf_len, 1, fixmap_idx); if (cper_estatus_check(estatus)) { @@ -431,9 +373,9 @@ static int __ghes_read_estatus(struct acpi_hest_generic= _status *estatus, return 0; } =20 -static int ghes_read_estatus(struct ghes *ghes, - struct acpi_hest_generic_status *estatus, - u64 *buf_paddr, enum fixed_addresses fixmap_idx) +int ghes_read_estatus(struct ghes *ghes, + struct acpi_hest_generic_status *estatus, + u64 *buf_paddr, enum fixed_addresses fixmap_idx) { int rc; =20 @@ -449,9 +391,9 @@ static int ghes_read_estatus(struct ghes *ghes, cper_estatus_len(estatus)); } =20 -static void ghes_clear_estatus(struct ghes *ghes, - struct acpi_hest_generic_status *estatus, - u64 buf_paddr, enum fixed_addresses fixmap_idx) +void ghes_clear_estatus(struct ghes *ghes, + struct acpi_hest_generic_status *estatus, + u64 buf_paddr, enum fixed_addresses fixmap_idx) { estatus->block_status =3D 0; =20 diff --git a/include/acpi/ghes_cper.h b/include/acpi/ghes_cper.h new file mode 100644 index 000000000000..6b7632cfaf66 --- /dev/null +++ b/include/acpi/ghes_cper.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * GHES declarations used by both the ACPI APEI GHES driver + * and the firmware-first CPER provider. + * + * These declarations lets GHES and other firmware-first error sources use + * the same helper so the non-ACPI path follows the same + * behavior as GHES instead of carrying a separate copy. + * + * Derived from the ACPI APEI GHES driver. + * + * Copyright 2010,2011 Intel Corp. + * Author: Huang Ying + */ + +#ifndef ACPI_APEI_GHES_CPER_H +#define ACPI_APEI_GHES_CPER_H + +#include + +#include + +#define GHES_PFX "GHES: " + +#define GHES_ESTATUS_MAX_SIZE 65536 +#define GHES_ESOURCE_PREALLOC_MAX_SIZE 65536 + +#define GHES_ESTATUS_POOL_MIN_ALLOC_ORDER 3 + +/* This is just an estimation for memory pool allocation */ +#define GHES_ESTATUS_CACHE_AVG_SIZE 512 + +#define GHES_ESTATUS_CACHES_SIZE 4 + +#define GHES_ESTATUS_IN_CACHE_MAX_NSEC 10000000000ULL +/* Prevent too many caches are allocated because of RCU */ +#define GHES_ESTATUS_CACHE_ALLOCED_MAX (GHES_ESTATUS_CACHES_SIZE * 3 / 2) + +#define GHES_ESTATUS_CACHE_LEN(estatus_len) \ + (sizeof(struct ghes_estatus_cache) + (estatus_len)) +#define GHES_ESTATUS_FROM_CACHE(estatus_cache) \ + ((struct acpi_hest_generic_status *) \ + ((struct ghes_estatus_cache *)(estatus_cache) + 1)) + +#define GHES_ESTATUS_NODE_LEN(estatus_len) \ + (sizeof(struct ghes_estatus_node) + (estatus_len)) +#define GHES_ESTATUS_FROM_NODE(estatus_node) \ + ((struct acpi_hest_generic_status *) \ + ((struct ghes_estatus_node *)(estatus_node) + 1)) + +#define GHES_VENDOR_ENTRY_LEN(gdata_len) \ + (sizeof(struct ghes_vendor_record_entry) + (gdata_len)) +#define GHES_GDATA_FROM_VENDOR_ENTRY(vendor_entry) \ + ((struct acpi_hest_generic_data *) \ + ((struct ghes_vendor_record_entry *)(vendor_entry) + 1)) + +static inline bool is_hest_type_generic_v2(struct ghes *ghes) +{ + return ghes->generic->header.type =3D=3D ACPI_HEST_TYPE_GENERIC_ERROR_V2; +} + +/* + * A platform may describe one error source for the handling of synchronous + * errors (e.g. MCE or SEA), or for handling asynchronous errors (e.g. SCI + * or External Interrupt). On x86, the HEST notifications are always + * asynchronous, so only SEA on ARM is delivered as a synchronous + * notification. + */ +static inline bool is_hest_sync_notify(struct ghes *ghes) +{ + u8 notify_type =3D ghes->generic->notify.type; + + return notify_type =3D=3D ACPI_HEST_NOTIFY_SEA; +} + +struct ghes_vendor_record_entry { + struct work_struct work; + int error_severity; + char vendor_record[]; +}; + +#ifdef CONFIG_ACPI_APEI +struct ghes *ghes_new(struct acpi_hest_generic *generic); +void ghes_fini(struct ghes *ghes); + +int ghes_read_estatus(struct ghes *ghes, + struct acpi_hest_generic_status *estatus, + u64 *buf_paddr, enum fixed_addresses fixmap_idx); +void ghes_clear_estatus(struct ghes *ghes, + struct acpi_hest_generic_status *estatus, + u64 buf_paddr, enum fixed_addresses fixmap_idx); +int __ghes_peek_estatus(struct ghes *ghes, + struct acpi_hest_generic_status *estatus, + u64 *buf_paddr, enum fixed_addresses fixmap_idx); +int __ghes_check_estatus(struct ghes *ghes, + struct acpi_hest_generic_status *estatus); +int __ghes_read_estatus(struct acpi_hest_generic_status *estatus, + u64 buf_paddr, enum fixed_addresses fixmap_idx, + size_t buf_len); +#endif + +#endif /* ACPI_APEI_GHES_CPER_H */ --=20 2.43.0 From nobody Mon Jun 8 12:17:15 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 389A43C4B85; Fri, 29 May 2026 09:51:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780048286; cv=none; b=peJgQv4yRuFjRfoT17AO58ICNJPbvwOtiaE9F5YCAMG9erE91u+zln4ih0o6lkHyfxmJMgbC8Yb2TvksHVMIjfgRfmnhDTxNAoFFetQVfVP41XbWopy+A6/OguwBrxAFAE4Hr1Yw35KteJ4sZ2To5AA3wwv3IKEzPMPPQR0HI1Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780048286; c=relaxed/simple; bh=JSh+Q/vHoSFoymgLe3sKdFZMl/TXUZwnf/sWaV43igU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=tOR1QnMmJeyQciVy/xwKSy0BdYx4JFMEKIzdiGkdVykCAOcwoXv2Q7YoaqEziFsaz0fdfLZDO8A+KEH3Y8Jo9D8kc4qDKB6emIByF/66OQ4ZpMwdI8J+FbXIpU5qezqj98R+LlrwWekCOU2rvVaMK6BtCOaa5VwbqbsupGcM/SA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=J5CzN77G; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="J5CzN77G" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 98A1322D7; Fri, 29 May 2026 02:51:18 -0700 (PDT) Received: from e134710.arm.com (e134710.arm.com [10.33.10.82]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 3C04E3F905; Fri, 29 May 2026 02:51:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1780048283; bh=JSh+Q/vHoSFoymgLe3sKdFZMl/TXUZwnf/sWaV43igU=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=J5CzN77GJ1q/UgidhWIM+/D0Bqrf/ebH3t/RWF09PTBJA+B22pC9ZXc1Nqi6dVsG8 RhhPnmUS9x+C4+FERStjxNzypTj5XJsIUXbj8nGoovpHfiqH1/MC67SG/QaWeYXvIJ Z/uKEeldVj4rl8mA+ZUDyo/ZexQX3kft+hRSAAXU= From: Ahmed Tiba Date: Fri, 29 May 2026 10:50:42 +0100 Subject: [PATCH v5 02/10] ACPI: APEI: GHES: move CPER read helpers Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-2-2e0500d42642@arm.com> References: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-0-2e0500d42642@arm.com> In-Reply-To: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-0-2e0500d42642@arm.com> To: will@kernel.org, jic23@kernel.org, xueshuai@linux.alibaba.com, saket.dumbre@intel.com, mchehab@kernel.org, dave@stgolabs.net, djbw@kernel.org, bp@alien8.de, tony.luck@intel.com, guohanjun@huawei.com, lenb@kernel.org, skhan@linuxfoundation.org, vishal.l.verma@intel.com, rafael@kernel.org, corbet@lwn.net, ira.weiny@intel.com, dave.jiang@intel.com, krzk+dt@kernel.org, robh@kernel.org, catalin.marinas@arm.com, alison.schofield@intel.com, conor+dt@kernel.org Cc: Ahmed Tiba , linux-arm-kernel@lists.infradead.org, Michael.Zhao2@arm.com, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-cxl@vger.kernel.org, Dmitry.Lamerov@arm.com, devicetree@vger.kernel.org, linux-acpi@vger.kernel.org, linux-edac@vger.kernel.org, acpica-devel@lists.linux.dev X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1780048269; l=11668; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=JSh+Q/vHoSFoymgLe3sKdFZMl/TXUZwnf/sWaV43igU=; b=72sSCnTP0JCxDW2O3TNFh4CamrmbaDP5qXwVyM9aTLWM924wpPU7+mLvoWX7nDH9m7dYEtEwQ 3dS4uScboVSDRlsFMEi7+h6dd89WbfAL92oYfdTNyiXvxAbsdZmRyno X-Developer-Key: i=ahmed.tiba@arm.com; a=ed25519; pk=xVOtd+Qklh/4tuM3tB+BEZD4jj5a6W59C3KCNX6v7OE= Relocate the CPER buffer mapping, peek, and clear helpers from ghes.c into ghes_cper.c so they can be shared with other firmware-first providers. This commit only shuffles code; behavior stays the same. Signed-off-by: Ahmed Tiba Reviewed-by: Jonathan Cameron --- drivers/acpi/apei/Makefile | 2 +- drivers/acpi/apei/ghes.c | 166 ----------------------------------- drivers/acpi/apei/ghes_cper.c | 195 ++++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 196 insertions(+), 167 deletions(-) diff --git a/drivers/acpi/apei/Makefile b/drivers/acpi/apei/Makefile index 66588d6be56f..f57f3b009d8e 100644 --- a/drivers/acpi/apei/Makefile +++ b/drivers/acpi/apei/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_ACPI_APEI) +=3D apei.o -obj-$(CONFIG_ACPI_APEI_GHES) +=3D ghes.o +obj-$(CONFIG_ACPI_APEI_GHES) +=3D ghes.o ghes_cper.o # clang versions prior to 18 may blow out the stack with KASAN ifeq ($(CONFIG_COMPILE_TEST)_$(CONFIG_CC_IS_CLANG)_$(call clang-min-versio= n, 180000),y_y_) KASAN_SANITIZE_ghes.o :=3D n diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 4f67f46410c4..3f35580e8efd 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -118,26 +118,6 @@ static struct gen_pool *ghes_estatus_pool; static struct ghes_estatus_cache __rcu *ghes_estatus_caches[GHES_ESTATUS_C= ACHES_SIZE]; static atomic_t ghes_estatus_cache_alloced; =20 -static void __iomem *ghes_map(u64 pfn, enum fixed_addresses fixmap_idx) -{ - phys_addr_t paddr; - pgprot_t prot; - - paddr =3D PFN_PHYS(pfn); - prot =3D arch_apei_get_mem_attribute(paddr); - __set_fixmap(fixmap_idx, paddr, prot); - - return (void __iomem *) __fix_to_virt(fixmap_idx); -} - -static void ghes_unmap(void __iomem *vaddr, enum fixed_addresses fixmap_id= x) -{ - int _idx =3D virt_to_fix((unsigned long)vaddr); - - WARN_ON_ONCE(fixmap_idx !=3D _idx); - clear_fixmap(fixmap_idx); -} - int ghes_estatus_pool_init(unsigned int num_ghes) { unsigned long addr, len; @@ -193,21 +173,6 @@ static void unmap_gen_v2(struct ghes *ghes) apei_unmap_generic_address(&ghes->generic_v2->read_ack_register); } =20 -static void ghes_ack_error(struct acpi_hest_generic_v2 *gv2) -{ - int rc; - u64 val =3D 0; - - rc =3D apei_read(&val, &gv2->read_ack_register); - if (rc) - return; - - val &=3D gv2->read_ack_preserve << gv2->read_ack_register.bit_offset; - val |=3D gv2->read_ack_write << gv2->read_ack_register.bit_offset; - - apei_write(val, &gv2->read_ack_register); -} - struct ghes *ghes_new(struct acpi_hest_generic *generic) { struct ghes *ghes; @@ -280,137 +245,6 @@ static inline int ghes_severity(int severity) } } =20 -static void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len, - int from_phys, - enum fixed_addresses fixmap_idx) -{ - void __iomem *vaddr; - u64 offset; - u32 trunk; - - while (len > 0) { - offset =3D paddr - (paddr & PAGE_MASK); - vaddr =3D ghes_map(PHYS_PFN(paddr), fixmap_idx); - trunk =3D PAGE_SIZE - offset; - trunk =3D min(trunk, len); - if (from_phys) - memcpy_fromio(buffer, vaddr + offset, trunk); - else - memcpy_toio(vaddr + offset, buffer, trunk); - len -=3D trunk; - paddr +=3D trunk; - buffer +=3D trunk; - ghes_unmap(vaddr, fixmap_idx); - } -} - -/* Check the top-level record header has an appropriate size. */ -int __ghes_check_estatus(struct ghes *ghes, - struct acpi_hest_generic_status *estatus) -{ - u32 len =3D cper_estatus_len(estatus); - u32 max_len =3D min(ghes->generic->error_block_length, - ghes->estatus_length); - - if (len < sizeof(*estatus)) { - pr_warn_ratelimited(FW_WARN GHES_PFX "Truncated error status block!\n"); - return -EIO; - } - - if (!len || len > max_len) { - pr_warn_ratelimited(FW_WARN GHES_PFX "Invalid error status block length!= \n"); - return -EIO; - } - - if (cper_estatus_check_header(estatus)) { - pr_warn_ratelimited(FW_WARN GHES_PFX "Invalid CPER header!\n"); - return -EIO; - } - - return 0; -} - -/* Read the CPER block, returning its address, and header in estatus. */ -int __ghes_peek_estatus(struct ghes *ghes, - struct acpi_hest_generic_status *estatus, - u64 *buf_paddr, enum fixed_addresses fixmap_idx) -{ - struct acpi_hest_generic *g =3D ghes->generic; - int rc; - - rc =3D apei_read(buf_paddr, &g->error_status_address); - if (rc) { - *buf_paddr =3D 0; - pr_warn_ratelimited(FW_WARN GHES_PFX - "Failed to read error status block address for hardware error sour= ce: %d.\n", - g->header.source_id); - return -EIO; - } - if (!*buf_paddr) - return -ENOENT; - - ghes_copy_tofrom_phys(estatus, *buf_paddr, sizeof(*estatus), 1, - fixmap_idx); - if (!estatus->block_status) { - *buf_paddr =3D 0; - return -ENOENT; - } - - return 0; -} - -int __ghes_read_estatus(struct acpi_hest_generic_status *estatus, - u64 buf_paddr, enum fixed_addresses fixmap_idx, - size_t buf_len) -{ - ghes_copy_tofrom_phys(estatus, buf_paddr, buf_len, 1, fixmap_idx); - if (cper_estatus_check(estatus)) { - pr_warn_ratelimited(FW_WARN GHES_PFX - "Failed to read error status block!\n"); - return -EIO; - } - - return 0; -} - -int ghes_read_estatus(struct ghes *ghes, - struct acpi_hest_generic_status *estatus, - u64 *buf_paddr, enum fixed_addresses fixmap_idx) -{ - int rc; - - rc =3D __ghes_peek_estatus(ghes, estatus, buf_paddr, fixmap_idx); - if (rc) - return rc; - - rc =3D __ghes_check_estatus(ghes, estatus); - if (rc) - return rc; - - return __ghes_read_estatus(estatus, *buf_paddr, fixmap_idx, - cper_estatus_len(estatus)); -} - -void ghes_clear_estatus(struct ghes *ghes, - struct acpi_hest_generic_status *estatus, - u64 buf_paddr, enum fixed_addresses fixmap_idx) -{ - estatus->block_status =3D 0; - - if (!buf_paddr) - return; - - ghes_copy_tofrom_phys(estatus, buf_paddr, - sizeof(estatus->block_status), 0, - fixmap_idx); - - /* - * GHESv2 type HEST entries introduce support for error acknowledgment, - * so only acknowledge the error if this support is present. - */ - if (is_hest_type_generic_v2(ghes)) - ghes_ack_error(ghes->generic_v2); -} =20 /** * struct ghes_task_work - for synchronous RAS event diff --git a/drivers/acpi/apei/ghes_cper.c b/drivers/acpi/apei/ghes_cper.c new file mode 100644 index 000000000000..7bb72fe57838 --- /dev/null +++ b/drivers/acpi/apei/ghes_cper.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Shared GHES helpers for firmware-first CPER error handling. + * + * This file holds the GHES helper code that is shared by the in-tree GHES + * driver and by other firmware-first error sources that reuse the same CP= ER + * handling flow. + * + * Derived from the ACPI APEI GHES driver. + * + * Copyright 2010,2011 Intel Corp. + * Author: Huang Ying + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "apei-internal.h" + +static void __iomem *ghes_map(u64 pfn, enum fixed_addresses fixmap_idx) +{ + phys_addr_t paddr; + pgprot_t prot; + + paddr =3D PFN_PHYS(pfn); + prot =3D arch_apei_get_mem_attribute(paddr); + __set_fixmap(fixmap_idx, paddr, prot); + + return (void __iomem *) __fix_to_virt(fixmap_idx); +} + +static void ghes_unmap(void __iomem *vaddr, enum fixed_addresses fixmap_id= x) +{ + int _idx =3D virt_to_fix((unsigned long)vaddr); + + WARN_ON_ONCE(fixmap_idx !=3D _idx); + clear_fixmap(fixmap_idx); +} + +static void ghes_ack_error(struct acpi_hest_generic_v2 *gv2) +{ + int rc; + u64 val =3D 0; + + rc =3D apei_read(&val, &gv2->read_ack_register); + if (rc) + return; + + val &=3D gv2->read_ack_preserve << gv2->read_ack_register.bit_offset; + val |=3D gv2->read_ack_write << gv2->read_ack_register.bit_offset; + + apei_write(val, &gv2->read_ack_register); +} + +static void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len, + int from_phys, + enum fixed_addresses fixmap_idx) +{ + void __iomem *vaddr; + u64 offset; + u32 trunk; + + while (len > 0) { + offset =3D paddr - (paddr & PAGE_MASK); + vaddr =3D ghes_map(PHYS_PFN(paddr), fixmap_idx); + trunk =3D PAGE_SIZE - offset; + trunk =3D min(trunk, len); + if (from_phys) + memcpy_fromio(buffer, vaddr + offset, trunk); + else + memcpy_toio(vaddr + offset, buffer, trunk); + len -=3D trunk; + paddr +=3D trunk; + buffer +=3D trunk; + ghes_unmap(vaddr, fixmap_idx); + } +} + +/* Check the top-level record header has an appropriate size. */ +int __ghes_check_estatus(struct ghes *ghes, + struct acpi_hest_generic_status *estatus) +{ + u32 len =3D cper_estatus_len(estatus); + u32 max_len =3D min(ghes->generic->error_block_length, + ghes->estatus_length); + + if (len < sizeof(*estatus)) { + pr_warn_ratelimited(FW_WARN GHES_PFX "Truncated error status block!\n"); + return -EIO; + } + + if (!len || len > max_len) { + pr_warn_ratelimited(FW_WARN GHES_PFX "Invalid error status block length!= \n"); + return -EIO; + } + + if (cper_estatus_check_header(estatus)) { + pr_warn_ratelimited(FW_WARN GHES_PFX "Invalid CPER header!\n"); + return -EIO; + } + + return 0; +} + +/* Read the CPER block, returning its address, and header in estatus. */ +int __ghes_peek_estatus(struct ghes *ghes, + struct acpi_hest_generic_status *estatus, + u64 *buf_paddr, enum fixed_addresses fixmap_idx) +{ + struct acpi_hest_generic *g =3D ghes->generic; + int rc; + + rc =3D apei_read(buf_paddr, &g->error_status_address); + if (rc) { + *buf_paddr =3D 0; + pr_warn_ratelimited(FW_WARN GHES_PFX + "Failed to read error status block address for hardware error sour= ce: %d.\n", + g->header.source_id); + return -EIO; + } + if (!*buf_paddr) + return -ENOENT; + + ghes_copy_tofrom_phys(estatus, *buf_paddr, sizeof(*estatus), 1, + fixmap_idx); + if (!estatus->block_status) { + *buf_paddr =3D 0; + return -ENOENT; + } + + return 0; +} + +int __ghes_read_estatus(struct acpi_hest_generic_status *estatus, + u64 buf_paddr, enum fixed_addresses fixmap_idx, + size_t buf_len) +{ + ghes_copy_tofrom_phys(estatus, buf_paddr, buf_len, 1, fixmap_idx); + if (cper_estatus_check(estatus)) { + pr_warn_ratelimited(FW_WARN GHES_PFX + "Failed to read error status block!\n"); + return -EIO; + } + + return 0; +} + +int ghes_read_estatus(struct ghes *ghes, + struct acpi_hest_generic_status *estatus, + u64 *buf_paddr, enum fixed_addresses fixmap_idx) +{ + int rc; + + rc =3D __ghes_peek_estatus(ghes, estatus, buf_paddr, fixmap_idx); + if (rc) + return rc; + + rc =3D __ghes_check_estatus(ghes, estatus); + if (rc) + return rc; + + return __ghes_read_estatus(estatus, *buf_paddr, fixmap_idx, + cper_estatus_len(estatus)); +} + +void ghes_clear_estatus(struct ghes *ghes, + struct acpi_hest_generic_status *estatus, + u64 buf_paddr, enum fixed_addresses fixmap_idx) +{ + estatus->block_status =3D 0; + + if (!buf_paddr) + return; + + ghes_copy_tofrom_phys(estatus, buf_paddr, + sizeof(estatus->block_status), 0, + fixmap_idx); + + /* + * GHESv2 type HEST entries introduce support for error acknowledgment, + * so only acknowledge the error if this support is present. + */ + if (is_hest_type_generic_v2(ghes)) + ghes_ack_error(ghes->generic_v2); +} --=20 2.43.0 From nobody Mon Jun 8 12:17:15 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id E120F3BCD10; Fri, 29 May 2026 09:51:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780048291; cv=none; b=IwRahztcKG8z5stbSx8G1nJRtyykkB1Ia0z/kloCUzydqh2NpA/DPeSfY1LPMITbnNBlFqqtYu43wvOYIh7AszANJRUrHtC5ERe7Rw7MZUtpM7/PipIvMpyD7R9IDpfm/emhgWvOwcI8uZ1Nl7HG44j7cBkLAIlBY8Ptf9eEp48= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780048291; c=relaxed/simple; bh=VBlXOTXQbKAdsShF6qFXMsgKYyrtlFR0YPM4M0Q4J+4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=StA9XdUDlHnwH6r8z0Zva3LRmu5l7kfbibfBX62689F+KemW7WugoZuJC5SvKmpzOkhWHHEB1X0Gp6SNoiuovlwI7rKnZCipT7mvWPp5bHmNPM9nD/YYdYA0G6OzNCqLyP6x4xzjt8qQX8mre3fckJGlf+xHlh9G0xWCLrGymdQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=B4irZ3Z4; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="B4irZ3Z4" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 5438132E5; Fri, 29 May 2026 02:51:23 -0700 (PDT) Received: from e134710.arm.com (e134710.arm.com [10.33.10.82]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id E7DE23F905; Fri, 29 May 2026 02:51:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1780048288; bh=VBlXOTXQbKAdsShF6qFXMsgKYyrtlFR0YPM4M0Q4J+4=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=B4irZ3Z4/zfk0EBeRDsa/WC/W5U5zyLZKeSj8pIUCTaeWyiHuZK2T6hhOsssIzI2J uKFz1Qg+ZRViPXJzaOaEsHuTeAmrI7BDU5gWnllKWp7PXHp+eoWPQDzCdfHiBsulOQ L0Q4umz2ph3b22RfxH7/4DA2p2MdCeHitrCVLrzU= From: Ahmed Tiba Date: Fri, 29 May 2026 10:50:43 +0100 Subject: [PATCH v5 03/10] ACPI: APEI: GHES: move GHESv2 ack and alloc helpers Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-3-2e0500d42642@arm.com> References: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-0-2e0500d42642@arm.com> In-Reply-To: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-0-2e0500d42642@arm.com> To: will@kernel.org, jic23@kernel.org, xueshuai@linux.alibaba.com, saket.dumbre@intel.com, mchehab@kernel.org, dave@stgolabs.net, djbw@kernel.org, bp@alien8.de, tony.luck@intel.com, guohanjun@huawei.com, lenb@kernel.org, skhan@linuxfoundation.org, vishal.l.verma@intel.com, rafael@kernel.org, corbet@lwn.net, ira.weiny@intel.com, dave.jiang@intel.com, krzk+dt@kernel.org, robh@kernel.org, catalin.marinas@arm.com, alison.schofield@intel.com, conor+dt@kernel.org Cc: Ahmed Tiba , linux-arm-kernel@lists.infradead.org, Michael.Zhao2@arm.com, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-cxl@vger.kernel.org, Dmitry.Lamerov@arm.com, devicetree@vger.kernel.org, linux-acpi@vger.kernel.org, linux-edac@vger.kernel.org, acpica-devel@lists.linux.dev X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1780048269; l=4727; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=VBlXOTXQbKAdsShF6qFXMsgKYyrtlFR0YPM4M0Q4J+4=; b=UJCFPKdH2IlyS25gSMshUk/Zhz1uXbuPB5ZLh9cN+UWLZeQvUTAEEAolq+bBCezwHYeCELlo/ Q6TewpzQghHD+7sIOe+8rhHVQDivdsgQYaOslhm18D2QBD5EZw9OEkW X-Developer-Key: i=ahmed.tiba@arm.com; a=ed25519; pk=xVOtd+Qklh/4tuM3tB+BEZD4jj5a6W59C3KCNX6v7OE= Move the GHESv2 acknowledgment and error-source allocation helpers from ghes.c into ghes_cper.c. This is a mechanical refactor that keeps the logic unchanged while making the helpers reusable. Signed-off-by: Ahmed Tiba Reviewed-by: Jonathan Cameron --- drivers/acpi/apei/ghes.c | 65 ---------------------------------------= ---- drivers/acpi/apei/ghes_cper.c | 65 +++++++++++++++++++++++++++++++++++++++= ++++ 2 files changed, 65 insertions(+), 65 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 3f35580e8efd..91638ae7e05e 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -163,71 +163,6 @@ void ghes_estatus_pool_region_free(unsigned long addr,= u32 size) } EXPORT_SYMBOL_GPL(ghes_estatus_pool_region_free); =20 -static int map_gen_v2(struct ghes *ghes) -{ - return apei_map_generic_address(&ghes->generic_v2->read_ack_register); -} - -static void unmap_gen_v2(struct ghes *ghes) -{ - apei_unmap_generic_address(&ghes->generic_v2->read_ack_register); -} - -struct ghes *ghes_new(struct acpi_hest_generic *generic) -{ - struct ghes *ghes; - unsigned int error_block_length; - int rc; - - ghes =3D kzalloc_obj(*ghes); - if (!ghes) - return ERR_PTR(-ENOMEM); - - ghes->generic =3D generic; - if (is_hest_type_generic_v2(ghes)) { - rc =3D map_gen_v2(ghes); - if (rc) - goto err_free; - } - - rc =3D apei_map_generic_address(&generic->error_status_address); - if (rc) - goto err_unmap_read_ack_addr; - error_block_length =3D generic->error_block_length; - if (error_block_length > GHES_ESTATUS_MAX_SIZE) { - pr_warn(FW_WARN GHES_PFX - "Error status block length is too long: %u for " - "generic hardware error source: %d.\n", - error_block_length, generic->header.source_id); - error_block_length =3D GHES_ESTATUS_MAX_SIZE; - } - ghes->estatus =3D kmalloc(error_block_length, GFP_KERNEL); - ghes->estatus_length =3D error_block_length; - if (!ghes->estatus) { - rc =3D -ENOMEM; - goto err_unmap_status_addr; - } - - return ghes; - -err_unmap_status_addr: - apei_unmap_generic_address(&generic->error_status_address); -err_unmap_read_ack_addr: - if (is_hest_type_generic_v2(ghes)) - unmap_gen_v2(ghes); -err_free: - kfree(ghes); - return ERR_PTR(rc); -} - -void ghes_fini(struct ghes *ghes) -{ - kfree(ghes->estatus); - apei_unmap_generic_address(&ghes->generic->error_status_address); - if (is_hest_type_generic_v2(ghes)) - unmap_gen_v2(ghes); -} - static inline int ghes_severity(int severity) { switch (severity) { diff --git a/drivers/acpi/apei/ghes_cper.c b/drivers/acpi/apei/ghes_cper.c index 7bb72fe57838..8080e0f76dac 100644 --- a/drivers/acpi/apei/ghes_cper.c +++ b/drivers/acpi/apei/ghes_cper.c @@ -62,6 +62,71 @@ static void ghes_ack_error(struct acpi_hest_generic_v2 *= gv2) apei_write(val, &gv2->read_ack_register); } =20 +static int map_gen_v2(struct ghes *ghes) +{ + return apei_map_generic_address(&ghes->generic_v2->read_ack_register); +} + +static void unmap_gen_v2(struct ghes *ghes) +{ + apei_unmap_generic_address(&ghes->generic_v2->read_ack_register); +} + +struct ghes *ghes_new(struct acpi_hest_generic *generic) +{ + struct ghes *ghes; + unsigned int error_block_length; + int rc; + + ghes =3D kzalloc_obj(*ghes); + if (!ghes) + return ERR_PTR(-ENOMEM); + + ghes->generic =3D generic; + if (is_hest_type_generic_v2(ghes)) { + rc =3D map_gen_v2(ghes); + if (rc) + goto err_free; + } + + rc =3D apei_map_generic_address(&generic->error_status_address); + if (rc) + goto err_unmap_read_ack_addr; + error_block_length =3D generic->error_block_length; + if (error_block_length > GHES_ESTATUS_MAX_SIZE) { + pr_warn(FW_WARN GHES_PFX + "Error status block length is too long: %u for " + "generic hardware error source: %d.\n", + error_block_length, generic->header.source_id); + error_block_length =3D GHES_ESTATUS_MAX_SIZE; + } + ghes->estatus =3D kmalloc(error_block_length, GFP_KERNEL); + ghes->estatus_length =3D error_block_length; + if (!ghes->estatus) { + rc =3D -ENOMEM; + goto err_unmap_status_addr; + } + + return ghes; + +err_unmap_status_addr: + apei_unmap_generic_address(&generic->error_status_address); +err_unmap_read_ack_addr: + if (is_hest_type_generic_v2(ghes)) + unmap_gen_v2(ghes); +err_free: + kfree(ghes); + return ERR_PTR(rc); +} + +void ghes_fini(struct ghes *ghes) +{ + kfree(ghes->estatus); + apei_unmap_generic_address(&ghes->generic->error_status_address); + if (is_hest_type_generic_v2(ghes)) + unmap_gen_v2(ghes); +} + static void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len, int from_phys, enum fixed_addresses fixmap_idx) --=20 2.43.0 From nobody Mon Jun 8 12:17:15 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id AB3DC3C9891; Fri, 29 May 2026 09:51:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780048297; cv=none; b=SgtQbc5X555c01mCIPEqiV2Bn9pkoNsKixuefR5NZoRUTWcFnqV0HXld+4QbeaP1bjBhNTW1xYqIujAAdWv97ytDKhjVWCsBIWO0DqaA2Br6ZZQ3osRQnk0izsVv0h5g0NxHwCFov6OrMAMDLBqNKJqAndEBOPCA+3EFG3TZnJU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780048297; c=relaxed/simple; bh=lFx22FK8BwhqLRtXVDa7n9zPnHQJet90r0IFTaX1i1s=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=kyGFsMBmUs6UJKED+t91d+6/jlarsAmuMtWT963acr9zVBqZ8YZ69D3X2PC6CeQyhexIXNrqRbQ2xUwGsshMf1z3T7f2fx8Ht2zXjw93LQAzcF6UTiajLG9ChYRGVNuwKM18Jhs2IwNEsVm1kme6zC1xoZBeqQOzaQRnl6xuExA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=IKIZrNKz; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="IKIZrNKz" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 0933522D7; Fri, 29 May 2026 02:51:28 -0700 (PDT) Received: from e134710.arm.com (e134710.arm.com [10.33.10.82]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id A2FC93F905; Fri, 29 May 2026 02:51:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1780048293; bh=lFx22FK8BwhqLRtXVDa7n9zPnHQJet90r0IFTaX1i1s=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=IKIZrNKz1w5bbzAPhqxgS4wkKjGAwtbT64bmt5iXfcU2V2FonliMgD0QXyZIsz8do QqhOMjAZUB9HtWcKoOkUS/sOPFDkdMlXHYftEqnx14WG9MQcqD70dkCbfRtsEUWavX kW1hoH9ygE6OtKkBfOrOsVvX3Rme1CHVYOV/DHyw= From: Ahmed Tiba Date: Fri, 29 May 2026 10:50:44 +0100 Subject: [PATCH v5 04/10] ACPI: APEI: GHES: move estatus cache helpers Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-4-2e0500d42642@arm.com> References: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-0-2e0500d42642@arm.com> In-Reply-To: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-0-2e0500d42642@arm.com> To: will@kernel.org, jic23@kernel.org, xueshuai@linux.alibaba.com, saket.dumbre@intel.com, mchehab@kernel.org, dave@stgolabs.net, djbw@kernel.org, bp@alien8.de, tony.luck@intel.com, guohanjun@huawei.com, lenb@kernel.org, skhan@linuxfoundation.org, vishal.l.verma@intel.com, rafael@kernel.org, corbet@lwn.net, ira.weiny@intel.com, dave.jiang@intel.com, krzk+dt@kernel.org, robh@kernel.org, catalin.marinas@arm.com, alison.schofield@intel.com, conor+dt@kernel.org Cc: Ahmed Tiba , linux-arm-kernel@lists.infradead.org, Michael.Zhao2@arm.com, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-cxl@vger.kernel.org, Dmitry.Lamerov@arm.com, devicetree@vger.kernel.org, linux-acpi@vger.kernel.org, linux-edac@vger.kernel.org, acpica-devel@lists.linux.dev X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1780048269; l=11290; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=lFx22FK8BwhqLRtXVDa7n9zPnHQJet90r0IFTaX1i1s=; b=SRigsCIzuicXgXu6KXQKiOnNICRrcCKoYHTED6AiIpcNs82LTNCJkQbr/QLdCdhy07fMq8gga 7zEfHMZZvW4C75hv9AsedWT8m3s+fwKZAqI3PihoAAdG/ZGR23wBqzA X-Developer-Key: i=ahmed.tiba@arm.com; a=ed25519; pk=xVOtd+Qklh/4tuM3tB+BEZD4jj5a6W59C3KCNX6v7OE= Relocate the estatus cache allocation and lookup helpers from ghes.c into ghes_cper.c. This code move keeps the logic intact while making the cache implementation available to forthcoming users. Signed-off-by: Ahmed Tiba Reviewed-by: Jonathan Cameron --- drivers/acpi/apei/ghes.c | 138 +-------------------------------------= --- drivers/acpi/apei/ghes_cper.c | 140 ++++++++++++++++++++++++++++++++++++++= ++++ include/acpi/ghes_cper.h | 6 ++ 3 files changed, 147 insertions(+), 137 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 91638ae7e05e..adab7404310e 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -113,10 +113,7 @@ static DEFINE_MUTEX(ghes_devs_mutex); */ static DEFINE_SPINLOCK(ghes_notify_lock_irq); =20 -static struct gen_pool *ghes_estatus_pool; - -static struct ghes_estatus_cache __rcu *ghes_estatus_caches[GHES_ESTATUS_C= ACHES_SIZE]; -static atomic_t ghes_estatus_cache_alloced; +struct gen_pool *ghes_estatus_pool; =20 int ghes_estatus_pool_init(unsigned int num_ghes) { @@ -733,139 +730,6 @@ static int ghes_print_estatus(const char *pfx, return 0; } =20 -/* - * GHES error status reporting throttle, to report more kinds of - * errors, instead of just most frequently occurred errors. - */ -static int ghes_estatus_cached(struct acpi_hest_generic_status *estatus) -{ - u32 len; - int i, cached =3D 0; - unsigned long long now; - struct ghes_estatus_cache *cache; - struct acpi_hest_generic_status *cache_estatus; - - len =3D cper_estatus_len(estatus); - rcu_read_lock(); - for (i =3D 0; i < GHES_ESTATUS_CACHES_SIZE; i++) { - cache =3D rcu_dereference(ghes_estatus_caches[i]); - if (cache =3D=3D NULL) - continue; - if (len !=3D cache->estatus_len) - continue; - cache_estatus =3D GHES_ESTATUS_FROM_CACHE(cache); - if (memcmp(estatus, cache_estatus, len)) - continue; - atomic_inc(&cache->count); - now =3D sched_clock(); - if (now - cache->time_in < GHES_ESTATUS_IN_CACHE_MAX_NSEC) - cached =3D 1; - break; - } - rcu_read_unlock(); - return cached; -} - -static struct ghes_estatus_cache *ghes_estatus_cache_alloc( - struct acpi_hest_generic *generic, - struct acpi_hest_generic_status *estatus) -{ - int alloced; - u32 len, cache_len; - struct ghes_estatus_cache *cache; - struct acpi_hest_generic_status *cache_estatus; - - alloced =3D atomic_add_return(1, &ghes_estatus_cache_alloced); - if (alloced > GHES_ESTATUS_CACHE_ALLOCED_MAX) { - atomic_dec(&ghes_estatus_cache_alloced); - return NULL; - } - len =3D cper_estatus_len(estatus); - cache_len =3D GHES_ESTATUS_CACHE_LEN(len); - cache =3D (void *)gen_pool_alloc(ghes_estatus_pool, cache_len); - if (!cache) { - atomic_dec(&ghes_estatus_cache_alloced); - return NULL; - } - cache_estatus =3D GHES_ESTATUS_FROM_CACHE(cache); - memcpy(cache_estatus, estatus, len); - cache->estatus_len =3D len; - atomic_set(&cache->count, 0); - cache->generic =3D generic; - cache->time_in =3D sched_clock(); - return cache; -} - -static void ghes_estatus_cache_rcu_free(struct rcu_head *head) -{ - struct ghes_estatus_cache *cache; - u32 len; - - cache =3D container_of(head, struct ghes_estatus_cache, rcu); - len =3D cper_estatus_len(GHES_ESTATUS_FROM_CACHE(cache)); - len =3D GHES_ESTATUS_CACHE_LEN(len); - gen_pool_free(ghes_estatus_pool, (unsigned long)cache, len); - atomic_dec(&ghes_estatus_cache_alloced); -} - -static void -ghes_estatus_cache_add(struct acpi_hest_generic *generic, - struct acpi_hest_generic_status *estatus) -{ - unsigned long long now, duration, period, max_period =3D 0; - struct ghes_estatus_cache *cache, *new_cache; - struct ghes_estatus_cache __rcu *victim; - int i, slot =3D -1, count; - - new_cache =3D ghes_estatus_cache_alloc(generic, estatus); - if (!new_cache) - return; - - rcu_read_lock(); - now =3D sched_clock(); - for (i =3D 0; i < GHES_ESTATUS_CACHES_SIZE; i++) { - cache =3D rcu_dereference(ghes_estatus_caches[i]); - if (cache =3D=3D NULL) { - slot =3D i; - break; - } - duration =3D now - cache->time_in; - if (duration >=3D GHES_ESTATUS_IN_CACHE_MAX_NSEC) { - slot =3D i; - break; - } - count =3D atomic_read(&cache->count); - period =3D duration; - do_div(period, (count + 1)); - if (period > max_period) { - max_period =3D period; - slot =3D i; - } - } - rcu_read_unlock(); - - if (slot !=3D -1) { - /* - * Use release semantics to ensure that ghes_estatus_cached() - * running on another CPU will see the updated cache fields if - * it can see the new value of the pointer. - */ - victim =3D xchg_release(&ghes_estatus_caches[slot], - RCU_INITIALIZER(new_cache)); - - /* - * At this point, victim may point to a cached item different - * from the one based on which we selected the slot. Instead of - * going to the loop again to pick another slot, let's just - * drop the other item anyway: this may cause a false cache - * miss later on, but that won't cause any problems. - */ - if (victim) - call_rcu(&unrcu_pointer(victim)->rcu, - ghes_estatus_cache_rcu_free); - } -} - static void __ghes_panic(struct ghes *ghes, struct acpi_hest_generic_status *estatus, u64 buf_paddr, enum fixed_addresses fixmap_idx) diff --git a/drivers/acpi/apei/ghes_cper.c b/drivers/acpi/apei/ghes_cper.c index 8080e0f76dac..0a117f478afb 100644 --- a/drivers/acpi/apei/ghes_cper.c +++ b/drivers/acpi/apei/ghes_cper.c @@ -13,10 +13,14 @@ */ =20 #include +#include #include #include +#include #include #include +#include +#include #include =20 #include @@ -27,6 +31,9 @@ =20 #include "apei-internal.h" =20 +static struct ghes_estatus_cache __rcu *ghes_estatus_caches[GHES_ESTATUS_C= ACHES_SIZE]; +static atomic_t ghes_estatus_cache_alloced; + static void __iomem *ghes_map(u64 pfn, enum fixed_addresses fixmap_idx) { phys_addr_t paddr; @@ -258,3 +265,136 @@ void ghes_clear_estatus(struct ghes *ghes, if (is_hest_type_generic_v2(ghes)) ghes_ack_error(ghes->generic_v2); } + +/* + * GHES error status reporting throttle, to report more kinds of + * errors, instead of just most frequently occurred errors. + */ +int ghes_estatus_cached(struct acpi_hest_generic_status *estatus) +{ + u32 len; + int i, cached =3D 0; + unsigned long long now; + struct ghes_estatus_cache *cache; + struct acpi_hest_generic_status *cache_estatus; + + len =3D cper_estatus_len(estatus); + rcu_read_lock(); + for (i =3D 0; i < GHES_ESTATUS_CACHES_SIZE; i++) { + cache =3D rcu_dereference(ghes_estatus_caches[i]); + if (cache =3D=3D NULL) + continue; + if (len !=3D cache->estatus_len) + continue; + cache_estatus =3D GHES_ESTATUS_FROM_CACHE(cache); + if (memcmp(estatus, cache_estatus, len)) + continue; + atomic_inc(&cache->count); + now =3D sched_clock(); + if (now - cache->time_in < GHES_ESTATUS_IN_CACHE_MAX_NSEC) + cached =3D 1; + break; + } + rcu_read_unlock(); + return cached; +} + +static struct ghes_estatus_cache *ghes_estatus_cache_alloc( + struct acpi_hest_generic *generic, + struct acpi_hest_generic_status *estatus) +{ + int alloced; + u32 len, cache_len; + struct ghes_estatus_cache *cache; + struct acpi_hest_generic_status *cache_estatus; + + alloced =3D atomic_add_return(1, &ghes_estatus_cache_alloced); + if (alloced > GHES_ESTATUS_CACHE_ALLOCED_MAX) { + atomic_dec(&ghes_estatus_cache_alloced); + return NULL; + } + len =3D cper_estatus_len(estatus); + cache_len =3D GHES_ESTATUS_CACHE_LEN(len); + cache =3D (void *)gen_pool_alloc(ghes_estatus_pool, cache_len); + if (cache =3D=3D NULL) { + atomic_dec(&ghes_estatus_cache_alloced); + return NULL; + } + cache_estatus =3D GHES_ESTATUS_FROM_CACHE(cache); + memcpy(cache_estatus, estatus, len); + cache->estatus_len =3D len; + atomic_set(&cache->count, 0); + cache->generic =3D generic; + cache->time_in =3D sched_clock(); + return cache; +} + +static void ghes_estatus_cache_rcu_free(struct rcu_head *head) +{ + struct ghes_estatus_cache *cache; + u32 len; + + cache =3D container_of(head, struct ghes_estatus_cache, rcu); + len =3D cper_estatus_len(GHES_ESTATUS_FROM_CACHE(cache)); + len =3D GHES_ESTATUS_CACHE_LEN(len); + gen_pool_free(ghes_estatus_pool, (unsigned long)cache, len); + atomic_dec(&ghes_estatus_cache_alloced); +} + +void +ghes_estatus_cache_add(struct acpi_hest_generic *generic, + struct acpi_hest_generic_status *estatus) +{ + unsigned long long now, duration, period, max_period =3D 0; + struct ghes_estatus_cache *cache, *new_cache; + struct ghes_estatus_cache __rcu *victim; + int i, slot =3D -1, count; + + new_cache =3D ghes_estatus_cache_alloc(generic, estatus); + if (!new_cache) + return; + + rcu_read_lock(); + now =3D sched_clock(); + for (i =3D 0; i < GHES_ESTATUS_CACHES_SIZE; i++) { + cache =3D rcu_dereference(ghes_estatus_caches[i]); + if (cache =3D=3D NULL) { + slot =3D i; + break; + } + duration =3D now - cache->time_in; + if (duration >=3D GHES_ESTATUS_IN_CACHE_MAX_NSEC) { + slot =3D i; + break; + } + count =3D atomic_read(&cache->count); + period =3D duration; + do_div(period, (count + 1)); + if (period > max_period) { + max_period =3D period; + slot =3D i; + } + } + rcu_read_unlock(); + + if (slot !=3D -1) { + /* + * Use release semantics to ensure that ghes_estatus_cached() + * running on another CPU will see the updated cache fields if + * it can see the new value of the pointer. + */ + victim =3D xchg_release(&ghes_estatus_caches[slot], + RCU_INITIALIZER(new_cache)); + + /* + * At this point, victim may point to a cached item different + * from the one based on which we selected the slot. Instead of + * going to the loop again to pick another slot, let's just + * drop the other item anyway: this may cause a false cache + * miss later on, but that won't cause any problems. + */ + if (victim) + call_rcu(&unrcu_pointer(victim)->rcu, + ghes_estatus_cache_rcu_free); + } +} diff --git a/include/acpi/ghes_cper.h b/include/acpi/ghes_cper.h index 6b7632cfaf66..1b5dbeca9bb6 100644 --- a/include/acpi/ghes_cper.h +++ b/include/acpi/ghes_cper.h @@ -16,6 +16,7 @@ #ifndef ACPI_APEI_GHES_CPER_H #define ACPI_APEI_GHES_CPER_H =20 +#include #include =20 #include @@ -54,6 +55,8 @@ ((struct acpi_hest_generic_data *) \ ((struct ghes_vendor_record_entry *)(vendor_entry) + 1)) =20 +extern struct gen_pool *ghes_estatus_pool; + static inline bool is_hest_type_generic_v2(struct ghes *ghes) { return ghes->generic->header.type =3D=3D ACPI_HEST_TYPE_GENERIC_ERROR_V2; @@ -98,5 +101,8 @@ int __ghes_read_estatus(struct acpi_hest_generic_status = *estatus, u64 buf_paddr, enum fixed_addresses fixmap_idx, size_t buf_len); #endif +int ghes_estatus_cached(struct acpi_hest_generic_status *estatus); +void ghes_estatus_cache_add(struct acpi_hest_generic *generic, + struct acpi_hest_generic_status *estatus); =20 #endif /* ACPI_APEI_GHES_CPER_H */ --=20 2.43.0 From nobody Mon Jun 8 12:17:15 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 8E66C3CA4A8; Fri, 29 May 2026 09:51:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780048300; cv=none; b=q6dkGvkaG5INC5LjUstIFpHH318MewHuLHIddgWIgSiDPYM0qsd10hOp33b4LyNZVQ/q+2ZnRSvUQ6xGYN14Yj0PgIEQf1s4nUQfng41DbAeoOYcFL8ZUTMuxVGX47VqkbdDv3Mihr/uKJGF6CLc3WPFqmx+jtzJA93Dv49iZyw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780048300; c=relaxed/simple; bh=GmkCj4OmkhZ+C8RQK5gSKhS8oTWK81IqKjqv4BAeWFY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=PzIy0Z8WMDeFscMFVfQuaDAJjFv1u6fW4lFfR5s9vsEv+cJeyJkO+ibpYTFm/Jekww/qCFV0ch32uL5hV5InXbWrBSFHpEkdZ897MMbp3vKiVQ7Cauj1+Ugalg1OozXN4zYD+a/7k3GkQ17k7Q1/s8yfkvx3KuBQnpDAY/Xfm30= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=jPChF0w2; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="jPChF0w2" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id BEB3A22D7; Fri, 29 May 2026 02:51:32 -0700 (PDT) Received: from e134710.arm.com (e134710.arm.com [10.33.10.82]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 5BB113F905; Fri, 29 May 2026 02:51:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1780048297; bh=GmkCj4OmkhZ+C8RQK5gSKhS8oTWK81IqKjqv4BAeWFY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=jPChF0w2ThAMptcfhQc729JlCBStycoP9pNFHoBh+sFE7JnyusHAqMuePqhDxX9cL Yl6VzT+73zjl36EV8at8RGG3ciD+t6Z/UNTx5yGdgUrqgHJ8vLuxSL4AC0/dGpx4Q8 WEp4rOh7ZloaCdyY6mwIrzvQ8+cgpzbopQol+l50= From: Ahmed Tiba Date: Fri, 29 May 2026 10:50:45 +0100 Subject: [PATCH v5 05/10] ACPI: APEI: GHES: move vendor record helpers Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-5-2e0500d42642@arm.com> References: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-0-2e0500d42642@arm.com> In-Reply-To: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-0-2e0500d42642@arm.com> To: will@kernel.org, jic23@kernel.org, xueshuai@linux.alibaba.com, saket.dumbre@intel.com, mchehab@kernel.org, dave@stgolabs.net, djbw@kernel.org, bp@alien8.de, tony.luck@intel.com, guohanjun@huawei.com, lenb@kernel.org, skhan@linuxfoundation.org, vishal.l.verma@intel.com, rafael@kernel.org, corbet@lwn.net, ira.weiny@intel.com, dave.jiang@intel.com, krzk+dt@kernel.org, robh@kernel.org, catalin.marinas@arm.com, alison.schofield@intel.com, conor+dt@kernel.org Cc: Ahmed Tiba , linux-arm-kernel@lists.infradead.org, Michael.Zhao2@arm.com, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-cxl@vger.kernel.org, Dmitry.Lamerov@arm.com, devicetree@vger.kernel.org, linux-acpi@vger.kernel.org, linux-edac@vger.kernel.org, acpica-devel@lists.linux.dev X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1780048269; l=6986; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=GmkCj4OmkhZ+C8RQK5gSKhS8oTWK81IqKjqv4BAeWFY=; b=KYRHDopWA2jNC6aRw2sm23yL35zhS2/aRndc8n6oRzMg8zJgGINYWaozijmn6Iv+o7C8Ef0iD X4Q0q9JnPfYApW6w8sB9qhEULNwYr6g9ZtjRdHphF2QG+Su1ZN3iDWo X-Developer-Key: i=ahmed.tiba@arm.com; a=ed25519; pk=xVOtd+Qklh/4tuM3tB+BEZD4jj5a6W59C3KCNX6v7OE= Shift the vendor record workqueue helpers into ghes_cper.c so both GHES and future DT-based providers can use the same implementation. The change is mechanical and keeps the notifier behavior identical. Signed-off-by: Ahmed Tiba --- drivers/acpi/apei/ghes.c | 86 +++++++++------------------------------= ---- drivers/acpi/apei/ghes_cper.c | 55 +++++++++++++++++++++++++++ include/acpi/ghes_cper.h | 2 + 3 files changed, 75 insertions(+), 68 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index adab7404310e..81ac51632f21 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -383,74 +383,6 @@ static void ghes_handle_aer(struct acpi_hest_generic_d= ata *gdata) #endif } =20 -static BLOCKING_NOTIFIER_HEAD(vendor_record_notify_list); - -int ghes_register_vendor_record_notifier(struct notifier_block *nb) -{ - return blocking_notifier_chain_register(&vendor_record_notify_list, nb); -} -EXPORT_SYMBOL_GPL(ghes_register_vendor_record_notifier); - -void ghes_unregister_vendor_record_notifier(struct notifier_block *nb) -{ - blocking_notifier_chain_unregister(&vendor_record_notify_list, nb); -} -EXPORT_SYMBOL_GPL(ghes_unregister_vendor_record_notifier); - -static void ghes_vendor_record_notifier_destroy(void *nb) -{ - ghes_unregister_vendor_record_notifier(nb); -} - -int devm_ghes_register_vendor_record_notifier(struct device *dev, - struct notifier_block *nb) -{ - int ret; - - ret =3D ghes_register_vendor_record_notifier(nb); - if (ret) - return ret; - - return devm_add_action_or_reset(dev, ghes_vendor_record_notifier_destroy,= nb); -} -EXPORT_SYMBOL_GPL(devm_ghes_register_vendor_record_notifier); - -static void ghes_vendor_record_work_func(struct work_struct *work) -{ - struct ghes_vendor_record_entry *entry; - struct acpi_hest_generic_data *gdata; - u32 len; - - entry =3D container_of(work, struct ghes_vendor_record_entry, work); - gdata =3D GHES_GDATA_FROM_VENDOR_ENTRY(entry); - - blocking_notifier_call_chain(&vendor_record_notify_list, - entry->error_severity, gdata); - - len =3D GHES_VENDOR_ENTRY_LEN(acpi_hest_get_record_size(gdata)); - gen_pool_free(ghes_estatus_pool, (unsigned long)entry, len); -} - -static void ghes_defer_non_standard_event(struct acpi_hest_generic_data *g= data, - int sev) -{ - struct acpi_hest_generic_data *copied_gdata; - struct ghes_vendor_record_entry *entry; - u32 len; - - len =3D GHES_VENDOR_ENTRY_LEN(acpi_hest_get_record_size(gdata)); - entry =3D (void *)gen_pool_alloc(ghes_estatus_pool, len); - if (!entry) - return; - - copied_gdata =3D GHES_GDATA_FROM_VENDOR_ENTRY(entry); - memcpy(copied_gdata, gdata, acpi_hest_get_record_size(gdata)); - entry->error_severity =3D sev; - - INIT_WORK(&entry->work, ghes_vendor_record_work_func); - schedule_work(&entry->work); -} - /* Room for 8 entries */ #define CXL_CPER_PROT_ERR_FIFO_DEPTH 8 static DEFINE_KFIFO(cxl_cper_prot_err_fifo, struct cxl_cper_prot_err_work_= data, @@ -514,6 +446,24 @@ int cxl_cper_prot_err_kfifo_get(struct cxl_cper_prot_e= rr_work_data *wd) } EXPORT_SYMBOL_NS_GPL(cxl_cper_prot_err_kfifo_get, "CXL"); =20 +static void ghes_vendor_record_notifier_destroy(void *nb) +{ + ghes_unregister_vendor_record_notifier(nb); +} + +int devm_ghes_register_vendor_record_notifier(struct device *dev, + struct notifier_block *nb) +{ + int ret; + + ret =3D ghes_register_vendor_record_notifier(nb); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, ghes_vendor_record_notifier_destroy,= nb); +} +EXPORT_SYMBOL_GPL(devm_ghes_register_vendor_record_notifier); + /* Room for 8 entries for each of the 4 event log queues */ #define CXL_CPER_FIFO_DEPTH 32 DEFINE_KFIFO(cxl_cper_fifo, struct cxl_cper_work_data, CXL_CPER_FIFO_DEPTH= ); diff --git a/drivers/acpi/apei/ghes_cper.c b/drivers/acpi/apei/ghes_cper.c index 0a117f478afb..131980d36064 100644 --- a/drivers/acpi/apei/ghes_cper.c +++ b/drivers/acpi/apei/ghes_cper.c @@ -14,12 +14,17 @@ =20 #include #include +#include #include #include +#include #include #include +#include +#include #include #include +#include #include #include =20 @@ -266,6 +271,56 @@ void ghes_clear_estatus(struct ghes *ghes, ghes_ack_error(ghes->generic_v2); } =20 +static BLOCKING_NOTIFIER_HEAD(vendor_record_notify_list); + +int ghes_register_vendor_record_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&vendor_record_notify_list, nb); +} +EXPORT_SYMBOL_GPL(ghes_register_vendor_record_notifier); + +void ghes_unregister_vendor_record_notifier(struct notifier_block *nb) +{ + blocking_notifier_chain_unregister(&vendor_record_notify_list, nb); +} +EXPORT_SYMBOL_GPL(ghes_unregister_vendor_record_notifier); + +static void ghes_vendor_record_work_func(struct work_struct *work) +{ + struct ghes_vendor_record_entry *entry; + struct acpi_hest_generic_data *gdata; + u32 len; + + entry =3D container_of(work, struct ghes_vendor_record_entry, work); + gdata =3D GHES_GDATA_FROM_VENDOR_ENTRY(entry); + + blocking_notifier_call_chain(&vendor_record_notify_list, + entry->error_severity, gdata); + + len =3D GHES_VENDOR_ENTRY_LEN(acpi_hest_get_record_size(gdata)); + gen_pool_free(ghes_estatus_pool, (unsigned long)entry, len); +} + +void ghes_defer_non_standard_event(struct acpi_hest_generic_data *gdata, + int sev) +{ + struct acpi_hest_generic_data *copied_gdata; + struct ghes_vendor_record_entry *entry; + u32 len; + + len =3D GHES_VENDOR_ENTRY_LEN(acpi_hest_get_record_size(gdata)); + entry =3D (void *)gen_pool_alloc(ghes_estatus_pool, len); + if (!entry) + return; + + copied_gdata =3D GHES_GDATA_FROM_VENDOR_ENTRY(entry); + memcpy(copied_gdata, gdata, acpi_hest_get_record_size(gdata)); + entry->error_severity =3D sev; + + INIT_WORK(&entry->work, ghes_vendor_record_work_func); + schedule_work(&entry->work); +} + /* * GHES error status reporting throttle, to report more kinds of * errors, instead of just most frequently occurred errors. diff --git a/include/acpi/ghes_cper.h b/include/acpi/ghes_cper.h index 1b5dbeca9bb6..51725f25c516 100644 --- a/include/acpi/ghes_cper.h +++ b/include/acpi/ghes_cper.h @@ -104,5 +104,7 @@ int __ghes_read_estatus(struct acpi_hest_generic_status= *estatus, int ghes_estatus_cached(struct acpi_hest_generic_status *estatus); void ghes_estatus_cache_add(struct acpi_hest_generic *generic, struct acpi_hest_generic_status *estatus); +void ghes_defer_non_standard_event(struct acpi_hest_generic_data *gdata, + int sev); =20 #endif /* ACPI_APEI_GHES_CPER_H */ --=20 2.43.0 From nobody Mon Jun 8 12:17:15 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 1BC0D3CAE70; Fri, 29 May 2026 09:51:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780048304; cv=none; b=t8Dpp5ypDBPqPmkp9PwtCdaiviXBJOsYyBU8kxBuHSIAaX11hCa5weVs5D5JSU2rt26OcrIAoBmVXiU+LZwxjAmNL0394yKU+9N4igrdWqcrgmEEkb0P+C9CyNzmNLv93jDGOL8oLxaE4iD11MDPzpodLVkn2bGIkD39TOeD5g8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780048304; c=relaxed/simple; bh=qIFFyyy4m0uVoomEtae1wRHS1d2gI1FUMrEpVshS6vY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ROWuqf5pcStzCanBTSYQn9HbspdCSIZxiUsdKclNG31jfSiaxgH37MOpcGs8zdBUos3BUD3M+Juhy57dZ1fg7ixhYseXQYk1TM6spdS51XE5lWMBTgwhDk8pqcEl7InvHOmWaEfKDPUOddyzU4zi+yP6ye+Us2DCtUeO+hczhDA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=K0yxv1mI; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="K0yxv1mI" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 73C0032E5; Fri, 29 May 2026 02:51:37 -0700 (PDT) Received: from e134710.arm.com (e134710.arm.com [10.33.10.82]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 195323F905; Fri, 29 May 2026 02:51:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1780048302; bh=qIFFyyy4m0uVoomEtae1wRHS1d2gI1FUMrEpVshS6vY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=K0yxv1mIHfRIjQVUddiv4tJ9zbuW6NsMf3bbwHp7/QB6+e1pVx2NxzsZxoo/iBOBW 3xToFeQhuJb8nuEYEM+nHsEWcuH2e9tQXMj2lgl48cYZzME/Ak9FK+0PAdf7yJxyft YqlFTbgCknX8R95VzGEK9L3u8q5XKJOGye2iLtU8= From: Ahmed Tiba Date: Fri, 29 May 2026 10:50:46 +0100 Subject: [PATCH v5 06/10] ACPI: APEI: GHES: move CXL CPER helpers Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-6-2e0500d42642@arm.com> References: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-0-2e0500d42642@arm.com> In-Reply-To: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-0-2e0500d42642@arm.com> To: will@kernel.org, jic23@kernel.org, xueshuai@linux.alibaba.com, saket.dumbre@intel.com, mchehab@kernel.org, dave@stgolabs.net, djbw@kernel.org, bp@alien8.de, tony.luck@intel.com, guohanjun@huawei.com, lenb@kernel.org, skhan@linuxfoundation.org, vishal.l.verma@intel.com, rafael@kernel.org, corbet@lwn.net, ira.weiny@intel.com, dave.jiang@intel.com, krzk+dt@kernel.org, robh@kernel.org, catalin.marinas@arm.com, alison.schofield@intel.com, conor+dt@kernel.org Cc: Ahmed Tiba , linux-arm-kernel@lists.infradead.org, Michael.Zhao2@arm.com, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-cxl@vger.kernel.org, Dmitry.Lamerov@arm.com, devicetree@vger.kernel.org, linux-acpi@vger.kernel.org, linux-edac@vger.kernel.org, acpica-devel@lists.linux.dev X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1780048269; l=10220; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=qIFFyyy4m0uVoomEtae1wRHS1d2gI1FUMrEpVshS6vY=; b=uiUZzhRkHQVdWfn4cIK8H6E1iU0KW5AnAh2aXXgcvgvVkBlBmmfqSbvVPJOMWI02ojwvAKzYY et16V8aeXI+CsTjoPzXnlgDzEE5hEYhN7QoxF+3BTH3u10y/GHD9g22 X-Developer-Key: i=ahmed.tiba@arm.com; a=ed25519; pk=xVOtd+Qklh/4tuM3tB+BEZD4jj5a6W59C3KCNX6v7OE= Move the CXL CPER handling paths out of ghes.c and into ghes_cper.c so the helpers can be reused. The code is moved as-is, with the public prototypes updated so GHES keeps calling into the new translation unit. Signed-off-by: Ahmed Tiba Reviewed-by: Jonathan Cameron --- drivers/acpi/apei/ghes.c | 132 --------------------------------------= --- drivers/acpi/apei/ghes_cper.c | 134 ++++++++++++++++++++++++++++++++++++++= ++++ include/acpi/ghes_cper.h | 11 ++++ 3 files changed, 145 insertions(+), 132 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 81ac51632f21..85be2ebf4d3e 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -383,69 +383,6 @@ static void ghes_handle_aer(struct acpi_hest_generic_d= ata *gdata) #endif } =20 -/* Room for 8 entries */ -#define CXL_CPER_PROT_ERR_FIFO_DEPTH 8 -static DEFINE_KFIFO(cxl_cper_prot_err_fifo, struct cxl_cper_prot_err_work_= data, - CXL_CPER_PROT_ERR_FIFO_DEPTH); - -/* Synchronize schedule_work() with cxl_cper_prot_err_work changes */ -static DEFINE_SPINLOCK(cxl_cper_prot_err_work_lock); -struct work_struct *cxl_cper_prot_err_work; - -static void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err, - int severity) -{ -#ifdef CONFIG_ACPI_APEI_PCIEAER - struct cxl_cper_prot_err_work_data wd; - - if (cxl_cper_sec_prot_err_valid(prot_err)) - return; - - guard(spinlock_irqsave)(&cxl_cper_prot_err_work_lock); - - if (!cxl_cper_prot_err_work) - return; - - if (cxl_cper_setup_prot_err_work_data(&wd, prot_err, severity)) - return; - - if (!kfifo_put(&cxl_cper_prot_err_fifo, wd)) { - pr_err_ratelimited("CXL CPER kfifo overflow\n"); - return; - } - - schedule_work(cxl_cper_prot_err_work); -#endif -} - -int cxl_cper_register_prot_err_work(struct work_struct *work) -{ - if (cxl_cper_prot_err_work) - return -EINVAL; - - guard(spinlock)(&cxl_cper_prot_err_work_lock); - cxl_cper_prot_err_work =3D work; - return 0; -} -EXPORT_SYMBOL_NS_GPL(cxl_cper_register_prot_err_work, "CXL"); - -int cxl_cper_unregister_prot_err_work(struct work_struct *work) -{ - if (cxl_cper_prot_err_work !=3D work) - return -EINVAL; - - guard(spinlock)(&cxl_cper_prot_err_work_lock); - cxl_cper_prot_err_work =3D NULL; - return 0; -} -EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_prot_err_work, "CXL"); - -int cxl_cper_prot_err_kfifo_get(struct cxl_cper_prot_err_work_data *wd) -{ - return kfifo_get(&cxl_cper_prot_err_fifo, wd); -} -EXPORT_SYMBOL_NS_GPL(cxl_cper_prot_err_kfifo_get, "CXL"); - static void ghes_vendor_record_notifier_destroy(void *nb) { ghes_unregister_vendor_record_notifier(nb); @@ -464,75 +401,6 @@ int devm_ghes_register_vendor_record_notifier(struct d= evice *dev, } EXPORT_SYMBOL_GPL(devm_ghes_register_vendor_record_notifier); =20 -/* Room for 8 entries for each of the 4 event log queues */ -#define CXL_CPER_FIFO_DEPTH 32 -DEFINE_KFIFO(cxl_cper_fifo, struct cxl_cper_work_data, CXL_CPER_FIFO_DEPTH= ); - -/* Synchronize schedule_work() with cxl_cper_work changes */ -static DEFINE_SPINLOCK(cxl_cper_work_lock); -struct work_struct *cxl_cper_work; - -static void cxl_cper_post_event(enum cxl_event_type event_type, - struct cxl_cper_event_rec *rec) -{ - struct cxl_cper_work_data wd; - - if (rec->hdr.length <=3D sizeof(rec->hdr) || - rec->hdr.length > sizeof(*rec)) { - pr_err(FW_WARN "CXL CPER Invalid section length (%u)\n", - rec->hdr.length); - return; - } - - if (!(rec->hdr.validation_bits & CPER_CXL_COMP_EVENT_LOG_VALID)) { - pr_err(FW_WARN "CXL CPER invalid event\n"); - return; - } - - guard(spinlock_irqsave)(&cxl_cper_work_lock); - - if (!cxl_cper_work) - return; - - wd.event_type =3D event_type; - memcpy(&wd.rec, rec, sizeof(wd.rec)); - - if (!kfifo_put(&cxl_cper_fifo, wd)) { - pr_err_ratelimited("CXL CPER kfifo overflow\n"); - return; - } - - schedule_work(cxl_cper_work); -} - -int cxl_cper_register_work(struct work_struct *work) -{ - if (cxl_cper_work) - return -EINVAL; - - guard(spinlock)(&cxl_cper_work_lock); - cxl_cper_work =3D work; - return 0; -} -EXPORT_SYMBOL_NS_GPL(cxl_cper_register_work, "CXL"); - -int cxl_cper_unregister_work(struct work_struct *work) -{ - if (cxl_cper_work !=3D work) - return -EINVAL; - - guard(spinlock)(&cxl_cper_work_lock); - cxl_cper_work =3D NULL; - return 0; -} -EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_work, "CXL"); - -int cxl_cper_kfifo_get(struct cxl_cper_work_data *wd) -{ - return kfifo_get(&cxl_cper_fifo, wd); -} -EXPORT_SYMBOL_NS_GPL(cxl_cper_kfifo_get, "CXL"); - static void ghes_log_hwerr(int sev, guid_t *sec_type) { if (sev !=3D CPER_SEV_RECOVERABLE) diff --git a/drivers/acpi/apei/ghes_cper.c b/drivers/acpi/apei/ghes_cper.c index 131980d36064..d7a666a163c3 100644 --- a/drivers/acpi/apei/ghes_cper.c +++ b/drivers/acpi/apei/ghes_cper.c @@ -12,10 +12,12 @@ * Author: Huang Ying */ =20 +#include #include #include #include #include +#include #include #include #include @@ -321,6 +323,138 @@ void ghes_defer_non_standard_event(struct acpi_hest_g= eneric_data *gdata, schedule_work(&entry->work); } =20 +/* Room for 8 entries */ +#define CXL_CPER_PROT_ERR_FIFO_DEPTH 8 +static DEFINE_KFIFO(cxl_cper_prot_err_fifo, struct cxl_cper_prot_err_work_= data, + CXL_CPER_PROT_ERR_FIFO_DEPTH); + +/* Synchronize schedule_work() with cxl_cper_prot_err_work changes */ +static DEFINE_SPINLOCK(cxl_cper_prot_err_work_lock); +struct work_struct *cxl_cper_prot_err_work; + +void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err, + int severity) +{ +#ifdef CONFIG_ACPI_APEI_PCIEAER + struct cxl_cper_prot_err_work_data wd; + + if (cxl_cper_sec_prot_err_valid(prot_err)) + return; + + guard(spinlock_irqsave)(&cxl_cper_prot_err_work_lock); + + if (!cxl_cper_prot_err_work) + return; + + if (cxl_cper_setup_prot_err_work_data(&wd, prot_err, severity)) + return; + + if (!kfifo_put(&cxl_cper_prot_err_fifo, wd)) { + pr_err_ratelimited("CXL CPER kfifo overflow\n"); + return; + } + + schedule_work(cxl_cper_prot_err_work); +#endif +} + +int cxl_cper_register_prot_err_work(struct work_struct *work) +{ + if (cxl_cper_prot_err_work) + return -EINVAL; + + guard(spinlock)(&cxl_cper_prot_err_work_lock); + cxl_cper_prot_err_work =3D work; + return 0; +} +EXPORT_SYMBOL_NS_GPL(cxl_cper_register_prot_err_work, "CXL"); + +int cxl_cper_unregister_prot_err_work(struct work_struct *work) +{ + if (cxl_cper_prot_err_work !=3D work) + return -EINVAL; + + guard(spinlock)(&cxl_cper_prot_err_work_lock); + cxl_cper_prot_err_work =3D NULL; + return 0; +} +EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_prot_err_work, "CXL"); + +int cxl_cper_prot_err_kfifo_get(struct cxl_cper_prot_err_work_data *wd) +{ + return kfifo_get(&cxl_cper_prot_err_fifo, wd); +} +EXPORT_SYMBOL_NS_GPL(cxl_cper_prot_err_kfifo_get, "CXL"); + +/* Room for 8 entries for each of the 4 event log queues */ +#define CXL_CPER_FIFO_DEPTH 32 +static DEFINE_KFIFO(cxl_cper_fifo, struct cxl_cper_work_data, CXL_CPER_FIF= O_DEPTH); + +/* Synchronize schedule_work() with cxl_cper_work changes */ +static DEFINE_SPINLOCK(cxl_cper_work_lock); +struct work_struct *cxl_cper_work; + +void cxl_cper_post_event(enum cxl_event_type event_type, + struct cxl_cper_event_rec *rec) +{ + struct cxl_cper_work_data wd; + + if (rec->hdr.length <=3D sizeof(rec->hdr) || + rec->hdr.length > sizeof(*rec)) { + pr_err(FW_WARN "CXL CPER Invalid section length (%u)\n", + rec->hdr.length); + return; + } + + if (!(rec->hdr.validation_bits & CPER_CXL_COMP_EVENT_LOG_VALID)) { + pr_err(FW_WARN "CXL CPER invalid event\n"); + return; + } + + guard(spinlock_irqsave)(&cxl_cper_work_lock); + + if (!cxl_cper_work) + return; + + wd.event_type =3D event_type; + memcpy(&wd.rec, rec, sizeof(wd.rec)); + + if (!kfifo_put(&cxl_cper_fifo, wd)) { + pr_err_ratelimited("CXL CPER kfifo overflow\n"); + return; + } + + schedule_work(cxl_cper_work); +} + +int cxl_cper_register_work(struct work_struct *work) +{ + if (cxl_cper_work) + return -EINVAL; + + guard(spinlock)(&cxl_cper_work_lock); + cxl_cper_work =3D work; + return 0; +} +EXPORT_SYMBOL_NS_GPL(cxl_cper_register_work, "CXL"); + +int cxl_cper_unregister_work(struct work_struct *work) +{ + if (cxl_cper_work !=3D work) + return -EINVAL; + + guard(spinlock)(&cxl_cper_work_lock); + cxl_cper_work =3D NULL; + return 0; +} +EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_work, "CXL"); + +int cxl_cper_kfifo_get(struct cxl_cper_work_data *wd) +{ + return kfifo_get(&cxl_cper_fifo, wd); +} +EXPORT_SYMBOL_NS_GPL(cxl_cper_kfifo_get, "CXL"); + /* * GHES error status reporting throttle, to report more kinds of * errors, instead of just most frequently occurred errors. diff --git a/include/acpi/ghes_cper.h b/include/acpi/ghes_cper.h index 51725f25c516..dd49e9179b63 100644 --- a/include/acpi/ghes_cper.h +++ b/include/acpi/ghes_cper.h @@ -20,6 +20,7 @@ #include =20 #include +#include =20 #define GHES_PFX "GHES: " =20 @@ -106,5 +107,15 @@ void ghes_estatus_cache_add(struct acpi_hest_generic *= generic, struct acpi_hest_generic_status *estatus); void ghes_defer_non_standard_event(struct acpi_hest_generic_data *gdata, int sev); +void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err, + int severity); +int cxl_cper_register_prot_err_work(struct work_struct *work); +int cxl_cper_unregister_prot_err_work(struct work_struct *work); +int cxl_cper_prot_err_kfifo_get(struct cxl_cper_prot_err_work_data *wd); +void cxl_cper_post_event(enum cxl_event_type event_type, + struct cxl_cper_event_rec *rec); +int cxl_cper_register_work(struct work_struct *work); +int cxl_cper_unregister_work(struct work_struct *work); +int cxl_cper_kfifo_get(struct cxl_cper_work_data *wd); =20 #endif /* ACPI_APEI_GHES_CPER_H */ --=20 2.43.0 From nobody Mon Jun 8 12:17:15 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id C4F8F3C8705; Fri, 29 May 2026 09:51:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780048309; cv=none; b=hx4AWYnrc6LJkmV3848huLNsuA0lrJW2fmhIv2VYis6VKJarGjkP5qsW9qDJzTOjIm6BLVKaPGrX6mi9/npFX4bmD1mVWMqDPcAEFNiYgJAxjlhs12EvEMxZR2SPhnJvIVrqAJTMJJ5l2A9I3GYY5e1IX6aM8KREVyvAJnvZMv8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780048309; c=relaxed/simple; bh=SE5bStAb7vsrTWobe7ySsF8dvuF70BXHOSqTYvhTqMs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=VlXXVZHaNyxwxEMloMm8haYcJak66p74mXm1v8u0TiLZfIINPEOEBC1QLcSrh02FwaWQzY5T4gf2kGNK2eV6bX5eh43D2WT2XuvNlJHnkXOBW/PQ+Xt0onV9K3iHkc4P5qT2s7cEoYtU49IvJmaUO5mzcxqBUZ1vI1yNJDUyrlQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=BU3NXZ6o; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="BU3NXZ6o" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 2F34422D7; Fri, 29 May 2026 02:51:42 -0700 (PDT) Received: from e134710.arm.com (e134710.arm.com [10.33.10.82]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id C356B3F905; Fri, 29 May 2026 02:51:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1780048307; bh=SE5bStAb7vsrTWobe7ySsF8dvuF70BXHOSqTYvhTqMs=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=BU3NXZ6odiQGKiv4ch3U38/iXXT2G4zVAoLaxd347qKSWfZh2Oex4FNTLIVB1MCoC 89syUHZX0VKkV3neV4Ies8q8Rs9x27Qv5CD+986Gs3rJ/vn0EhWq6qKxzJcrGRo/DQ gtv48tUl+Nxj36R7rwGymxgYDnjNFPhF/p2I7Ke4= From: Ahmed Tiba Date: Fri, 29 May 2026 10:50:47 +0100 Subject: [PATCH v5 07/10] ACPI: APEI: introduce GHES helper Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-7-2e0500d42642@arm.com> References: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-0-2e0500d42642@arm.com> In-Reply-To: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-0-2e0500d42642@arm.com> To: will@kernel.org, jic23@kernel.org, xueshuai@linux.alibaba.com, saket.dumbre@intel.com, mchehab@kernel.org, dave@stgolabs.net, djbw@kernel.org, bp@alien8.de, tony.luck@intel.com, guohanjun@huawei.com, lenb@kernel.org, skhan@linuxfoundation.org, vishal.l.verma@intel.com, rafael@kernel.org, corbet@lwn.net, ira.weiny@intel.com, dave.jiang@intel.com, krzk+dt@kernel.org, robh@kernel.org, catalin.marinas@arm.com, alison.schofield@intel.com, conor+dt@kernel.org Cc: Ahmed Tiba , linux-arm-kernel@lists.infradead.org, Michael.Zhao2@arm.com, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-cxl@vger.kernel.org, Dmitry.Lamerov@arm.com, devicetree@vger.kernel.org, linux-acpi@vger.kernel.org, linux-edac@vger.kernel.org, acpica-devel@lists.linux.dev X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1780048269; l=3795; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=SE5bStAb7vsrTWobe7ySsF8dvuF70BXHOSqTYvhTqMs=; b=7czOdHXKZ2MOgr/mh/A0OH2WaB60dBSoL3E3nNReSRgpz8/Ku7ZegC1pW28v58DMWM8TA7u/b rnEgh4fECQIDGzAudhHAhzqCghhGMc76dqybMf8h0j6h5hL4Gr+yY6H X-Developer-Key: i=ahmed.tiba@arm.com; a=ed25519; pk=xVOtd+Qklh/4tuM3tB+BEZD4jj5a6W59C3KCNX6v7OE= Add a dedicated GHES_CPER_HELPERS Kconfig entry so the shared helper code can be built even when ACPI_APEI_GHES is disabled. Update the build glue and headers to depend on the new symbol. Signed-off-by: Ahmed Tiba --- drivers/Makefile | 1 + drivers/acpi/Kconfig | 4 ++++ drivers/acpi/apei/Kconfig | 1 + drivers/acpi/apei/Makefile | 2 +- include/acpi/ghes.h | 10 ++++++---- include/cxl/event.h | 2 +- 6 files changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/Makefile b/drivers/Makefile index 0841ea851847..27a664cb45ea 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -31,6 +31,7 @@ obj-y +=3D idle/ obj-y +=3D char/ipmi/ =20 obj-$(CONFIG_ACPI) +=3D acpi/ +obj-$(CONFIG_GHES_CPER_HELPERS) +=3D acpi/apei/ghes_cper.o =20 # PnP must come after ACPI since it will eventually need to check if acpi # was used and do nothing if so diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index f165d14cf61a..13ef0e99f840 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -6,6 +6,10 @@ config ARCH_SUPPORTS_ACPI bool =20 +config GHES_CPER_HELPERS + bool + select UEFI_CPER + menuconfig ACPI bool "ACPI (Advanced Configuration and Power Interface) Support" depends on ARCH_SUPPORTS_ACPI diff --git a/drivers/acpi/apei/Kconfig b/drivers/acpi/apei/Kconfig index 428458c623f0..ddb62638eb02 100644 --- a/drivers/acpi/apei/Kconfig +++ b/drivers/acpi/apei/Kconfig @@ -21,6 +21,7 @@ config ACPI_APEI_GHES bool "APEI Generic Hardware Error Source" depends on ACPI_APEI select ACPI_HED + select GHES_CPER_HELPERS select IRQ_WORK select GENERIC_ALLOCATOR select ARM_SDE_INTERFACE if ARM64 diff --git a/drivers/acpi/apei/Makefile b/drivers/acpi/apei/Makefile index f57f3b009d8e..66588d6be56f 100644 --- a/drivers/acpi/apei/Makefile +++ b/drivers/acpi/apei/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_ACPI_APEI) +=3D apei.o -obj-$(CONFIG_ACPI_APEI_GHES) +=3D ghes.o ghes_cper.o +obj-$(CONFIG_ACPI_APEI_GHES) +=3D ghes.o # clang versions prior to 18 may blow out the stack with KASAN ifeq ($(CONFIG_COMPILE_TEST)_$(CONFIG_CC_IS_CLANG)_$(call clang-min-versio= n, 180000),y_y_) KASAN_SANITIZE_ghes.o :=3D n diff --git a/include/acpi/ghes.h b/include/acpi/ghes.h index 8d7e5caef3f1..2ffab36b6154 100644 --- a/include/acpi/ghes.h +++ b/include/acpi/ghes.h @@ -83,15 +83,17 @@ int devm_ghes_register_vendor_record_notifier(struct de= vice *dev, struct notifier_block *nb); =20 struct list_head *ghes_get_devices(void); - -void ghes_estatus_pool_region_free(unsigned long addr, u32 size); #else static inline struct list_head *ghes_get_devices(void) { return NULL; } - -static inline void ghes_estatus_pool_region_free(unsigned long addr, u32 s= ize) { return; } #endif =20 +#ifdef CONFIG_GHES_CPER_HELPERS int ghes_estatus_pool_init(unsigned int num_ghes); +void ghes_estatus_pool_region_free(unsigned long addr, u32 size); +#else +static inline int ghes_estatus_pool_init(unsigned int num_ghes) { return -= ENODEV; } +static inline void ghes_estatus_pool_region_free(unsigned long addr, u32 s= ize) { } +#endif =20 static inline int acpi_hest_get_version(struct acpi_hest_generic_data *gda= ta) { diff --git a/include/cxl/event.h b/include/cxl/event.h index ff97fea718d2..2ebd65b0d9d6 100644 --- a/include/cxl/event.h +++ b/include/cxl/event.h @@ -285,7 +285,7 @@ struct cxl_cper_prot_err_work_data { int severity; }; =20 -#ifdef CONFIG_ACPI_APEI_GHES +#ifdef CONFIG_GHES_CPER_HELPERS int cxl_cper_register_work(struct work_struct *work); int cxl_cper_unregister_work(struct work_struct *work); int cxl_cper_kfifo_get(struct cxl_cper_work_data *wd); --=20 2.43.0 From nobody Mon Jun 8 12:17:15 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 7A56B3C9EDD; Fri, 29 May 2026 09:51:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780048315; cv=none; b=Bm+Inajpk9BwC8DUBEZHNyekhtzeTGHwV/8IGg24vM91Dz1Zme2eTA5rj4ZLN+sYzkcBitb4PQVWwdbeM3YsmD/lvMK2k8DzLJDtu2XbX2XlTwgb675ATmX7CVcMyAwkiCeumDSumv189fweydFrgJNgZoiMfLSAM+2qReVQuZc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780048315; c=relaxed/simple; bh=Nr8pbX6kuG+sJhGivkt1KNgOf7a+dvHPl5xri+bUnFA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=OcdXaekdb2FRQ+GX34mtatNzz18RyxX8HOTudNvd4BaoSX/FQadZ2b/ZWnr1b+tUsuoCqKkszSPCda+H0LmIYUytaIPv+Qr3f7lrbiLBBB7T8Q/Q3RoS6dW9VuNcw4nJYJgxfgr18Adv3Op0HrXxsjoiVeWgYhZyn08d61aEdJg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=ckRToeV9; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="ckRToeV9" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 0BDEF32E5; Fri, 29 May 2026 02:51:47 -0700 (PDT) Received: from e134710.arm.com (e134710.arm.com [10.33.10.82]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 818FE3F905; Fri, 29 May 2026 02:51:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1780048312; bh=Nr8pbX6kuG+sJhGivkt1KNgOf7a+dvHPl5xri+bUnFA=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=ckRToeV9DVJLWiQogo5nSgYezMWHblLuwGzVQEs/miTAdC1nJ3nR/eUS3opyj/dS9 33lfzigmcQH/38cvbAd/ZX29314KPdecwscD3kdGPXfAGqJxyqPUKGb4YHVnkmXSPB WS8WtMWDRJXvzrGoNHd1bwFrYvf4gIsZr1KcKisQ= From: Ahmed Tiba Date: Fri, 29 May 2026 10:50:48 +0100 Subject: [PATCH v5 08/10] ACPI: APEI: share GHES CPER helpers Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-8-2e0500d42642@arm.com> References: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-0-2e0500d42642@arm.com> In-Reply-To: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-0-2e0500d42642@arm.com> To: will@kernel.org, jic23@kernel.org, xueshuai@linux.alibaba.com, saket.dumbre@intel.com, mchehab@kernel.org, dave@stgolabs.net, djbw@kernel.org, bp@alien8.de, tony.luck@intel.com, guohanjun@huawei.com, lenb@kernel.org, skhan@linuxfoundation.org, vishal.l.verma@intel.com, rafael@kernel.org, corbet@lwn.net, ira.weiny@intel.com, dave.jiang@intel.com, krzk+dt@kernel.org, robh@kernel.org, catalin.marinas@arm.com, alison.schofield@intel.com, conor+dt@kernel.org Cc: Ahmed Tiba , linux-arm-kernel@lists.infradead.org, Michael.Zhao2@arm.com, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-cxl@vger.kernel.org, Dmitry.Lamerov@arm.com, devicetree@vger.kernel.org, linux-acpi@vger.kernel.org, linux-edac@vger.kernel.org, acpica-devel@lists.linux.dev X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1780048269; l=31155; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=Nr8pbX6kuG+sJhGivkt1KNgOf7a+dvHPl5xri+bUnFA=; b=pMq/wRrVIt0Tanj0rvk7XxF019BRNmXn6vsMohQl0oqel8s/6PFCW2guHP02IArrkiOXuUtK6 ynfc3oE3NHKA+siZKpZunVcKDUKnIZaGXV45Fu3BfDQnpXJl1HD1Zdv X-Developer-Key: i=ahmed.tiba@arm.com; a=ed25519; pk=xVOtd+Qklh/4tuM3tB+BEZD4jj5a6W59C3KCNX6v7OE= Wire GHES up to the helper routines in ghes_cper.c and remove the local copies from ghes.c. This keeps the control flow identical while letting the helpers be shared with other firmware-first providers. Signed-off-by: Ahmed Tiba --- drivers/acpi/apei/ghes.c | 416 +-------------------------------------- drivers/acpi/apei/ghes_cper.c | 438 ++++++++++++++++++++++++++++++++++++++= +++- include/acpi/ghes_cper.h | 20 ++ 3 files changed, 459 insertions(+), 415 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 85be2ebf4d3e..f85b97c4db4c 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -67,8 +67,6 @@ #define FIX_APEI_GHES_SDEI_CRITICAL __end_of_fixed_addresses #endif =20 -static ATOMIC_NOTIFIER_HEAD(ghes_report_chain); - /* * This driver isn't really modular, however for the time being, * continuing to use module_param is the easiest way to remain @@ -113,276 +111,6 @@ static DEFINE_MUTEX(ghes_devs_mutex); */ static DEFINE_SPINLOCK(ghes_notify_lock_irq); =20 -struct gen_pool *ghes_estatus_pool; - -int ghes_estatus_pool_init(unsigned int num_ghes) -{ - unsigned long addr, len; - int rc; - - ghes_estatus_pool =3D gen_pool_create(GHES_ESTATUS_POOL_MIN_ALLOC_ORDER, = -1); - if (!ghes_estatus_pool) - return -ENOMEM; - - len =3D GHES_ESTATUS_CACHE_AVG_SIZE * GHES_ESTATUS_CACHE_ALLOCED_MAX; - len +=3D (num_ghes * GHES_ESOURCE_PREALLOC_MAX_SIZE); - - addr =3D (unsigned long)vmalloc(PAGE_ALIGN(len)); - if (!addr) - goto err_pool_alloc; - - rc =3D gen_pool_add(ghes_estatus_pool, addr, PAGE_ALIGN(len), -1); - if (rc) - goto err_pool_add; - - return 0; - -err_pool_add: - vfree((void *)addr); - -err_pool_alloc: - gen_pool_destroy(ghes_estatus_pool); - - return -ENOMEM; -} - -/** - * ghes_estatus_pool_region_free - free previously allocated memory - * from the ghes_estatus_pool. - * @addr: address of memory to free. - * @size: size of memory to free. - * - * Returns none. - */ -void ghes_estatus_pool_region_free(unsigned long addr, u32 size) -{ - gen_pool_free(ghes_estatus_pool, addr, size); -} -EXPORT_SYMBOL_GPL(ghes_estatus_pool_region_free); - -static inline int ghes_severity(int severity) -{ - switch (severity) { - case CPER_SEV_INFORMATIONAL: - return GHES_SEV_NO; - case CPER_SEV_CORRECTED: - return GHES_SEV_CORRECTED; - case CPER_SEV_RECOVERABLE: - return GHES_SEV_RECOVERABLE; - case CPER_SEV_FATAL: - return GHES_SEV_PANIC; - default: - /* Unknown, go panic */ - return GHES_SEV_PANIC; - } -} - - -/** - * struct ghes_task_work - for synchronous RAS event - * - * @twork: callback_head for task work - * @pfn: page frame number of corrupted page - * @flags: work control flags - * - * Structure to pass task work to be handled before - * returning to user-space via task_work_add(). - */ -struct ghes_task_work { - struct callback_head twork; - u64 pfn; - int flags; -}; - -static void memory_failure_cb(struct callback_head *twork) -{ - struct ghes_task_work *twcb =3D container_of(twork, struct ghes_task_work= , twork); - int ret; - - ret =3D memory_failure(twcb->pfn, twcb->flags); - gen_pool_free(ghes_estatus_pool, (unsigned long)twcb, sizeof(*twcb)); - - if (!ret || ret =3D=3D -EHWPOISON || ret =3D=3D -EOPNOTSUPP) - return; - - pr_err("%#llx: Sending SIGBUS to %s:%d due to hardware memory corruption\= n", - twcb->pfn, current->comm, task_pid_nr(current)); - force_sig(SIGBUS); -} - -static bool ghes_do_memory_failure(u64 physical_addr, int flags) -{ - struct ghes_task_work *twcb; - unsigned long pfn; - - if (!IS_ENABLED(CONFIG_ACPI_APEI_MEMORY_FAILURE)) - return false; - - pfn =3D PHYS_PFN(physical_addr); - - if (flags =3D=3D MF_ACTION_REQUIRED && current->mm) { - twcb =3D (void *)gen_pool_alloc(ghes_estatus_pool, sizeof(*twcb)); - if (!twcb) - return false; - - twcb->pfn =3D pfn; - twcb->flags =3D flags; - init_task_work(&twcb->twork, memory_failure_cb); - task_work_add(current, &twcb->twork, TWA_RESUME); - return true; - } - - memory_failure_queue(pfn, flags); - return true; -} - -static bool ghes_handle_memory_failure(struct acpi_hest_generic_data *gdat= a, - int sev, bool sync) -{ - int flags =3D -1; - int sec_sev =3D ghes_severity(gdata->error_severity); - struct cper_sec_mem_err *mem_err =3D acpi_hest_get_payload(gdata); - - if (!(mem_err->validation_bits & CPER_MEM_VALID_PA)) - return false; - - /* iff following two events can be handled properly by now */ - if (sec_sev =3D=3D GHES_SEV_CORRECTED && - (gdata->flags & CPER_SEC_ERROR_THRESHOLD_EXCEEDED)) - flags =3D MF_SOFT_OFFLINE; - if (sev =3D=3D GHES_SEV_RECOVERABLE && sec_sev =3D=3D GHES_SEV_RECOVERABL= E) - flags =3D sync ? MF_ACTION_REQUIRED : 0; - - if (flags !=3D -1) - return ghes_do_memory_failure(mem_err->physical_addr, flags); - - return false; -} - -static bool ghes_handle_arm_hw_error(struct acpi_hest_generic_data *gdata, - int sev, bool sync) -{ - struct cper_sec_proc_arm *err =3D acpi_hest_get_payload(gdata); - int flags =3D sync ? MF_ACTION_REQUIRED : 0; - int length =3D gdata->error_data_length; - char error_type[120]; - bool queued =3D false; - int sec_sev, i; - char *p; - - sec_sev =3D ghes_severity(gdata->error_severity); - if (length >=3D sizeof(*err)) { - log_arm_hw_error(err, sec_sev); - } else { - pr_warn(FW_BUG "arm error length: %d\n", length); - pr_warn(FW_BUG "length is too small\n"); - pr_warn(FW_BUG "firmware-generated error record is incorrect\n"); - return false; - } - - if (sev !=3D GHES_SEV_RECOVERABLE || sec_sev !=3D GHES_SEV_RECOVERABLE) - return false; - - p =3D (char *)(err + 1); - length -=3D sizeof(err); - - for (i =3D 0; i < err->err_info_num; i++) { - struct cper_arm_err_info *err_info; - bool is_cache, has_pa; - - /* Ensure we have enough data for the error info header */ - if (length < sizeof(*err_info)) - break; - - err_info =3D (struct cper_arm_err_info *)p; - - /* Validate the claimed length before using it */ - length -=3D err_info->length; - if (length < 0) - break; - - is_cache =3D err_info->type & CPER_ARM_CACHE_ERROR; - has_pa =3D (err_info->validation_bits & CPER_ARM_INFO_VALID_PHYSICAL_ADD= R); - - /* - * The field (err_info->error_info & BIT(26)) is fixed to set to - * 1 in some old firmware of HiSilicon Kunpeng920. We assume that - * firmware won't mix corrected errors in an uncorrected section, - * and don't filter out 'corrected' error here. - */ - if (is_cache && has_pa) { - queued =3D ghes_do_memory_failure(err_info->physical_fault_addr, flags); - p +=3D err_info->length; - continue; - } - - cper_bits_to_str(error_type, sizeof(error_type), - FIELD_GET(CPER_ARM_ERR_TYPE_MASK, err_info->type), - cper_proc_error_type_strs, - ARRAY_SIZE(cper_proc_error_type_strs)); - - pr_warn_ratelimited(FW_WARN GHES_PFX - "Unhandled processor error type 0x%02x: %s%s\n", - err_info->type, error_type, - (err_info->type & ~CPER_ARM_ERR_TYPE_MASK) ? " with reserved bit(s= )" : ""); - p +=3D err_info->length; - } - - return queued; -} - -/* - * PCIe AER errors need to be sent to the AER driver for reporting and - * recovery. The GHES severities map to the following AER severities and - * require the following handling: - * - * GHES_SEV_CORRECTABLE -> AER_CORRECTABLE - * These need to be reported by the AER driver but no recovery is - * necessary. - * GHES_SEV_RECOVERABLE -> AER_NONFATAL - * GHES_SEV_RECOVERABLE && CPER_SEC_RESET -> AER_FATAL - * These both need to be reported and recovered from by the AER driver. - * GHES_SEV_PANIC does not make it to this handling since the kernel must - * panic. - */ -static void ghes_handle_aer(struct acpi_hest_generic_data *gdata) -{ -#ifdef CONFIG_ACPI_APEI_PCIEAER - struct cper_sec_pcie *pcie_err =3D acpi_hest_get_payload(gdata); - - if (pcie_err->validation_bits & CPER_PCIE_VALID_DEVICE_ID && - pcie_err->validation_bits & CPER_PCIE_VALID_AER_INFO) { - unsigned int devfn; - int aer_severity; - u8 *aer_info; - - devfn =3D PCI_DEVFN(pcie_err->device_id.device, - pcie_err->device_id.function); - aer_severity =3D cper_severity_to_aer(gdata->error_severity); - - /* - * If firmware reset the component to contain - * the error, we must reinitialize it before - * use, so treat it as a fatal AER error. - */ - if (gdata->flags & CPER_SEC_RESET) - aer_severity =3D AER_FATAL; - - aer_info =3D (void *)gen_pool_alloc(ghes_estatus_pool, - sizeof(struct aer_capability_regs)); - if (!aer_info) - return; - memcpy(aer_info, pcie_err->aer_info, sizeof(struct aer_capability_regs)); - - aer_recover_queue(pcie_err->device_id.segment, - pcie_err->device_id.bus, - devfn, aer_severity, - (struct aer_capability_regs *) - aer_info); - } -#endif -} - static void ghes_vendor_record_notifier_destroy(void *nb) { ghes_unregister_vendor_record_notifier(nb); @@ -401,151 +129,11 @@ int devm_ghes_register_vendor_record_notifier(struct= device *dev, } EXPORT_SYMBOL_GPL(devm_ghes_register_vendor_record_notifier); =20 -static void ghes_log_hwerr(int sev, guid_t *sec_type) -{ - if (sev !=3D CPER_SEV_RECOVERABLE) - return; - - if (guid_equal(sec_type, &CPER_SEC_PROC_ARM) || - guid_equal(sec_type, &CPER_SEC_PROC_GENERIC) || - guid_equal(sec_type, &CPER_SEC_PROC_IA)) { - hwerr_log_error_type(HWERR_RECOV_CPU); - return; - } - - if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR) || - guid_equal(sec_type, &CPER_SEC_CXL_GEN_MEDIA_GUID) || - guid_equal(sec_type, &CPER_SEC_CXL_DRAM_GUID) || - guid_equal(sec_type, &CPER_SEC_CXL_MEM_MODULE_GUID)) { - hwerr_log_error_type(HWERR_RECOV_CXL); - return; - } - - if (guid_equal(sec_type, &CPER_SEC_PCIE) || - guid_equal(sec_type, &CPER_SEC_PCI_X_BUS)) { - hwerr_log_error_type(HWERR_RECOV_PCI); - return; - } - - if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) { - hwerr_log_error_type(HWERR_RECOV_MEMORY); - return; - } - - hwerr_log_error_type(HWERR_RECOV_OTHERS); -} - static void ghes_do_proc(struct ghes *ghes, const struct acpi_hest_generic_status *estatus) { - int sev, sec_sev; - struct acpi_hest_generic_data *gdata; - guid_t *sec_type; - const guid_t *fru_id =3D &guid_null; - char *fru_text =3D ""; - bool queued =3D false; - bool sync =3D is_hest_sync_notify(ghes); - - sev =3D ghes_severity(estatus->error_severity); - apei_estatus_for_each_section(estatus, gdata) { - sec_type =3D (guid_t *)gdata->section_type; - sec_sev =3D ghes_severity(gdata->error_severity); - if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID) - fru_id =3D (guid_t *)gdata->fru_id; - - if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) - fru_text =3D gdata->fru_text; - - ghes_log_hwerr(sev, sec_type); - if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) { - struct cper_sec_mem_err *mem_err =3D acpi_hest_get_payload(gdata); - - atomic_notifier_call_chain(&ghes_report_chain, sev, mem_err); - - arch_apei_report_mem_error(sev, mem_err); - queued =3D ghes_handle_memory_failure(gdata, sev, sync); - } else if (guid_equal(sec_type, &CPER_SEC_PCIE)) { - ghes_handle_aer(gdata); - } else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) { - queued =3D ghes_handle_arm_hw_error(gdata, sev, sync); - } else if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR)) { - struct cxl_cper_sec_prot_err *prot_err =3D acpi_hest_get_payload(gdata); - - cxl_cper_post_prot_err(prot_err, gdata->error_severity); - } else if (guid_equal(sec_type, &CPER_SEC_CXL_GEN_MEDIA_GUID)) { - struct cxl_cper_event_rec *rec =3D acpi_hest_get_payload(gdata); - - cxl_cper_post_event(CXL_CPER_EVENT_GEN_MEDIA, rec); - } else if (guid_equal(sec_type, &CPER_SEC_CXL_DRAM_GUID)) { - struct cxl_cper_event_rec *rec =3D acpi_hest_get_payload(gdata); - - cxl_cper_post_event(CXL_CPER_EVENT_DRAM, rec); - } else if (guid_equal(sec_type, &CPER_SEC_CXL_MEM_MODULE_GUID)) { - struct cxl_cper_event_rec *rec =3D acpi_hest_get_payload(gdata); - - cxl_cper_post_event(CXL_CPER_EVENT_MEM_MODULE, rec); - } else { - void *err =3D acpi_hest_get_payload(gdata); - - ghes_defer_non_standard_event(gdata, sev); - log_non_standard_event(sec_type, fru_id, fru_text, - sec_sev, err, - gdata->error_data_length); - } - } - - /* - * If no memory failure work is queued for abnormal synchronous - * errors, do a force kill. - */ - if (sync && !queued) { - dev_err(ghes->dev, - HW_ERR GHES_PFX "%s:%d: synchronous unrecoverable error (SIGBUS)\n", - current->comm, task_pid_nr(current)); - force_sig(SIGBUS); - } -} - -static void __ghes_print_estatus(const char *pfx, - const struct acpi_hest_generic *generic, - const struct acpi_hest_generic_status *estatus) -{ - static atomic_t seqno; - unsigned int curr_seqno; - char pfx_seq[64]; - - if (pfx =3D=3D NULL) { - if (ghes_severity(estatus->error_severity) <=3D - GHES_SEV_CORRECTED) - pfx =3D KERN_WARNING; - else - pfx =3D KERN_ERR; - } - curr_seqno =3D atomic_inc_return(&seqno); - snprintf(pfx_seq, sizeof(pfx_seq), "%s{%u}" HW_ERR, pfx, curr_seqno); - printk("%s""Hardware error from APEI Generic Hardware Error Source: %d\n", - pfx_seq, generic->header.source_id); - cper_estatus_print(pfx_seq, estatus); -} - -static int ghes_print_estatus(const char *pfx, - const struct acpi_hest_generic *generic, - const struct acpi_hest_generic_status *estatus) -{ - /* Not more than 2 messages every 5 seconds */ - static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5*HZ, 2); - static DEFINE_RATELIMIT_STATE(ratelimit_uncorrected, 5*HZ, 2); - struct ratelimit_state *ratelimit; - - if (ghes_severity(estatus->error_severity) <=3D GHES_SEV_CORRECTED) - ratelimit =3D &ratelimit_corrected; - else - ratelimit =3D &ratelimit_uncorrected; - if (__ratelimit(ratelimit)) { - __ghes_print_estatus(pfx, generic, estatus); - return 1; - } - return 0; + ghes_cper_handle_status(ghes->dev, ghes->generic, + estatus, is_hest_sync_notify(ghes)); } =20 static void __ghes_panic(struct ghes *ghes, diff --git a/drivers/acpi/apei/ghes_cper.c b/drivers/acpi/apei/ghes_cper.c index d7a666a163c3..0ff9d06eb78f 100644 --- a/drivers/acpi/apei/ghes_cper.c +++ b/drivers/acpi/apei/ghes_cper.c @@ -13,22 +13,32 @@ */ =20 #include +#include +#include #include #include -#include #include +#include #include #include #include #include #include +#include +#include +#include +#include #include #include +#include +#include #include #include #include #include #include +#include +#include =20 #include #include @@ -38,9 +48,363 @@ =20 #include "apei-internal.h" =20 +ATOMIC_NOTIFIER_HEAD(ghes_report_chain); + +#ifndef CONFIG_ACPI_APEI +void __weak arch_apei_report_mem_error(int sev, struct cper_sec_mem_err *m= em_err) { } +#endif + static struct ghes_estatus_cache __rcu *ghes_estatus_caches[GHES_ESTATUS_C= ACHES_SIZE]; static atomic_t ghes_estatus_cache_alloced; =20 +struct gen_pool *ghes_estatus_pool; + +int ghes_estatus_pool_init(unsigned int num_ghes) +{ + unsigned long addr, len; + int rc; + + ghes_estatus_pool =3D gen_pool_create(GHES_ESTATUS_POOL_MIN_ALLOC_ORDER, = -1); + if (!ghes_estatus_pool) + return -ENOMEM; + + len =3D GHES_ESTATUS_CACHE_AVG_SIZE * GHES_ESTATUS_CACHE_ALLOCED_MAX; + len +=3D (num_ghes * GHES_ESOURCE_PREALLOC_MAX_SIZE); + + addr =3D (unsigned long)vmalloc(PAGE_ALIGN(len)); + if (!addr) + goto err_pool_alloc; + + rc =3D gen_pool_add(ghes_estatus_pool, addr, PAGE_ALIGN(len), -1); + if (rc) + goto err_pool_add; + + return 0; + +err_pool_add: + vfree((void *)addr); + +err_pool_alloc: + gen_pool_destroy(ghes_estatus_pool); + + return -ENOMEM; +} +EXPORT_SYMBOL_GPL(ghes_estatus_pool_init); + +/** + * ghes_estatus_pool_region_free - free previously allocated memory + * from the ghes_estatus_pool. + * @addr: address of memory to free. + * @size: size of memory to free. + * + * Returns none. + */ +void ghes_estatus_pool_region_free(unsigned long addr, u32 size) +{ + gen_pool_free(ghes_estatus_pool, addr, size); +} +EXPORT_SYMBOL_GPL(ghes_estatus_pool_region_free); + +int ghes_severity(int severity) +{ + switch (severity) { + case CPER_SEV_INFORMATIONAL: + return GHES_SEV_NO; + case CPER_SEV_CORRECTED: + return GHES_SEV_CORRECTED; + case CPER_SEV_RECOVERABLE: + return GHES_SEV_RECOVERABLE; + case CPER_SEV_FATAL: + return GHES_SEV_PANIC; + default: + /* Unknown, go panic */ + return GHES_SEV_PANIC; + } +} + +/** + * struct ghes_task_work - for synchronous RAS event + * + * @twork: callback_head for task work + * @pfn: page frame number of corrupted page + * @flags: work control flags + * + * Structure to pass task work to be handled before + * returning to user-space via task_work_add(). + */ +struct ghes_task_work { + struct callback_head twork; + u64 pfn; + int flags; +}; + +static void memory_failure_cb(struct callback_head *twork) +{ + struct ghes_task_work *twcb =3D container_of(twork, struct ghes_task_work= , twork); + int ret; + + ret =3D memory_failure(twcb->pfn, twcb->flags); + gen_pool_free(ghes_estatus_pool, (unsigned long)twcb, sizeof(*twcb)); + + if (!ret || ret =3D=3D -EHWPOISON || ret =3D=3D -EOPNOTSUPP) + return; + + pr_err("%#llx: Sending SIGBUS to %s:%d due to hardware memory corruption\= n", + twcb->pfn, current->comm, task_pid_nr(current)); + force_sig(SIGBUS); +} + +static bool ghes_do_memory_failure(u64 physical_addr, int flags) +{ + struct ghes_task_work *twcb; + unsigned long pfn; + + if (!IS_ENABLED(CONFIG_ACPI_APEI_MEMORY_FAILURE)) + return false; + + pfn =3D PHYS_PFN(physical_addr); + + if (flags =3D=3D MF_ACTION_REQUIRED && current->mm) { + twcb =3D (void *)gen_pool_alloc(ghes_estatus_pool, sizeof(*twcb)); + if (!twcb) + return false; + + twcb->pfn =3D pfn; + twcb->flags =3D flags; + init_task_work(&twcb->twork, memory_failure_cb); + task_work_add(current, &twcb->twork, TWA_RESUME); + return true; + } + + memory_failure_queue(pfn, flags); + return true; +} + +bool ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata, + int sev, bool sync) +{ + int flags =3D -1; + int sec_sev =3D ghes_severity(gdata->error_severity); + struct cper_sec_mem_err *mem_err =3D acpi_hest_get_payload(gdata); + + if (!(mem_err->validation_bits & CPER_MEM_VALID_PA)) + return false; + + /* iff following two events can be handled properly by now */ + if (sec_sev =3D=3D GHES_SEV_CORRECTED && + (gdata->flags & CPER_SEC_ERROR_THRESHOLD_EXCEEDED)) + flags =3D MF_SOFT_OFFLINE; + if (sev =3D=3D GHES_SEV_RECOVERABLE && sec_sev =3D=3D GHES_SEV_RECOVERABL= E) + flags =3D sync ? MF_ACTION_REQUIRED : 0; + + if (flags !=3D -1) + return ghes_do_memory_failure(mem_err->physical_addr, flags); + + return false; +} + +bool ghes_handle_arm_hw_error(struct acpi_hest_generic_data *gdata, + int sev, bool sync) +{ + struct cper_sec_proc_arm *err =3D acpi_hest_get_payload(gdata); + int flags =3D sync ? MF_ACTION_REQUIRED : 0; + int length =3D gdata->error_data_length; + char error_type[120]; + bool queued =3D false; + int sec_sev, i; + char *p; + + sec_sev =3D ghes_severity(gdata->error_severity); + if (length >=3D sizeof(*err)) { + log_arm_hw_error(err, sec_sev); + } else { + pr_warn(FW_BUG "arm error length: %d\n", length); + pr_warn(FW_BUG "length is too small\n"); + pr_warn(FW_BUG "firmware-generated error record is incorrect\n"); + return false; + } + + if (sev !=3D GHES_SEV_RECOVERABLE || sec_sev !=3D GHES_SEV_RECOVERABLE) + return false; + + p =3D (char *)(err + 1); + length -=3D sizeof(err); + + for (i =3D 0; i < err->err_info_num; i++) { + struct cper_arm_err_info *err_info; + bool is_cache, has_pa; + + /* Ensure we have enough data for the error info header */ + if (length < sizeof(*err_info)) + break; + + err_info =3D (struct cper_arm_err_info *)p; + + /* Validate the claimed length before using it */ + length -=3D err_info->length; + if (length < 0) + break; + + is_cache =3D err_info->type & CPER_ARM_CACHE_ERROR; + has_pa =3D (err_info->validation_bits & CPER_ARM_INFO_VALID_PHYSICAL_ADD= R); + + /* + * The field (err_info->error_info & BIT(26)) is fixed to set to + * 1 in some old firmware of HiSilicon Kunpeng920. We assume that + * firmware won't mix corrected errors in an uncorrected section, + * and don't filter out 'corrected' error here. + */ + if (is_cache && has_pa) { + queued =3D ghes_do_memory_failure(err_info->physical_fault_addr, flags); + p +=3D err_info->length; + continue; + } + + cper_bits_to_str(error_type, sizeof(error_type), + FIELD_GET(CPER_ARM_ERR_TYPE_MASK, err_info->type), + cper_proc_error_type_strs, + ARRAY_SIZE(cper_proc_error_type_strs)); + + pr_warn_ratelimited(FW_WARN GHES_PFX + "Unhandled processor error type 0x%02x: %s%s\n", + err_info->type, error_type, + err_info->type & ~CPER_ARM_ERR_TYPE_MASK ? + " with reserved bit(s)" : ""); + p +=3D err_info->length; + } + + return queued; +} + +/* + * PCIe AER errors need to be sent to the AER driver for reporting and + * recovery. The GHES severities map to the following AER severities and + * require the following handling: + * + * GHES_SEV_CORRECTABLE -> AER_CORRECTABLE + * These need to be reported by the AER driver but no recovery is + * necessary. + * GHES_SEV_RECOVERABLE -> AER_NONFATAL + * GHES_SEV_RECOVERABLE && CPER_SEC_RESET -> AER_FATAL + * These both need to be reported and recovered from by the AER driver. + * GHES_SEV_PANIC does not make it to this handling since the kernel must + * panic. + */ +void ghes_handle_aer(struct acpi_hest_generic_data *gdata) +{ +#ifdef CONFIG_ACPI_APEI_PCIEAER + struct cper_sec_pcie *pcie_err =3D acpi_hest_get_payload(gdata); + + if (pcie_err->validation_bits & CPER_PCIE_VALID_DEVICE_ID && + pcie_err->validation_bits & CPER_PCIE_VALID_AER_INFO) { + unsigned int devfn; + int aer_severity; + u8 *aer_info; + + devfn =3D PCI_DEVFN(pcie_err->device_id.device, + pcie_err->device_id.function); + aer_severity =3D cper_severity_to_aer(gdata->error_severity); + + /* + * If firmware reset the component to contain + * the error, we must reinitialize it before + * use, so treat it as a fatal AER error. + */ + if (gdata->flags & CPER_SEC_RESET) + aer_severity =3D AER_FATAL; + + aer_info =3D (void *)gen_pool_alloc(ghes_estatus_pool, + sizeof(struct aer_capability_regs)); + if (!aer_info) + return; + memcpy(aer_info, pcie_err->aer_info, sizeof(struct aer_capability_regs)); + + aer_recover_queue(pcie_err->device_id.segment, + pcie_err->device_id.bus, + devfn, aer_severity, + (struct aer_capability_regs *) + aer_info); + } +#endif +} + +void ghes_log_hwerr(int sev, guid_t *sec_type) +{ + if (sev !=3D CPER_SEV_RECOVERABLE) + return; + + if (guid_equal(sec_type, &CPER_SEC_PROC_ARM) || + guid_equal(sec_type, &CPER_SEC_PROC_GENERIC) || + guid_equal(sec_type, &CPER_SEC_PROC_IA)) { + hwerr_log_error_type(HWERR_RECOV_CPU); + return; + } + + if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR) || + guid_equal(sec_type, &CPER_SEC_CXL_GEN_MEDIA_GUID) || + guid_equal(sec_type, &CPER_SEC_CXL_DRAM_GUID) || + guid_equal(sec_type, &CPER_SEC_CXL_MEM_MODULE_GUID)) { + hwerr_log_error_type(HWERR_RECOV_CXL); + return; + } + + if (guid_equal(sec_type, &CPER_SEC_PCIE) || + guid_equal(sec_type, &CPER_SEC_PCI_X_BUS)) { + hwerr_log_error_type(HWERR_RECOV_PCI); + return; + } + + if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) { + hwerr_log_error_type(HWERR_RECOV_MEMORY); + return; + } + + hwerr_log_error_type(HWERR_RECOV_OTHERS); +} + +void __ghes_print_estatus(const char *pfx, + const struct acpi_hest_generic *generic, + const struct acpi_hest_generic_status *estatus) +{ + static atomic_t seqno; + unsigned int curr_seqno; + char pfx_seq[64]; + + if (!pfx) { + if (ghes_severity(estatus->error_severity) <=3D + GHES_SEV_CORRECTED) + pfx =3D KERN_WARNING; + else + pfx =3D KERN_ERR; + } + curr_seqno =3D atomic_inc_return(&seqno); + snprintf(pfx_seq, sizeof(pfx_seq), "%s{%u}" HW_ERR, pfx, curr_seqno); + printk("%sHardware error from APEI Generic Hardware Error Source: %d\n", + pfx_seq, generic->header.source_id); + cper_estatus_print(pfx_seq, estatus); +} + +int ghes_print_estatus(const char *pfx, + const struct acpi_hest_generic *generic, + const struct acpi_hest_generic_status *estatus) +{ + /* Not more than 2 messages every 5 seconds */ + static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5 * HZ, 2); + static DEFINE_RATELIMIT_STATE(ratelimit_uncorrected, 5 * HZ, 2); + struct ratelimit_state *ratelimit; + + if (ghes_severity(estatus->error_severity) <=3D GHES_SEV_CORRECTED) + ratelimit =3D &ratelimit_corrected; + else + ratelimit =3D &ratelimit_uncorrected; + if (__ratelimit(ratelimit)) { + __ghes_print_estatus(pfx, generic, estatus); + return 1; + } + return 0; +} + +#ifdef CONFIG_ACPI_APEI static void __iomem *ghes_map(u64 pfn, enum fixed_addresses fixmap_idx) { phys_addr_t paddr; @@ -272,6 +636,7 @@ void ghes_clear_estatus(struct ghes *ghes, if (is_hest_type_generic_v2(ghes)) ghes_ack_error(ghes->generic_v2); } +#endif /* CONFIG_ACPI_APEI */ =20 static BLOCKING_NOTIFIER_HEAD(vendor_record_notify_list); =20 @@ -323,6 +688,77 @@ void ghes_defer_non_standard_event(struct acpi_hest_ge= neric_data *gdata, schedule_work(&entry->work); } =20 +void ghes_cper_handle_status(struct device *dev, + const struct acpi_hest_generic *generic, + const struct acpi_hest_generic_status *estatus, + bool sync) +{ + int sev, sec_sev; + struct acpi_hest_generic_data *gdata; + guid_t *sec_type; + const guid_t *fru_id =3D &guid_null; + char *fru_text =3D ""; + bool queued =3D false; + + sev =3D ghes_severity(estatus->error_severity); + apei_estatus_for_each_section(estatus, gdata) { + sec_type =3D (guid_t *)gdata->section_type; + sec_sev =3D ghes_severity(gdata->error_severity); + if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID) + fru_id =3D (guid_t *)gdata->fru_id; + + if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) + fru_text =3D gdata->fru_text; + + ghes_log_hwerr(sev, sec_type); + if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) { + struct cper_sec_mem_err *mem_err =3D acpi_hest_get_payload(gdata); + + atomic_notifier_call_chain(&ghes_report_chain, sev, mem_err); + + arch_apei_report_mem_error(sev, mem_err); + queued =3D ghes_handle_memory_failure(gdata, sev, sync); + } else if (guid_equal(sec_type, &CPER_SEC_PCIE)) { + ghes_handle_aer(gdata); + } else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) { + queued =3D ghes_handle_arm_hw_error(gdata, sev, sync); + } else if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR)) { + struct cxl_cper_sec_prot_err *prot_err =3D acpi_hest_get_payload(gdata); + + cxl_cper_post_prot_err(prot_err, gdata->error_severity); + } else if (guid_equal(sec_type, &CPER_SEC_CXL_GEN_MEDIA_GUID)) { + struct cxl_cper_event_rec *rec =3D acpi_hest_get_payload(gdata); + + cxl_cper_post_event(CXL_CPER_EVENT_GEN_MEDIA, rec); + } else if (guid_equal(sec_type, &CPER_SEC_CXL_DRAM_GUID)) { + struct cxl_cper_event_rec *rec =3D acpi_hest_get_payload(gdata); + + cxl_cper_post_event(CXL_CPER_EVENT_DRAM, rec); + } else if (guid_equal(sec_type, &CPER_SEC_CXL_MEM_MODULE_GUID)) { + struct cxl_cper_event_rec *rec =3D acpi_hest_get_payload(gdata); + + cxl_cper_post_event(CXL_CPER_EVENT_MEM_MODULE, rec); + } else { + void *err =3D acpi_hest_get_payload(gdata); + + ghes_defer_non_standard_event(gdata, sev); + log_non_standard_event(sec_type, fru_id, fru_text, + sec_sev, err, + gdata->error_data_length); + } + } + + /* + * If no memory failure work is queued for abnormal synchronous + * errors, do a force kill. + */ + if (sync && !queued) { + dev_err(dev, + HW_ERR GHES_PFX "%s:%d: synchronous unrecoverable error (SIGBUS)\n", + current->comm, task_pid_nr(current)); + force_sig(SIGBUS); + } +} /* Room for 8 entries */ #define CXL_CPER_PROT_ERR_FIFO_DEPTH 8 static DEFINE_KFIFO(cxl_cper_prot_err_fifo, struct cxl_cper_prot_err_work_= data, diff --git a/include/acpi/ghes_cper.h b/include/acpi/ghes_cper.h index dd49e9179b63..511b95b50911 100644 --- a/include/acpi/ghes_cper.h +++ b/include/acpi/ghes_cper.h @@ -17,6 +17,8 @@ #define ACPI_APEI_GHES_CPER_H =20 #include +#include +#include #include =20 #include @@ -57,6 +59,7 @@ ((struct ghes_vendor_record_entry *)(vendor_entry) + 1)) =20 extern struct gen_pool *ghes_estatus_pool; +extern struct atomic_notifier_head ghes_report_chain; =20 static inline bool is_hest_type_generic_v2(struct ghes *ghes) { @@ -107,6 +110,23 @@ void ghes_estatus_cache_add(struct acpi_hest_generic *= generic, struct acpi_hest_generic_status *estatus); void ghes_defer_non_standard_event(struct acpi_hest_generic_data *gdata, int sev); +int ghes_severity(int severity); +bool ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata, + int sev, bool sync); +bool ghes_handle_arm_hw_error(struct acpi_hest_generic_data *gdata, + int sev, bool sync); +void ghes_handle_aer(struct acpi_hest_generic_data *gdata); +void ghes_log_hwerr(int sev, guid_t *sec_type); +void __ghes_print_estatus(const char *pfx, + const struct acpi_hest_generic *generic, + const struct acpi_hest_generic_status *estatus); +int ghes_print_estatus(const char *pfx, + const struct acpi_hest_generic *generic, + const struct acpi_hest_generic_status *estatus); +void ghes_cper_handle_status(struct device *dev, + const struct acpi_hest_generic *generic, + const struct acpi_hest_generic_status *estatus, + bool sync); void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err, int severity); int cxl_cper_register_prot_err_work(struct work_struct *work); --=20 2.43.0 From nobody Mon Jun 8 12:17:15 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 5D7E43C5525; Fri, 29 May 2026 09:51:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780048318; cv=none; b=bPXlYKn+MzIJj1HULxURb9PE2qIYtvF7eiMQbsM4+p7pUqRnjZRLj/4kETKgBVExdNYCJtppTaERwGQEKFVn4J0qXnKAHmJSYu6HYFSya/H2WuGz3fNwkAAfP9ijk/HN8Ne8XFW3zmcGL1X0ceB/ANXXJjgRGuwncSQnjpxwLq4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780048318; c=relaxed/simple; bh=G1ftAdKNVg9HYWt6/7QhaAQawKb5RYu0DeFnL5fEvGQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=oPFdRNf9b+lcFCSig4+dgelk6jZ7T687H7C6iIDe1cT1zsYW5qbYR/arRS/Wdbi4iKWYvaB85gzciCiB8gEANQh1G/TMvaDMcI4rXLjw9XHWIoaxaMrMb6XXZxlq5v+RTTMinES3TtquwXL2myXe16DGgrT++LWpvFibZok8iRM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=l098FqpO; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="l098FqpO" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id B913422D7; Fri, 29 May 2026 02:51:51 -0700 (PDT) Received: from e134710.arm.com (e134710.arm.com [10.33.10.82]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 5B4AE3F905; Fri, 29 May 2026 02:51:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1780048316; bh=G1ftAdKNVg9HYWt6/7QhaAQawKb5RYu0DeFnL5fEvGQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=l098FqpO1fiETy/PtHT1VY75tQtwkAoOGNUItobgKu8uMq+/fOGXZe6ks1JsDmhnq ozj3xwjuCol0qwfvZwqZknHvXKm0TGTTEKgadBYMLv3FiMnnvk0mKGC1cBz0INc3/j 4ZPPPW85NyTMKTlBLysWhmxEun0qdMHrs0zCRTkc= From: Ahmed Tiba Date: Fri, 29 May 2026 10:50:49 +0100 Subject: [PATCH v5 09/10] dt-bindings: firmware: add arm,ras-cper Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-9-2e0500d42642@arm.com> References: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-0-2e0500d42642@arm.com> In-Reply-To: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-0-2e0500d42642@arm.com> To: will@kernel.org, jic23@kernel.org, xueshuai@linux.alibaba.com, saket.dumbre@intel.com, mchehab@kernel.org, dave@stgolabs.net, djbw@kernel.org, bp@alien8.de, tony.luck@intel.com, guohanjun@huawei.com, lenb@kernel.org, skhan@linuxfoundation.org, vishal.l.verma@intel.com, rafael@kernel.org, corbet@lwn.net, ira.weiny@intel.com, dave.jiang@intel.com, krzk+dt@kernel.org, robh@kernel.org, catalin.marinas@arm.com, alison.schofield@intel.com, conor+dt@kernel.org Cc: Ahmed Tiba , linux-arm-kernel@lists.infradead.org, Michael.Zhao2@arm.com, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-cxl@vger.kernel.org, Dmitry.Lamerov@arm.com, devicetree@vger.kernel.org, linux-acpi@vger.kernel.org, linux-edac@vger.kernel.org, acpica-devel@lists.linux.dev X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1780048269; l=2722; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=G1ftAdKNVg9HYWt6/7QhaAQawKb5RYu0DeFnL5fEvGQ=; b=yJnoEaUmwzQl3GmFCFEwHXg6/dKhep2MRU6opjCH8f3XDi0ulkMs3PFg4YBVOvW+ORW4wPgae Bw8FEWluc2uBIJBsJ+h84yM6yMvTO5SjzAEE7UjUgq1CLVikSYAZoDA X-Developer-Key: i=ahmed.tiba@arm.com; a=ed25519; pk=xVOtd+Qklh/4tuM3tB+BEZD4jj5a6W59C3KCNX6v7OE= Describe the DeviceTree node that exposes the Arm firmware-first CPER provider and hook the file into MAINTAINERS so the binding has an owner. Signed-off-by: Ahmed Tiba --- .../devicetree/bindings/firmware/arm,ras-cper.yaml | 54 ++++++++++++++++++= ++++ MAINTAINERS | 5 ++ 2 files changed, 59 insertions(+) diff --git a/Documentation/devicetree/bindings/firmware/arm,ras-cper.yaml b= /Documentation/devicetree/bindings/firmware/arm,ras-cper.yaml new file mode 100644 index 000000000000..3d4de096093f --- /dev/null +++ b/Documentation/devicetree/bindings/firmware/arm,ras-cper.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/firmware/arm,ras-cper.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Arm RAS CPER provider + +maintainers: + - Ahmed Tiba + +description: + Arm Reliability, Availability and Serviceability (RAS) firmware can expo= se + a firmware-first CPER error source directly via DeviceTree. Firmware + provides the CPER Generic Error Status block and notifies the OS through + an interrupt. + +properties: + compatible: + const: arm,ras-cper + + memory-region: + minItems: 1 + items: + - description: + CPER Generic Error Status block exposed by firmware. + - description: + Optional firmware-owned ack buffer used on platforms + where firmware needs an explicit "ack" handshake before overwrit= ing + the CPER buffer. Firmware watches bit 0 and expects the OS to se= t it + once the current status block has been consumed. + + interrupts: + maxItems: 1 + description: + Interrupt used to signal that a new status record is ready. + +required: + - compatible + - memory-region + - interrupts + +additionalProperties: false + +examples: + - | + #include + + error-handler { + compatible =3D "arm,ras-cper"; + memory-region =3D <&ras_cper_buffer>, <&ras_cper_ack>; + interrupts =3D ; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index 461a3eed6129..8a9714603a7d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22261,6 +22261,11 @@ M: Alexandre Bounine S: Maintained F: drivers/rapidio/ =20 +RAS ERROR STATUS +M: Ahmed Tiba +S: Maintained +F: Documentation/devicetree/bindings/firmware/arm,ras-cper.yaml + RAS INFRASTRUCTURE M: Tony Luck M: Borislav Petkov --=20 2.43.0 From nobody Mon Jun 8 12:17:15 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id DCAFE3CF661; Fri, 29 May 2026 09:52:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780048324; cv=none; b=ioMsJ5V4rc81JTFggo5vzaow6qTtkJhHqSgUPE7nSrOnkrgdATnijuZabJGvLCc/sb9EhT5s0fqh1KQQ3/HeMQxgWJlqD7XUVrxrbFrPvsM26lCuHaIdnpFsyJQdieOGpgqDhAf+GzZwsx2zk55yWOc2Vm5h9xdTMrB1tm58Lrs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780048324; c=relaxed/simple; bh=3Vm+nt9+NrRY2BNeT6yqN54lwtE/pTm7P8uhemvSbxw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=daAm3p4QqSKBW/lVdpbKtE12kfjojy6yjcAPa6FtSOFMC2sBnLkVHRsAxg36VKLz8uW6hiRyKnkQEMjw9gZi7fDuw6+jCLi3zjtRKwrHyj77olM3i4TtorNXp1vM4NcbcflefA8Rg7+ERXXDh26TYFxk5JGAMqvpxfjTi8ezN6s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=C7xg8XMz; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="C7xg8XMz" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 75F0532E5; Fri, 29 May 2026 02:51:56 -0700 (PDT) Received: from e134710.arm.com (e134710.arm.com [10.33.10.82]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 147B23F905; Fri, 29 May 2026 02:51:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1780048321; bh=3Vm+nt9+NrRY2BNeT6yqN54lwtE/pTm7P8uhemvSbxw=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=C7xg8XMzq5zTtGfFr5s/2WYQYt+gpieTU7iOBQhmMtsGT9GXHigWk02g3JisLNZDg EfrT3vAxG+K6+BkarPyGHy2JBMBbg5gRsupN9ig5hxDK/Ef7hbUap+i/mB3/6iaxka U9nGu5xRIHxBLhx7jgQU3+80wTtca/MbrCaWAopA= From: Ahmed Tiba Date: Fri, 29 May 2026 10:50:50 +0100 Subject: [PATCH v5 10/10] RAS: add firmware-first CPER provider Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-10-2e0500d42642@arm.com> References: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-0-2e0500d42642@arm.com> In-Reply-To: <20260529-topics-ahmtib01-ras_ffh_arm_internal_review-v5-0-2e0500d42642@arm.com> To: will@kernel.org, jic23@kernel.org, xueshuai@linux.alibaba.com, saket.dumbre@intel.com, mchehab@kernel.org, dave@stgolabs.net, djbw@kernel.org, bp@alien8.de, tony.luck@intel.com, guohanjun@huawei.com, lenb@kernel.org, skhan@linuxfoundation.org, vishal.l.verma@intel.com, rafael@kernel.org, corbet@lwn.net, ira.weiny@intel.com, dave.jiang@intel.com, krzk+dt@kernel.org, robh@kernel.org, catalin.marinas@arm.com, alison.schofield@intel.com, conor+dt@kernel.org Cc: Ahmed Tiba , linux-arm-kernel@lists.infradead.org, Michael.Zhao2@arm.com, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-cxl@vger.kernel.org, Dmitry.Lamerov@arm.com, devicetree@vger.kernel.org, linux-acpi@vger.kernel.org, linux-edac@vger.kernel.org, acpica-devel@lists.linux.dev X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1780048269; l=12767; i=ahmed.tiba@arm.com; s=20260219; h=from:subject:message-id; bh=3Vm+nt9+NrRY2BNeT6yqN54lwtE/pTm7P8uhemvSbxw=; b=fJuzbuh5gHAUrlcRsuZoAdSAQnVi9D2NMEtR4k8a1eCtv2+bFccnL+0PmDxls6bHh0Ns0V/Ml OLwy/sSySYiAqqdGcScJ/ea+VtjIxNA6sBpX7CZX6tgab7fccOmfE7q X-Developer-Key: i=ahmed.tiba@arm.com; a=ed25519; pk=xVOtd+Qklh/4tuM3tB+BEZD4jj5a6W59C3KCNX6v7OE= Add a firmware-first CPER provider that reuses the shared GHES helpers, wire it into the RAS Kconfig/Makefile and document it in the admin guide. Update MAINTAINERS now that the driver exists. Signed-off-by: Ahmed Tiba --- Documentation/admin-guide/RAS/main.rst | 18 +++ MAINTAINERS | 1 + drivers/acpi/apei/apei-internal.h | 10 +- drivers/acpi/apei/ghes_cper.c | 2 + drivers/ras/Kconfig | 11 ++ drivers/ras/Makefile | 1 + drivers/ras/cper-esource.c | 257 +++++++++++++++++++++++++++++= ++++ include/acpi/ghes_cper.h | 10 ++ 8 files changed, 301 insertions(+), 9 deletions(-) diff --git a/Documentation/admin-guide/RAS/main.rst b/Documentation/admin-g= uide/RAS/main.rst index 5a45db32c49b..84219d25a072 100644 --- a/Documentation/admin-guide/RAS/main.rst +++ b/Documentation/admin-guide/RAS/main.rst @@ -205,6 +205,24 @@ Architecture (MCA)\ [#f3]_. .. [#f3] For more details about the Machine Check Architecture (MCA), please read Documentation/arch/x86/x86_64/machinecheck.rst at the Kernel= tree. =20 +Firmware-first CPER providers +----------------------------- + +Some systems expose Common Platform Error Record (CPER) data +through platform firmware instead of ACPI HEST tables. +Enable ``CONFIG_RAS_CPER_ESOURCE`` to build the ``drivers/ras/cper-esource= .c`` +driver. The current in-tree firmware description uses the +``Documentation/devicetree/bindings/firmware/arm,ras-cper.yaml`` binding. +The driver reuses the GHES CPER helper object in +``drivers/acpi/apei/ghes_cper.c`` so the logging, notifier chains, and +memory failure handling match the ACPI GHES behaviour even when +ACPI is disabled. + +Once a platform describes a firmware-first provider, both ACPI GHES and the +firmware-described driver reuse the same code paths. This keeps the +behaviour consistent regardless of whether the error source is described +by ACPI tables or another firmware description. + EDAC - Error Detection And Correction ************************************* =20 diff --git a/MAINTAINERS b/MAINTAINERS index 8a9714603a7d..c14638cd97f6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22265,6 +22265,7 @@ RAS ERROR STATUS M: Ahmed Tiba S: Maintained F: Documentation/devicetree/bindings/firmware/arm,ras-cper.yaml +F: drivers/ras/cper-esource.c =20 RAS INFRASTRUCTURE M: Tony Luck diff --git a/drivers/acpi/apei/apei-internal.h b/drivers/acpi/apei/apei-int= ernal.h index 77c10a7a7a9f..c16ac541f15b 100644 --- a/drivers/acpi/apei/apei-internal.h +++ b/drivers/acpi/apei/apei-internal.h @@ -8,6 +8,7 @@ #define APEI_INTERNAL_H =20 #include +#include =20 struct apei_exec_context; =20 @@ -120,15 +121,6 @@ int apei_exec_collect_resources(struct apei_exec_conte= xt *ctx, struct dentry; struct dentry *apei_get_debugfs_dir(void); =20 -static inline u32 cper_estatus_len(struct acpi_hest_generic_status *estatu= s) -{ - if (estatus->raw_data_length) - return estatus->raw_data_offset + \ - estatus->raw_data_length; - else - return sizeof(*estatus) + estatus->data_length; -} - int apei_osc_setup(void); =20 int einj_get_available_error_type(u32 *type, int einj_action); diff --git a/drivers/acpi/apei/ghes_cper.c b/drivers/acpi/apei/ghes_cper.c index 0ff9d06eb78f..a7691aa5011c 100644 --- a/drivers/acpi/apei/ghes_cper.c +++ b/drivers/acpi/apei/ghes_cper.c @@ -46,7 +46,9 @@ #include #include =20 +#ifdef CONFIG_ACPI_APEI #include "apei-internal.h" +#endif =20 ATOMIC_NOTIFIER_HEAD(ghes_report_chain); =20 diff --git a/drivers/ras/Kconfig b/drivers/ras/Kconfig index fc4f4bb94a4c..3c1c63b2fefc 100644 --- a/drivers/ras/Kconfig +++ b/drivers/ras/Kconfig @@ -34,6 +34,17 @@ if RAS source "arch/x86/ras/Kconfig" source "drivers/ras/amd/atl/Kconfig" =20 +config RAS_CPER_ESOURCE + bool "Firmware-first CPER error source block provider" + select GHES_CPER_HELPERS + help + Enable support for firmware-first Common Platform Error Record + (CPER) error source block providers. The current in-tree user is + described by the arm,ras-cper DeviceTree binding. The driver + reuses the existing GHES CPER helpers so the error processing + matches the ACPI code paths, but it can be built even when ACPI is + disabled. + config RAS_FMPM tristate "FRU Memory Poison Manager" default m diff --git a/drivers/ras/Makefile b/drivers/ras/Makefile index 11f95d59d397..0de069557f31 100644 --- a/drivers/ras/Makefile +++ b/drivers/ras/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_RAS) +=3D ras.o obj-$(CONFIG_DEBUG_FS) +=3D debugfs.o obj-$(CONFIG_RAS_CEC) +=3D cec.o +obj-$(CONFIG_RAS_CPER_ESOURCE) +=3D cper-esource.o =20 obj-$(CONFIG_RAS_FMPM) +=3D amd/fmpm.o obj-y +=3D amd/atl/ diff --git a/drivers/ras/cper-esource.c b/drivers/ras/cper-esource.c new file mode 100644 index 000000000000..83f7a910e50a --- /dev/null +++ b/drivers/ras/cper-esource.c @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Firmware-first CPER error source provider. + * + * This driver shares the GHES CPER helpers so we keep the reporting and + * notifier behaviour identical to ACPI GHES. + * + * Copyright (C) 2026 ARM Ltd. + * Author: Ahmed Tiba + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static DEFINE_IDA(cper_esource_source_ids); + +struct cper_esource_ack { + void __iomem *addr; + u64 preserve; + u64 set; + u8 width; + bool present; +}; + +struct cper_esource { + struct device *dev; + void __iomem *status; + size_t status_len; + + struct cper_esource_ack ack; + + struct acpi_hest_generic *generic; + struct acpi_hest_generic_status *estatus; + + bool sync; + int irq; + + /* Serializes access while firmware and the OS share the status buffer. */ + spinlock_t lock; +}; + +static void cper_esource_release_source_id(void *data) +{ + struct acpi_hest_generic *generic =3D data; + + ida_free(&cper_esource_source_ids, generic->header.source_id); +} + +static int cper_esource_init_pool(void) +{ + if (ghes_estatus_pool) + return 0; + + return ghes_estatus_pool_init(1); +} + +static int cper_esource_copy_status(struct cper_esource *ctx) +{ + memcpy_fromio(ctx->estatus, ctx->status, ctx->status_len); + return 0; +} + +static void cper_esource_ack(struct cper_esource *ctx) +{ + u64 val; + + if (!ctx->ack.present) + return; + + if (ctx->ack.width =3D=3D 64) { + val =3D readq(ctx->ack.addr); + val &=3D ctx->ack.preserve; + val |=3D ctx->ack.set; + writeq(val, ctx->ack.addr); + } else { + val =3D readl(ctx->ack.addr); + val &=3D (u32)ctx->ack.preserve; + val |=3D (u32)ctx->ack.set; + writel(val, ctx->ack.addr); + } +} + +static void cper_esource_fatal(struct cper_esource *ctx) +{ + __ghes_print_estatus(KERN_EMERG, ctx->generic, ctx->estatus); + add_taint(TAINT_MACHINE_CHECK, LOCKDEP_STILL_OK); + panic("GHES: fatal firmware-first CPER record from %s\n", + dev_name(ctx->dev)); +} + +static void cper_esource_process(struct cper_esource *ctx) +{ + int sev; + + guard(spinlock_irqsave)(&ctx->lock); + + if (cper_esource_copy_status(ctx)) + return; + + sev =3D ghes_severity(ctx->estatus->error_severity); + if (sev >=3D GHES_SEV_PANIC) + cper_esource_fatal(ctx); + + if (!ghes_estatus_cached(ctx->estatus) && + ghes_print_estatus(NULL, ctx->generic, ctx->estatus)) + ghes_estatus_cache_add(ctx->generic, ctx->estatus); + + ghes_cper_handle_status(ctx->dev, ctx->generic, ctx->estatus, ctx->sync); + cper_esource_ack(ctx); +} + +static irqreturn_t cper_esource_irq(int irq, void *data) +{ + struct cper_esource *ctx =3D data; + + cper_esource_process(ctx); + + return IRQ_HANDLED; +} + +static int cper_esource_init_ack(struct platform_device *pdev, + struct cper_esource *ctx) +{ + struct device *dev =3D &pdev->dev; + struct resource *res; + size_t size; + + res =3D platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) + return 0; + + ctx->ack.addr =3D devm_platform_get_and_ioremap_resource(pdev, 1, &res); + if (IS_ERR(ctx->ack.addr)) + return PTR_ERR(ctx->ack.addr); + + size =3D resource_size(res); + switch (size) { + case 4: + ctx->ack.width =3D 32; + ctx->ack.preserve =3D ~0U; + break; + case 8: + ctx->ack.width =3D 64; + ctx->ack.preserve =3D ~0ULL; + break; + default: + return dev_err_probe(dev, -EINVAL, + "unsupported ack resource size %zu\n", size); + } + + ctx->ack.set =3D BIT_ULL(0); + ctx->ack.present =3D true; + return 0; +} + +static int cper_esource_probe(struct platform_device *pdev) +{ + struct device *dev =3D &pdev->dev; + struct cper_esource *ctx; + struct resource *res; + int source_id; + int rc; + + ctx =3D devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + spin_lock_init(&ctx->lock); + ctx->dev =3D dev; + ctx->sync =3D device_property_read_bool(dev, "arm,sea-notify"); + + ctx->status =3D devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(ctx->status)) + return dev_err_probe(dev, PTR_ERR(ctx->status), + "failed to map status region\n"); + + ctx->status_len =3D resource_size(res); + if (!ctx->status_len) + return dev_err_probe(dev, -EINVAL, "status region has zero length\n"); + + rc =3D cper_esource_init_ack(pdev, ctx); + if (rc) + return rc; + + rc =3D cper_esource_init_pool(); + if (rc) + return rc; + + ctx->estatus =3D devm_kzalloc(dev, ctx->status_len, GFP_KERNEL); + if (!ctx->estatus) + return -ENOMEM; + + ctx->generic =3D devm_kzalloc(dev, sizeof(*ctx->generic), GFP_KERNEL); + if (!ctx->generic) + return -ENOMEM; + + source_id =3D ida_alloc_min(&cper_esource_source_ids, 1, GFP_KERNEL); + if (source_id < 0) + return source_id; + + ctx->generic->header.type =3D ACPI_HEST_TYPE_GENERIC_ERROR; + ctx->generic->header.source_id =3D source_id; + + rc =3D devm_add_action_or_reset(dev, cper_esource_release_source_id, + ctx->generic); + if (rc) + return rc; + + ctx->generic->notify.type =3D ctx->sync ? + ACPI_HEST_NOTIFY_SEA : ACPI_HEST_NOTIFY_EXTERNAL; + ctx->generic->error_block_length =3D ctx->status_len; + + ctx->irq =3D platform_get_irq(pdev, 0); + if (ctx->irq < 0) + return ctx->irq; + + rc =3D devm_request_threaded_irq(dev, ctx->irq, NULL, cper_esource_irq, + IRQF_ONESHOT, + dev_name(dev), ctx); + if (rc) + return dev_err_probe(dev, rc, "failed to request interrupt\n"); + + return 0; +} + +static const struct of_device_id cper_esource_of_match[] =3D { + { .compatible =3D "arm,ras-cper" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, cper_esource_of_match); + +static struct platform_driver cper_esource_driver =3D { + .driver =3D { + .name =3D "cper-esource", + .of_match_table =3D cper_esource_of_match, + }, + .probe =3D cper_esource_probe, +}; + +module_platform_driver(cper_esource_driver); + +MODULE_AUTHOR("Ahmed Tiba "); +MODULE_DESCRIPTION("Firmware-first CPER provider"); +MODULE_LICENSE("GPL"); diff --git a/include/acpi/ghes_cper.h b/include/acpi/ghes_cper.h index 511b95b50911..a78d4a773129 100644 --- a/include/acpi/ghes_cper.h +++ b/include/acpi/ghes_cper.h @@ -80,6 +80,14 @@ static inline bool is_hest_sync_notify(struct ghes *ghes) return notify_type =3D=3D ACPI_HEST_NOTIFY_SEA; } =20 +static inline u32 cper_estatus_len(struct acpi_hest_generic_status *estatu= s) +{ + if (estatus->raw_data_length) + return estatus->raw_data_offset + estatus->raw_data_length; + else + return sizeof(*estatus) + estatus->data_length; +} + struct ghes_vendor_record_entry { struct work_struct work; int error_severity; @@ -108,6 +116,8 @@ int __ghes_read_estatus(struct acpi_hest_generic_status= *estatus, int ghes_estatus_cached(struct acpi_hest_generic_status *estatus); void ghes_estatus_cache_add(struct acpi_hest_generic *generic, struct acpi_hest_generic_status *estatus); +int ghes_register_vendor_record_notifier(struct notifier_block *nb); +void ghes_unregister_vendor_record_notifier(struct notifier_block *nb); void ghes_defer_non_standard_event(struct acpi_hest_generic_data *gdata, int sev); int ghes_severity(int severity); --=20 2.43.0