[PATCH v7 09/25] ACPI / APEI: Generalise the estatus queue's notify code
From: James Morse <james.morse@arm.com>
Date: 2018-12-03 18:07:09
Also in:
kvmarm, linux-acpi, linux-mm
Subsystem:
acpi, acpi apei, the rest · Maintainers:
"Rafael J. Wysocki", Linus Torvalds
Refactor the estatus queue's pool notification routine from NOTIFY_NMI's handlers. This will allow another notification method to use the estatus queue without duplicating this code. This patch adds rcu_read_lock()/rcu_read_unlock() around the list list_for_each_entry_rcu() walker. These aren't strictly necessary as the whole nmi_enter/nmi_exit() window is a spooky RCU read-side critical section. _in_nmi_notify_one() is separate from the rcu-list walker for a later caller that doesn't need to walk a list. Signed-off-by: James Morse <james.morse@arm.com> Reviewed-by: Punit Agrawal <redacted> Tested-by: Tyler Baicar <redacted> --- Changes since v6: * Removed pool grow/remove code as this is no longer necessary. Changes since v3: * Removed duplicate or redundant paragraphs in commit message. * Fixed the style of a zero check. Changes since v1: * Tidied up _in_nmi_notify_one(). --- drivers/acpi/apei/ghes.c | 63 ++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 22 deletions(-)
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index d06456e60318..366dbdd41ef3 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c@@ -907,37 +907,56 @@ static void __process_error(struct ghes *ghes) #endif } -static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) +static int _in_nmi_notify_one(struct ghes *ghes) { u64 buf_paddr; - struct ghes *ghes; - int sev, ret = NMI_DONE; + int sev; - if (!atomic_add_unless(&ghes_in_nmi, 1, 1)) - return ret; + if (ghes_read_estatus(ghes, &buf_paddr)) { + ghes_clear_estatus(ghes, buf_paddr); + return -ENOENT; + } - list_for_each_entry_rcu(ghes, &ghes_nmi, list) { - if (ghes_read_estatus(ghes, &buf_paddr)) { - ghes_clear_estatus(ghes, buf_paddr); - continue; - } else { - ret = NMI_HANDLED; - } + sev = ghes_severity(ghes->estatus->error_severity); + if (sev >= GHES_SEV_PANIC) { + ghes_print_queued_estatus(); + __ghes_panic(ghes); + } - sev = ghes_severity(ghes->estatus->error_severity); - if (sev >= GHES_SEV_PANIC) { - ghes_print_queued_estatus(); - __ghes_panic(ghes); - } + __process_error(ghes); + ghes_clear_estatus(ghes, buf_paddr); - __process_error(ghes); - ghes_clear_estatus(ghes, buf_paddr); + return 0; +} + +static int ghes_estatus_queue_notified(struct list_head *rcu_list) +{ + int ret = -ENOENT; + struct ghes *ghes; + + rcu_read_lock(); + list_for_each_entry_rcu(ghes, rcu_list, list) { + if (!_in_nmi_notify_one(ghes)) + ret = 0; } + rcu_read_unlock(); -#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG - if (ret == NMI_HANDLED) + if (IS_ENABLED(CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG) && !ret) irq_work_queue(&ghes_proc_irq_work); -#endif + + return ret; +} + +static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) +{ + int ret = NMI_DONE; + + if (!atomic_add_unless(&ghes_in_nmi, 1, 1)) + return ret; + + if (!ghes_estatus_queue_notified(&ghes_nmi)) + ret = NMI_HANDLED; + atomic_dec(&ghes_in_nmi); return ret; }
--
2.19.2
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel