From: Zhang Jian zhangjian210@huawei.com
ascend inclusion category: feature bugzilla: NA CVE: NA
-------------------------------------------------
RAS Function only support by acpi, in some scenarios no use acpi. the features add support by dts, irq passed and parsed by dts mode, notify ghes proc.
Signed-off-by: Zhang Jian zhangjian210@huawei.com Signed-off-by: Zhang Zekun zhangzekun11@huawei.com Reviewed-by: Weilong Chen chenweilong@huawei.com --- drivers/acpi/apei/ghes.c | 47 +++++++++++++++++++++++++++++---- drivers/acpi/dt_apei.c | 56 ++++++++++++++++++++++++++++++++++++++++ include/acpi/dt_apei.h | 31 ++++++++++++++++++++++ 3 files changed, 129 insertions(+), 5 deletions(-) create mode 100644 include/acpi/dt_apei.h
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 9c38c2cdd2fd..fc5767a91788 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -45,6 +45,7 @@ #include <acpi/actbl1.h> #include <acpi/ghes.h> #include <acpi/apei.h> +#include <acpi/dt_apei.h> #include <asm/fixmap.h> #include <asm/tlbflush.h> #include <ras/ras_event.h> @@ -146,6 +147,17 @@ static atomic_t ghes_estatus_cache_alloced;
static int ghes_panic_timeout __read_mostly = 30;
+bool dt_apei_disable = true; +bool enable_acpi_dt_apei = false; + +static int __init enable_acpi_dt_apei_setup(char *str) +{ + if (IS_ENABLED(CONFIG_ACPI_DT_APEI)) + enable_acpi_dt_apei = true; + return 1; +} +__setup("enable_acpi_dt_apei", enable_acpi_dt_apei_setup); + static void __iomem *ghes_map(u64 pfn, enum fixed_addresses fixmap_idx) { phys_addr_t paddr; @@ -949,6 +961,28 @@ static struct notifier_block ghes_notifier_hed = { .notifier_call = ghes_notify_hed, };
+static inline void register_acpi_notifier_hed(void) +{ + if (!list_empty(&ghes_hed)) + return; + if (enable_acpi_dt_apei) { + register_dt_apei_notifier(&ghes_notifier_hed); + return; + } + register_acpi_hed_notifier(&ghes_notifier_hed); +} + +static inline void unregister_acpi_notifier_hed(void) +{ + if (!list_empty(&ghes_hed)) + return; + if (enable_acpi_dt_apei) { + unregister_dt_apei_notifier(&ghes_notifier_hed); + return; + } + unregister_acpi_hed_notifier(&ghes_notifier_hed); +} + /* * Handlers for CPER records may not be NMI safe. For example, * memory_failure_queue() takes spinlocks and calls schedule_work_on(). @@ -1361,8 +1395,7 @@ static int ghes_probe(struct platform_device *ghes_dev) case ACPI_HEST_NOTIFY_GSIV: case ACPI_HEST_NOTIFY_GPIO: mutex_lock(&ghes_list_mutex); - if (list_empty(&ghes_hed)) - register_acpi_hed_notifier(&ghes_notifier_hed); + register_acpi_notifier_hed(); list_add_rcu(&ghes->list, &ghes_hed); mutex_unlock(&ghes_list_mutex); break; @@ -1424,8 +1457,7 @@ static int ghes_remove(struct platform_device *ghes_dev) case ACPI_HEST_NOTIFY_GPIO: mutex_lock(&ghes_list_mutex); list_del_rcu(&ghes->list); - if (list_empty(&ghes_hed)) - unregister_acpi_hed_notifier(&ghes_notifier_hed); + unregister_acpi_notifier_hed(); mutex_unlock(&ghes_list_mutex); synchronize_rcu(); break; @@ -1469,7 +1501,12 @@ static int __init ghes_init(void) { int rc;
- if (acpi_disabled) + /* + * @dt_apei_disable and @acpi_disabled will not be true. one of them + * will be true. Neither @dt_apei_disable nor @acpi_disable is false, the + * last code must be executed. + */ + if (dt_apei_disable && acpi_disabled) return -ENODEV;
switch (hest_disable) { diff --git a/drivers/acpi/dt_apei.c b/drivers/acpi/dt_apei.c index bec3c1cc4d00..b98ee9ed5a96 100644 --- a/drivers/acpi/dt_apei.c +++ b/drivers/acpi/dt_apei.c @@ -15,14 +15,61 @@ #include <linux/platform_device.h> #include <linux/device.h> #include <linux/of.h> +#include <linux/of_irq.h> #include <linux/of_address.h> #include <acpi/apei.h> +#include <acpi/dt_apei.h>
struct apei_table_params { acpi_physical_address phys; acpi_size size; };
+static BLOCKING_NOTIFIER_HEAD(apei_hed_notify_list); + +int register_dt_apei_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&apei_hed_notify_list, nb); +} +EXPORT_SYMBOL_GPL(register_dt_apei_notifier); + +void unregister_dt_apei_notifier(struct notifier_block *nb) +{ + blocking_notifier_chain_unregister(&apei_hed_notify_list, nb); +} +EXPORT_SYMBOL_GPL(unregister_dt_apei_notifier); + +/* notify ghes proc ras error */ +static irqreturn_t handle_threaded_apei_irq(int irq, void *data) +{ + blocking_notifier_call_chain(&apei_hed_notify_list, 0, NULL); + return IRQ_HANDLED; +} + +static int dt_apei_register_irq(struct device_node *np) +{ + int irq; + int rc; + + irq = irq_of_parse_and_map(np, 0); + if (irq <= 0) { + pr_err("%s: parse and map irq (%d) fail\n", + __func__, irq); + return -ENOENT; + } + + rc = request_threaded_irq(irq, NULL, handle_threaded_apei_irq, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "APEI:HED", NULL); + if (rc != 0) { + pr_err("%s: request apei irq fail (%d)\n", + __func__, rc); + return rc; + } + + return 0; +} + static void dt_apei_print_table_header(struct acpi_table_header *header) { pr_info("%-4.4s %06X (v%.2d %-6.6s %-8.8s %08X %-4.4s %08X)", @@ -116,16 +163,25 @@ static int __init do_dt_apei_init(void) /* acpi permanent mmap has been set in acpi map table */ acpi_permanent_mmap = true;
+ dt_apei_disable = false; rc = dt_apei_parse_hest(&table_params); if (rc != 0) return rc;
+ rc = dt_apei_register_irq(np); + if (rc != 0) + return rc; + pr_info("%s: dt apei probe success\n", __func__); return 0; }
static int __init dt_apei_init(void) { + if (!enable_acpi_dt_apei) { + pr_info("%s: dt apei probe not enabled\n", __func__); + return 0; + } return do_dt_apei_init(); }
diff --git a/include/acpi/dt_apei.h b/include/acpi/dt_apei.h new file mode 100644 index 000000000000..829ea576f255 --- /dev/null +++ b/include/acpi/dt_apei.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * dt_apei.h - APEI notifier Interface + */ + +#ifndef ACPI_DT_APEI_H +#define ACPI_DT_APEI_H + +#include <linux/notifier.h> + +#define DT_APEI_ENABLED (1) +#define DT_APEI_DISABLED (0) + +extern bool enable_acpi_dt_apei; +extern bool dt_apei_disable; + +#ifdef CONFIG_ACPI_DT_APEI +int register_dt_apei_notifier(struct notifier_block *nb); +void unregister_dt_apei_notifier(struct notifier_block *nb); +#else +static inline int register_dt_apei_notifier(struct notifier_block *nb) +{ + return -EINVAL; +} + +static inline void unregister_dt_apei_notifier(struct notifier_block *nb) +{ + return; +} +#endif +#endif