APEI is currently implemented so that it depends on x86 hardware. The primary dependency is that GHES uses the x86 NMI for hardware error notification and MCE for memory error handling. These patches remove that dependency.
Other APEI features such as error reporting via external IRQ, error serialization, or error injection, do not require changes to use them on non-x86 architectures.
The following patch set eliminates the APEI Kconfig x86 dependency by making these changes: - treat NMI notification as GHES architecture - HAVE_ACPI_APEI_NMI - group and wrap around #ifdef CONFIG_HAVE_ACPI_APEI_NMI code which is used only for NMI path - identify architectural boxes and abstract it accordingly (tlb flush and MCE) - rework ioremap for both IRQ and NMI context
NMI code is kept in ghes.c file since NMI and IRQ context are tightly coupled.
Note, these patches introduce no functional changes for x86. The NMI notification feature is hard selected for x86. Architectures that want to use this feature should also provide NMI code infrastructure.
V1->V2 - address Borislav's comment - abstract arch-specific calls instead of wrapping into the #ifdef
V2->V3 - address Robert's comment - disable ACPI_APEI_NMI selection so that it is hard selected by arch Kconfig - rename ACPI_APEI_NMI to ARCH_HAS_ACPI_APEI_NMI
V3->V4 - do not abstract NMI calls for archs which do not support it - merge some of patches to make review process easier
Tomasz Nowicki (3): apei, mce: Factor out APEI architecture specific MCE calls. acpi, apei, ghes: Make NMI error notification to be GHES architecture extension. acpi, apei, ghes: Factor out ioremap virtual memory for IRQ and NMI context.
arch/x86/Kconfig | 2 + arch/x86/kernel/acpi/Makefile | 1 + arch/x86/kernel/acpi/apei.c | 62 +++++++++++++++ drivers/acpi/apei/Kconfig | 8 +- drivers/acpi/apei/apei-base.c | 13 ++++ drivers/acpi/apei/ghes.c | 173 ++++++++++++++++++++++++++--------------- drivers/acpi/apei/hest.c | 29 +------ include/acpi/apei.h | 4 + include/linux/nmi.h | 4 + 9 files changed, 204 insertions(+), 92 deletions(-) create mode 100644 arch/x86/kernel/acpi/apei.c
This commit abstracts MCE calls and provides weak corresponding default implementation for those architectures which do not need arch specific actions. Each platform willing to do additional architectural actions should provides desired function definition. It allows us to avoid wrap code into #ifdef in generic code and prevent new platform from introducing dummy stub function too.
Initially, there are two APEI arch-specific calls: - arch_apei_enable_cmcff() - arch_apei_report_mem_error() Both interact with MCE driver for X86 architecture.
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org --- arch/x86/kernel/acpi/Makefile | 1 + arch/x86/kernel/acpi/apei.c | 56 +++++++++++++++++++++++++++++++++++++++++ drivers/acpi/apei/apei-base.c | 13 ++++++++++ drivers/acpi/apei/ghes.c | 6 ++--- drivers/acpi/apei/hest.c | 29 +-------------------- include/acpi/apei.h | 3 +++ 6 files changed, 76 insertions(+), 32 deletions(-) create mode 100644 arch/x86/kernel/acpi/apei.c
diff --git a/arch/x86/kernel/acpi/Makefile b/arch/x86/kernel/acpi/Makefile index 163b225..3242e59 100644 --- a/arch/x86/kernel/acpi/Makefile +++ b/arch/x86/kernel/acpi/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_ACPI) += boot.o obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup_$(BITS).o +obj-$(CONFIG_ACPI_APEI) += apei.o
ifneq ($(CONFIG_ACPI_PROCESSOR),) obj-y += cstate.o diff --git a/arch/x86/kernel/acpi/apei.c b/arch/x86/kernel/acpi/apei.c new file mode 100644 index 0000000..12b13de --- /dev/null +++ b/arch/x86/kernel/acpi/apei.c @@ -0,0 +1,56 @@ +/* + * Arch-specific APEI-related functions. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <acpi/apei.h> + +#include <asm/mce.h> + +int arch_apei_enable_cmcff(struct acpi_hest_header *hest_hdr, void *data) +{ +#ifdef CONFIG_X86_MCE + int i; + struct acpi_hest_ia_corrected *cmc; + struct acpi_hest_ia_error_bank *mc_bank; + + if (hest_hdr->type != ACPI_HEST_TYPE_IA32_CORRECTED_CHECK) + return 0; + + cmc = (struct acpi_hest_ia_corrected *)hest_hdr; + if (!cmc->enabled) + return 0; + + /* + * We expect HEST to provide a list of MC banks that report errors + * in firmware first mode. Otherwise, return non-zero value to + * indicate that we are done parsing HEST. + */ + if (!(cmc->flags & ACPI_HEST_FIRMWARE_FIRST) || + !cmc->num_hardware_banks) + return 1; + + pr_info("HEST: Enabling Firmware First mode for corrected errors.\n"); + + mc_bank = (struct acpi_hest_ia_error_bank *)(cmc + 1); + for (i = 0; i < cmc->num_hardware_banks; i++, mc_bank++) + mce_disable_bank(mc_bank->bank_number); +#endif + return 1; +} + +void arch_apei_report_mem_error(int sev, struct cper_sec_mem_err *mem_err) +{ +#ifdef CONFIG_X86_MCE + apei_mce_report_mem_error(sev, mem_err); +#endif +} diff --git a/drivers/acpi/apei/apei-base.c b/drivers/acpi/apei/apei-base.c index 8678dfe..2cd7bdd 100644 --- a/drivers/acpi/apei/apei-base.c +++ b/drivers/acpi/apei/apei-base.c @@ -745,6 +745,19 @@ struct dentry *apei_get_debugfs_dir(void) } EXPORT_SYMBOL_GPL(apei_get_debugfs_dir);
+int __weak arch_apei_enable_cmcff(struct acpi_hest_header *hest_hdr, + void *data) +{ + return 1; +} +EXPORT_SYMBOL_GPL(arch_apei_enable_cmcff); + +void __weak arch_apei_report_mem_error(int sev, + struct cper_sec_mem_err *mem_err) +{ +} +EXPORT_SYMBOL_GPL(arch_apei_report_mem_error); + int apei_osc_setup(void) { static u8 whea_uuid_str[] = "ed855e0c-6c90-47bf-a62a-26de0fc5ad5c"; diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index dab7cb7..352170a 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -49,7 +49,7 @@ #include <linux/aer.h>
#include <acpi/ghes.h> -#include <asm/mce.h> +#include <acpi/apei.h> #include <asm/tlbflush.h> #include <asm/nmi.h>
@@ -455,9 +455,7 @@ static void ghes_do_proc(struct ghes *ghes, mem_err = (struct cper_sec_mem_err *)(gdata+1); ghes_edac_report_mem_error(ghes, sev, mem_err);
-#ifdef CONFIG_X86_MCE - apei_mce_report_mem_error(sev, mem_err); -#endif + arch_apei_report_mem_error(sev, mem_err); ghes_handle_memory_failure(gdata, sev); } #ifdef CONFIG_ACPI_APEI_PCIEAER diff --git a/drivers/acpi/apei/hest.c b/drivers/acpi/apei/hest.c index f5e37f3..06e9b41 100644 --- a/drivers/acpi/apei/hest.c +++ b/drivers/acpi/apei/hest.c @@ -36,7 +36,6 @@ #include <linux/io.h> #include <linux/platform_device.h> #include <acpi/apei.h> -#include <asm/mce.h>
#include "apei-internal.h"
@@ -128,33 +127,7 @@ EXPORT_SYMBOL_GPL(apei_hest_parse); */ static int __init hest_parse_cmc(struct acpi_hest_header *hest_hdr, void *data) { -#ifdef CONFIG_X86_MCE - int i; - struct acpi_hest_ia_corrected *cmc; - struct acpi_hest_ia_error_bank *mc_bank; - - if (hest_hdr->type != ACPI_HEST_TYPE_IA32_CORRECTED_CHECK) - return 0; - - cmc = (struct acpi_hest_ia_corrected *)hest_hdr; - if (!cmc->enabled) - return 0; - - /* - * We expect HEST to provide a list of MC banks that report errors - * in firmware first mode. Otherwise, return non-zero value to - * indicate that we are done parsing HEST. - */ - if (!(cmc->flags & ACPI_HEST_FIRMWARE_FIRST) || !cmc->num_hardware_banks) - return 1; - - pr_info(HEST_PFX "Enabling Firmware First mode for corrected errors.\n"); - - mc_bank = (struct acpi_hest_ia_error_bank *)(cmc + 1); - for (i = 0; i < cmc->num_hardware_banks; i++, mc_bank++) - mce_disable_bank(mc_bank->bank_number); -#endif - return 1; + return arch_apei_enable_cmcff(hest_hdr, data); }
struct ghes_arr { diff --git a/include/acpi/apei.h b/include/acpi/apei.h index 04f349d..8a23c95 100644 --- a/include/acpi/apei.h +++ b/include/acpi/apei.h @@ -42,5 +42,8 @@ ssize_t erst_read(u64 record_id, struct cper_record_header *record, size_t buflen); int erst_clear(u64 record_id);
+int arch_apei_enable_cmcff(struct acpi_hest_header *hest_hdr, void *data); +void arch_apei_report_mem_error(int sev, struct cper_sec_mem_err *mem_err); + #endif #endif
On Tue, Jun 24, 2014 at 01:41:33PM +0200, Tomasz Nowicki wrote:
This commit abstracts MCE calls and provides weak corresponding default implementation for those architectures which do not need arch specific actions. Each platform willing to do additional architectural actions should provides desired function definition. It allows us to avoid wrap code into #ifdef in generic code and prevent new platform from introducing dummy stub function too.
Initially, there are two APEI arch-specific calls:
- arch_apei_enable_cmcff()
- arch_apei_report_mem_error()
Both interact with MCE driver for X86 architecture.
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org
Acked-by: Borislav Petkov bp@suse.de
Currently APEI depends on x86 architecture. It is because of NMI hardware error notification of GHES which is currently supported by x86 only. However, many other APEI features can be still used perfectly by other architectures.
This commit adds two symbols: 1. HAVE_ACPI_APEI for those archs which support APEI. 2. HAVE_ACPI_APEI_NMI which is used for NMI code isolation in ghes.c file. NMI related data and functions are grouped so they can be wrapped inside one #ifdef section. Appropriate function stubs are provided for !NMI case.
Note there is no functional changes for x86 due to hard selected HAVE_ACPI_APEI and HAVE_ACPI_APEI_NMI symbols.
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org --- arch/x86/Kconfig | 2 + drivers/acpi/apei/Kconfig | 8 ++- drivers/acpi/apei/ghes.c | 149 +++++++++++++++++++++++++++++---------------- include/linux/nmi.h | 4 ++ 4 files changed, 110 insertions(+), 53 deletions(-)
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 3fc9b12..e7cca5d 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -132,6 +132,8 @@ config X86 select HAVE_CC_STACKPROTECTOR select GENERIC_CPU_AUTOPROBE select HAVE_ARCH_AUDITSYSCALL + select HAVE_ACPI_APEI + select HAVE_ACPI_APEI_NMI
config INSTRUCTION_DECODER def_bool y diff --git a/drivers/acpi/apei/Kconfig b/drivers/acpi/apei/Kconfig index c4dac71..b0140c8 100644 --- a/drivers/acpi/apei/Kconfig +++ b/drivers/acpi/apei/Kconfig @@ -1,9 +1,15 @@ +config HAVE_ACPI_APEI + bool + +config HAVE_ACPI_APEI_NMI + bool + config ACPI_APEI bool "ACPI Platform Error Interface (APEI)" select MISC_FILESYSTEMS select PSTORE select UEFI_CPER - depends on X86 + depends on HAVE_ACPI_APEI help APEI allows to report errors (for example from the chipset) to the operating system. This improves NMI handling diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 352170a..d1fadf2 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -47,11 +47,11 @@ #include <linux/genalloc.h> #include <linux/pci.h> #include <linux/aer.h> +#include <linux/nmi.h>
#include <acpi/ghes.h> #include <acpi/apei.h> #include <asm/tlbflush.h> -#include <asm/nmi.h>
#include "apei-internal.h"
@@ -86,8 +86,6 @@ bool ghes_disable; module_param_named(disable, ghes_disable, bool, 0);
-static int ghes_panic_timeout __read_mostly = 30; - /* * All error sources notified with SCI shares one notifier function, * so they need to be linked and checked one by one. This is applied @@ -97,16 +95,9 @@ static int ghes_panic_timeout __read_mostly = 30; * list changing, not for traversing. */ static LIST_HEAD(ghes_sci); -static LIST_HEAD(ghes_nmi); static DEFINE_MUTEX(ghes_list_mutex);
/* - * NMI may be triggered on any CPU, so ghes_nmi_lock is used for - * mutual exclusion. - */ -static DEFINE_RAW_SPINLOCK(ghes_nmi_lock); - -/* * Because the memory area used to transfer hardware error information * from BIOS to Linux can be determined only in NMI, IRQ or timer * handler, but general ioremap can not be used in atomic context, so @@ -130,18 +121,8 @@ static struct vm_struct *ghes_ioremap_area; static DEFINE_RAW_SPINLOCK(ghes_ioremap_lock_nmi); static DEFINE_SPINLOCK(ghes_ioremap_lock_irq);
-/* - * printk is not safe in NMI context. So in NMI handler, we allocate - * required memory from lock-less memory allocator - * (ghes_estatus_pool), save estatus into it, put them into lock-less - * list (ghes_estatus_llist), then delay printk into IRQ context via - * irq_work (ghes_proc_irq_work). ghes_estatus_size_request record - * required pool size by all NMI error source. - */ static struct gen_pool *ghes_estatus_pool; static unsigned long ghes_estatus_pool_size_request; -static struct llist_head ghes_estatus_llist; -static struct irq_work ghes_proc_irq_work;
struct ghes_estatus_cache *ghes_estatus_caches[GHES_ESTATUS_CACHES_SIZE]; static atomic_t ghes_estatus_cache_alloced; @@ -249,11 +230,6 @@ static int ghes_estatus_pool_expand(unsigned long len) return 0; }
-static void ghes_estatus_pool_shrink(unsigned long len) -{ - ghes_estatus_pool_size_request -= PAGE_ALIGN(len); -} - static struct ghes *ghes_new(struct acpi_hest_generic *generic) { struct ghes *ghes; @@ -732,6 +708,32 @@ static int ghes_notify_sci(struct notifier_block *this, return ret; }
+static struct notifier_block ghes_notifier_sci = { + .notifier_call = ghes_notify_sci, +}; + +#ifdef CONFIG_HAVE_ACPI_APEI_NMI +/* + * printk is not safe in NMI context. So in NMI handler, we allocate + * required memory from lock-less memory allocator + * (ghes_estatus_pool), save estatus into it, put them into lock-less + * list (ghes_estatus_llist), then delay printk into IRQ context via + * irq_work (ghes_proc_irq_work). ghes_estatus_size_request record + * required pool size by all NMI error source. + */ +static struct llist_head ghes_estatus_llist; +static struct irq_work ghes_proc_irq_work; + +/* + * NMI may be triggered on any CPU, so ghes_nmi_lock is used for + * mutual exclusion. + */ +static DEFINE_RAW_SPINLOCK(ghes_nmi_lock); + +static LIST_HEAD(ghes_nmi); + +static int ghes_panic_timeout __read_mostly = 30; + static struct llist_node *llist_nodes_reverse(struct llist_node *llnode) { struct llist_node *next, *tail = NULL; @@ -875,10 +877,6 @@ out: return ret; }
-static struct notifier_block ghes_notifier_sci = { - .notifier_call = ghes_notify_sci, -}; - static unsigned long ghes_esource_prealloc_size( const struct acpi_hest_generic *generic) { @@ -894,11 +892,71 @@ static unsigned long ghes_esource_prealloc_size( return prealloc_size; }
+static void ghes_estatus_pool_shrink(unsigned long len) +{ + ghes_estatus_pool_size_request -= PAGE_ALIGN(len); +} + +static void ghes_nmi_add(struct ghes *ghes) +{ + unsigned long len; + + len = ghes_esource_prealloc_size(ghes->generic); + ghes_estatus_pool_expand(len); + mutex_lock(&ghes_list_mutex); + if (list_empty(&ghes_nmi)) + register_nmi_handler(NMI_LOCAL, ghes_notify_nmi, 0, "ghes"); + list_add_rcu(&ghes->list, &ghes_nmi); + mutex_unlock(&ghes_list_mutex); +} + +static void ghes_nmi_remove(struct ghes *ghes) +{ + unsigned long len; + + mutex_lock(&ghes_list_mutex); + list_del_rcu(&ghes->list); + if (list_empty(&ghes_nmi)) + unregister_nmi_handler(NMI_LOCAL, "ghes"); + mutex_unlock(&ghes_list_mutex); + /* + * To synchronize with NMI handler, ghes can only be + * freed after NMI handler finishes. + */ + synchronize_rcu(); + len = ghes_esource_prealloc_size(ghes->generic); + ghes_estatus_pool_shrink(len); +} + +static void ghes_nmi_init_cxt(void) +{ + init_irq_work(&ghes_proc_irq_work, ghes_proc_in_irq); +} +#else +static inline void ghes_nmi_add(struct ghes *ghes) +{ + pr_err(GHES_PFX "ID: %d, trying to add NMI notification which is not supported!\n", + ghes->generic->header.source_id); + BUG(); +} + +static inline void ghes_nmi_remove(struct ghes *ghes) +{ + pr_err(GHES_PFX "ID: %d, trying to remove NMI notification which is not supported!\n", + ghes->generic->header.source_id); + BUG(); +} + +static inline void ghes_nmi_init_cxt(void) +{ +} +#endif + static int ghes_probe(struct platform_device *ghes_dev) { struct acpi_hest_generic *generic; struct ghes *ghes = NULL; - unsigned long len; + int rc = -EINVAL;
generic = *(struct acpi_hest_generic **)ghes_dev->dev.platform_data; @@ -909,7 +967,13 @@ static int ghes_probe(struct platform_device *ghes_dev) case ACPI_HEST_NOTIFY_POLLED: case ACPI_HEST_NOTIFY_EXTERNAL: case ACPI_HEST_NOTIFY_SCI: + break; case ACPI_HEST_NOTIFY_NMI: + if (!IS_ENABLED(CONFIG_HAVE_ACPI_APEI_NMI)) { + pr_warn(GHES_PFX "Generic hardware error source: %d notified via NMI interrupt is not supported!\n", + generic->header.source_id); + goto err; + } break; case ACPI_HEST_NOTIFY_LOCAL: pr_warning(GHES_PFX "Generic hardware error source: %d notified via local interrupt is not supported!\n", @@ -970,14 +1034,7 @@ static int ghes_probe(struct platform_device *ghes_dev) mutex_unlock(&ghes_list_mutex); break; case ACPI_HEST_NOTIFY_NMI: - len = ghes_esource_prealloc_size(generic); - ghes_estatus_pool_expand(len); - mutex_lock(&ghes_list_mutex); - if (list_empty(&ghes_nmi)) - register_nmi_handler(NMI_LOCAL, ghes_notify_nmi, 0, - "ghes"); - list_add_rcu(&ghes->list, &ghes_nmi); - mutex_unlock(&ghes_list_mutex); + ghes_nmi_add(ghes); break; default: BUG(); @@ -999,7 +1056,6 @@ static int ghes_remove(struct platform_device *ghes_dev) { struct ghes *ghes; struct acpi_hest_generic *generic; - unsigned long len;
ghes = platform_get_drvdata(ghes_dev); generic = ghes->generic; @@ -1020,18 +1076,7 @@ static int ghes_remove(struct platform_device *ghes_dev) mutex_unlock(&ghes_list_mutex); break; case ACPI_HEST_NOTIFY_NMI: - mutex_lock(&ghes_list_mutex); - list_del_rcu(&ghes->list); - if (list_empty(&ghes_nmi)) - unregister_nmi_handler(NMI_LOCAL, "ghes"); - mutex_unlock(&ghes_list_mutex); - /* - * To synchronize with NMI handler, ghes can only be - * freed after NMI handler finishes. - */ - synchronize_rcu(); - len = ghes_esource_prealloc_size(generic); - ghes_estatus_pool_shrink(len); + ghes_nmi_remove(ghes); break; default: BUG(); @@ -1075,7 +1120,7 @@ static int __init ghes_init(void) return -EINVAL; }
- init_irq_work(&ghes_proc_irq_work, ghes_proc_in_irq); + ghes_nmi_init_cxt();
rc = ghes_ioremap_init(); if (rc) diff --git a/include/linux/nmi.h b/include/linux/nmi.h index 447775e..1d2a6ab 100644 --- a/include/linux/nmi.h +++ b/include/linux/nmi.h @@ -63,4 +63,8 @@ extern int proc_dowatchdog(struct ctl_table *, int , void __user *, size_t *, loff_t *); #endif
+#ifdef CONFIG_HAVE_ACPI_APEI_NMI +#include <asm/nmi.h> +#endif + #endif
On Tue, Jun 24, 2014 at 01:41:34PM +0200, Tomasz Nowicki wrote:
Currently APEI depends on x86 architecture. It is because of NMI hardware error notification of GHES which is currently supported by x86 only. However, many other APEI features can be still used perfectly by other architectures.
This commit adds two symbols:
- HAVE_ACPI_APEI for those archs which support APEI.
- HAVE_ACPI_APEI_NMI which is used for NMI code isolation in ghes.c file. NMI related data and functions are grouped so they can be wrapped inside one #ifdef section. Appropriate function stubs are provided for !NMI case.
Note there is no functional changes for x86 due to hard selected HAVE_ACPI_APEI and HAVE_ACPI_APEI_NMI symbols.
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org
arch/x86/Kconfig | 2 + drivers/acpi/apei/Kconfig | 8 ++- drivers/acpi/apei/ghes.c | 149 +++++++++++++++++++++++++++++---------------- include/linux/nmi.h | 4 ++ 4 files changed, 110 insertions(+), 53 deletions(-)
...
@@ -894,11 +892,71 @@ static unsigned long ghes_esource_prealloc_size( return prealloc_size; } +static void ghes_estatus_pool_shrink(unsigned long len) +{
- ghes_estatus_pool_size_request -= PAGE_ALIGN(len);
+}
+static void ghes_nmi_add(struct ghes *ghes) +{
- unsigned long len;
- len = ghes_esource_prealloc_size(ghes->generic);
- ghes_estatus_pool_expand(len);
- mutex_lock(&ghes_list_mutex);
- if (list_empty(&ghes_nmi))
register_nmi_handler(NMI_LOCAL, ghes_notify_nmi, 0, "ghes");
- list_add_rcu(&ghes->list, &ghes_nmi);
- mutex_unlock(&ghes_list_mutex);
+}
+static void ghes_nmi_remove(struct ghes *ghes) +{
- unsigned long len;
- mutex_lock(&ghes_list_mutex);
- list_del_rcu(&ghes->list);
- if (list_empty(&ghes_nmi))
unregister_nmi_handler(NMI_LOCAL, "ghes");
- mutex_unlock(&ghes_list_mutex);
- /*
* To synchronize with NMI handler, ghes can only be
* freed after NMI handler finishes.
*/
- synchronize_rcu();
- len = ghes_esource_prealloc_size(ghes->generic);
- ghes_estatus_pool_shrink(len);
+}
+static void ghes_nmi_init_cxt(void) +{
- init_irq_work(&ghes_proc_irq_work, ghes_proc_in_irq);
+} +#else
Markers please to know to which CONFIG_ symbol this #else refers to:
#else /* CONFIG_HAVE_ACPI_APEI_NMI */
+static inline void ghes_nmi_add(struct ghes *ghes) +{
- pr_err(GHES_PFX "ID: %d, trying to add NMI notification which is not supported!\n",
ghes->generic->header.source_id);
- BUG();
+}
+static inline void ghes_nmi_remove(struct ghes *ghes) +{
- pr_err(GHES_PFX "ID: %d, trying to remove NMI notification which is not supported!\n",
ghes->generic->header.source_id);
- BUG();
+}
+static inline void ghes_nmi_init_cxt(void) +{ +} +#endif
Ditto.
On 02.07.2014 14:16, Borislav Petkov wrote:
On Tue, Jun 24, 2014 at 01:41:34PM +0200, Tomasz Nowicki wrote:
Currently APEI depends on x86 architecture. It is because of NMI hardware error notification of GHES which is currently supported by x86 only. However, many other APEI features can be still used perfectly by other architectures.
This commit adds two symbols:
- HAVE_ACPI_APEI for those archs which support APEI.
- HAVE_ACPI_APEI_NMI which is used for NMI code isolation in ghes.c file. NMI related data and functions are grouped so they can be wrapped inside one #ifdef section. Appropriate function stubs are provided for !NMI case.
Note there is no functional changes for x86 due to hard selected HAVE_ACPI_APEI and HAVE_ACPI_APEI_NMI symbols.
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org
arch/x86/Kconfig | 2 + drivers/acpi/apei/Kconfig | 8 ++- drivers/acpi/apei/ghes.c | 149 +++++++++++++++++++++++++++++---------------- include/linux/nmi.h | 4 ++ 4 files changed, 110 insertions(+), 53 deletions(-)
...
@@ -894,11 +892,71 @@ static unsigned long ghes_esource_prealloc_size( return prealloc_size; }
+static void ghes_estatus_pool_shrink(unsigned long len) +{
- ghes_estatus_pool_size_request -= PAGE_ALIGN(len);
+}
+static void ghes_nmi_add(struct ghes *ghes) +{
- unsigned long len;
- len = ghes_esource_prealloc_size(ghes->generic);
- ghes_estatus_pool_expand(len);
- mutex_lock(&ghes_list_mutex);
- if (list_empty(&ghes_nmi))
register_nmi_handler(NMI_LOCAL, ghes_notify_nmi, 0, "ghes");
- list_add_rcu(&ghes->list, &ghes_nmi);
- mutex_unlock(&ghes_list_mutex);
+}
+static void ghes_nmi_remove(struct ghes *ghes) +{
- unsigned long len;
- mutex_lock(&ghes_list_mutex);
- list_del_rcu(&ghes->list);
- if (list_empty(&ghes_nmi))
unregister_nmi_handler(NMI_LOCAL, "ghes");
- mutex_unlock(&ghes_list_mutex);
- /*
* To synchronize with NMI handler, ghes can only be
* freed after NMI handler finishes.
*/
- synchronize_rcu();
- len = ghes_esource_prealloc_size(ghes->generic);
- ghes_estatus_pool_shrink(len);
+}
+static void ghes_nmi_init_cxt(void) +{
- init_irq_work(&ghes_proc_irq_work, ghes_proc_in_irq);
+} +#else
Markers please to know to which CONFIG_ symbol this #else refers to:
#else /* CONFIG_HAVE_ACPI_APEI_NMI */
+static inline void ghes_nmi_add(struct ghes *ghes) +{
- pr_err(GHES_PFX "ID: %d, trying to add NMI notification which is not supported!\n",
ghes->generic->header.source_id);
- BUG();
+}
+static inline void ghes_nmi_remove(struct ghes *ghes) +{
- pr_err(GHES_PFX "ID: %d, trying to remove NMI notification which is not supported!\n",
ghes->generic->header.source_id);
- BUG();
+}
+static inline void ghes_nmi_init_cxt(void) +{ +} +#endif
Ditto.
Right, I will fix that and thanks for all you acks.
BTW, who else should I asked for review to move upstream process one step further?
Regards, Tomasz
On Fri, Jul 04, 2014 at 08:52:44AM +0200, Tomasz Nowicki wrote:
Right, I will fix that and thanks for all you acks.
BTW, who else should I asked for review to move upstream process one step further?
I think you have enough people on CC and if they would find cycles to review them, they're welcome to do so.
But the patches have gotten pretty neat and clean in the meantime so I think Rafael could go over them quickly and maybe even pick them up...
:-)
GHES currently maps two pages with atomic_ioremap. From now on, NMI is architectural depended so there is no need to allocate an NMI page for platforms without NMI support.
To make it possible to not use a second page, swap the existing page order so that the IRQ context page is first, and the optional NMI context page is second. Then, use HAVE_ACPI_APEI_NMI to decide how many pages are to be allocated.
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org --- arch/x86/kernel/acpi/apei.c | 6 ++++++ drivers/acpi/apei/ghes.c | 18 +++++++++++------- include/acpi/apei.h | 1 + 3 files changed, 18 insertions(+), 7 deletions(-)
diff --git a/arch/x86/kernel/acpi/apei.c b/arch/x86/kernel/acpi/apei.c index 12b13de..c280df6 100644 --- a/arch/x86/kernel/acpi/apei.c +++ b/arch/x86/kernel/acpi/apei.c @@ -15,6 +15,7 @@ #include <acpi/apei.h>
#include <asm/mce.h> +#include <asm/tlbflush.h>
int arch_apei_enable_cmcff(struct acpi_hest_header *hest_hdr, void *data) { @@ -54,3 +55,8 @@ void arch_apei_report_mem_error(int sev, struct cper_sec_mem_err *mem_err) apei_mce_report_mem_error(sev, mem_err); #endif } + +void arch_apei_flush_tlb_one(unsigned long addr) +{ + __flush_tlb_one(addr); +} diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index d1fadf2..6d36f19 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -105,12 +105,16 @@ static DEFINE_MUTEX(ghes_list_mutex); */
/* - * Two virtual pages are used, one for NMI context, the other for - * IRQ/PROCESS context + * Two virtual pages are used, one for IRQ/PROCESS context, the other for + * NMI context (optionally). */ -#define GHES_IOREMAP_PAGES 2 -#define GHES_IOREMAP_NMI_PAGE(base) (base) -#define GHES_IOREMAP_IRQ_PAGE(base) ((base) + PAGE_SIZE) +#ifdef CONFIG_HAVE_ACPI_APEI_NMI +#define GHES_IOREMAP_PAGES 2 +#else +#define GHES_IOREMAP_PAGES 1 +#endif +#define GHES_IOREMAP_IRQ_PAGE(base) (base) +#define GHES_IOREMAP_NMI_PAGE(base) ((base) + PAGE_SIZE)
/* virtual memory area for atomic ioremap */ static struct vm_struct *ghes_ioremap_area; @@ -173,7 +177,7 @@ static void ghes_iounmap_nmi(void __iomem *vaddr_ptr)
BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_NMI_PAGE(base)); unmap_kernel_range_noflush(vaddr, PAGE_SIZE); - __flush_tlb_one(vaddr); + arch_apei_flush_tlb_one(vaddr); }
static void ghes_iounmap_irq(void __iomem *vaddr_ptr) @@ -183,7 +187,7 @@ static void ghes_iounmap_irq(void __iomem *vaddr_ptr)
BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_IRQ_PAGE(base)); unmap_kernel_range_noflush(vaddr, PAGE_SIZE); - __flush_tlb_one(vaddr); + arch_apei_flush_tlb_one(vaddr); }
static int ghes_estatus_pool_init(void) diff --git a/include/acpi/apei.h b/include/acpi/apei.h index 8a23c95..76284bb 100644 --- a/include/acpi/apei.h +++ b/include/acpi/apei.h @@ -44,6 +44,7 @@ int erst_clear(u64 record_id);
int arch_apei_enable_cmcff(struct acpi_hest_header *hest_hdr, void *data); void arch_apei_report_mem_error(int sev, struct cper_sec_mem_err *mem_err); +void arch_apei_flush_tlb_one(unsigned long addr);
#endif #endif
On Tue, Jun 24, 2014 at 01:41:35PM +0200, Tomasz Nowicki wrote:
GHES currently maps two pages with atomic_ioremap. From now on, NMI is architectural depended so there is no need to allocate an NMI page for platforms without NMI support.
To make it possible to not use a second page, swap the existing page order so that the IRQ context page is first, and the optional NMI context page is second. Then, use HAVE_ACPI_APEI_NMI to decide how many pages are to be allocated.
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org
Acked-by: Borislav Petkov bp@suse.de