2014-04-09 15:14:20

by Tomasz Nowicki

[permalink] [raw]
Subject: [PATCH 0/7] APEI: Make APEI architecture independent.

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. 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:
- replace arch specific calls with more generic one
- treat NMI notification as GHES feature - CONFIG_ACPI_APEI_NMI
- isolate NMI related code
- reorganize function logic

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 selected for x86 by default. Architectures that want to use this
feature should also provide NMI code infrastructure.

Tomasz Nowicki (7):
apei, mce: Call MCE-specific code only for X86 architecture.
acpi, apei, ghes: Introduce more generic mechanism to init/deinit
GHES error notifications.
ACPI, APEI, GHES: Introduce ACPI_NMI to make NMI error notification a
GHES feature.
acpi, apei, ghes: Factor out NMI error notification context.
acpi, apei, ghes: Attach NMI init/deinit functions while
CONFIG_ACPI_NMI is enabled.
acpi, apei, ghes: Make unmapping functionality independent from
architecture.
acpi, apei, ghes: Factor out ioremap virtual memory for IRQ and NMI
context.

drivers/acpi/apei/Kconfig | 10 +-
drivers/acpi/apei/ghes.c | 329 ++++++++++++++++++++++++++++-----------------
drivers/acpi/apei/hest.c | 8 +-
3 files changed, 218 insertions(+), 129 deletions(-)

--
1.7.9.5


2014-04-09 15:14:35

by Tomasz Nowicki

[permalink] [raw]
Subject: [PATCH 5/7] acpi, apei, ghes: Attach NMI init/deinit functions while CONFIG_ACPI_APEI_NMI is enabled.

Thanks to more generic way of init/deinit error notification, we can
register NMI related calls in runtime. It happens before walking through
GHES entries, so probe function will treat NMI as supported.

Signed-off-by: Tomasz Nowicki <[email protected]>
---
drivers/acpi/apei/ghes.c | 21 ++++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)

diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index 7a0d66e..aaf8db3 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -906,7 +906,6 @@ static void ghes_estatus_pool_shrink(unsigned long len)
{
ghes_estatus_pool_size_request -= PAGE_ALIGN(len);
}
-#endif

static int ghes_notify_init_nmi(struct ghes *ghes)
{
@@ -941,6 +940,20 @@ static void ghes_notify_remove_nmi(struct ghes *ghes)
ghes_estatus_pool_shrink(len);
}

+static void ghes_init_nmi(void)
+{
+ init_irq_work(&ghes_proc_irq_work, ghes_proc_in_irq);
+ ghes_notify_tab[ACPI_HEST_NOTIFY_NMI].init_call = ghes_notify_init_nmi;
+ ghes_notify_tab[ACPI_HEST_NOTIFY_NMI].remove_call = ghes_notify_remove_nmi;
+
+}
+#else
+inline static void ghes_init_nmi(void)
+{
+
+}
+#endif
+
static int ghes_notify_init_polled(struct ghes *ghes)
{
ghes->timer.function = ghes_poll_func;
@@ -1018,9 +1031,7 @@ static struct ghes_notify_setup
[ACPI_HEST_NOTIFY_SCI] = {"SCI",
ghes_notify_init_sci,
ghes_notify_remove_sci},
- [ACPI_HEST_NOTIFY_NMI] = {"NMI",
- ghes_notify_init_nmi,
- ghes_notify_remove_nmi},
+ [ACPI_HEST_NOTIFY_NMI] = {"NMI", NULL, NULL},
[ACPI_HEST_NOTIFY_CMCI] = {"CMCI", NULL, NULL},
[ACPI_HEST_NOTIFY_MCE] = {"MCE", NULL, NULL},
};
@@ -1141,7 +1152,7 @@ static int __init ghes_init(void)
return -EINVAL;
}

- init_irq_work(&ghes_proc_irq_work, ghes_proc_in_irq);
+ ghes_init_nmi();

rc = ghes_ioremap_init();
if (rc)
--
1.7.9.5

2014-04-09 15:14:32

by Tomasz Nowicki

[permalink] [raw]
Subject: [PATCH 4/7] acpi, apei, ghes: Factor out NMI error notification context.

Use CONFIG_ACPI_APEI_NMI to isolate NMI error notification path. NMI related
data and functions are grouped so they can be wrapped inside one

Signed-off-by: Tomasz Nowicki <[email protected]>
---
drivers/acpi/apei/ghes.c | 54 +++++++++++++++++++++++++---------------------
1 file changed, 30 insertions(+), 24 deletions(-)

diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index ca8387e..7a0d66e 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -53,7 +53,9 @@
#include <asm/mce.h>
#endif
#include <asm/tlbflush.h>
+#ifdef CONFIG_ACPI_APEI_NMI
#include <asm/nmi.h>
+#endif

#include "apei-internal.h"

@@ -88,8 +90,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
@@ -99,16 +99,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
@@ -132,18 +125,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;
@@ -259,11 +242,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;
@@ -744,6 +722,28 @@ static int ghes_notify_sci(struct notifier_block *this,
return ret;
}

+#ifdef CONFIG_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;
@@ -902,6 +902,12 @@ 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);
+}
+#endif
+
static int ghes_notify_init_nmi(struct ghes *ghes)
{
unsigned long len;
--
1.7.9.5

2014-04-09 15:14:26

by Tomasz Nowicki

[permalink] [raw]
Subject: [PATCH 2/7] acpi, apei, ghes: Introduce more generic mechanism to init/deinit GHES error notifications.

Init/deinit of GHES error notifications are moved to corresponding
functions e.g. for SCI ghes_notify_init_sci{sci} and ghes_notify_remove_{sci}
which in turn are gathered to ghes_notify_tab table.
ghes_probe and ghes_remove check notification support based on
function reference pointer in ghes_notify_tab and call it with ghes argument.

This approach allows us to change address of a given error notification
init/deinit function call in ghes_notify_tab in runtime. In turn,
we can avoid #ifdef usage in common code e.g. for NMI which is currently
supported by x86.

Signed-off-by: Tomasz Nowicki <[email protected]>
---
drivers/acpi/apei/ghes.c | 242 ++++++++++++++++++++++++++++------------------
1 file changed, 149 insertions(+), 93 deletions(-)

diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index f7edffc..ca8387e 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -148,6 +148,14 @@ 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;

+struct ghes_notify_setup {
+ const char *notif_name;
+ int (*init_call)(struct ghes *ghes);
+ void (*remove_call)(struct ghes *ghes);
+};
+
+static struct ghes_notify_setup ghes_notify_tab[];
+
static int ghes_ioremap_init(void)
{
ghes_ioremap_area = __get_vm_area(PAGE_SIZE * GHES_IOREMAP_PAGES,
@@ -879,10 +887,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)
{
@@ -898,33 +902,151 @@ static unsigned long ghes_esource_prealloc_size(
return prealloc_size;
}

+static int ghes_notify_init_nmi(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);
+
+ return 0;
+}
+
+static void ghes_notify_remove_nmi(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 int ghes_notify_init_polled(struct ghes *ghes)
+{
+ ghes->timer.function = ghes_poll_func;
+ ghes->timer.data = (unsigned long)ghes;
+ init_timer_deferrable(&ghes->timer);
+ ghes_add_timer(ghes);
+
+ return 0;
+}
+
+static void ghes_notify_remove_polled(struct ghes *ghes)
+{
+ del_timer_sync(&ghes->timer);
+}
+
+static int ghes_notify_init_external(struct ghes *ghes)
+{
+ int rc;
+
+ /* External interrupt vector is GSI */
+ rc = acpi_gsi_to_irq(ghes->generic->notify.vector, &ghes->irq);
+ if (rc) {
+ pr_err(GHES_PFX "Failed to map GSI to IRQ for generic hardware "
+ "error source: %d\n", ghes->generic->header.source_id);
+ goto out;
+ }
+
+ rc = request_irq(ghes->irq, ghes_irq_func, 0, "GHES IRQ", ghes);
+ if (rc)
+ pr_err(GHES_PFX "Failed to register IRQ for generic hardware "
+ "error source: %d\n", ghes->generic->header.source_id);
+
+out:
+ return rc;
+}
+
+static void ghes_notify_remove_external(struct ghes *ghes)
+{
+ free_irq(ghes->irq, ghes);
+}
+
+static struct notifier_block ghes_notifier_sci = {
+ .notifier_call = ghes_notify_sci,
+};
+
+static int ghes_notify_init_sci(struct ghes *ghes)
+{
+ mutex_lock(&ghes_list_mutex);
+ if (list_empty(&ghes_sci))
+ register_acpi_hed_notifier(&ghes_notifier_sci);
+ list_add_rcu(&ghes->list, &ghes_sci);
+ mutex_unlock(&ghes_list_mutex);
+
+ return 0;
+}
+
+static void ghes_notify_remove_sci(struct ghes *ghes)
+{
+ mutex_lock(&ghes_list_mutex);
+ list_del_rcu(&ghes->list);
+ if (list_empty(&ghes_sci))
+ unregister_acpi_hed_notifier(&ghes_notifier_sci);
+ mutex_unlock(&ghes_list_mutex);
+}
+
+static struct ghes_notify_setup
+ ghes_notify_tab[ACPI_HEST_NOTIFY_RESERVED] = {
+ [ACPI_HEST_NOTIFY_POLLED] = {"POLLED",
+ ghes_notify_init_polled,
+ ghes_notify_remove_polled},
+ [ACPI_HEST_NOTIFY_EXTERNAL] = {"EXT_IRQ",
+ ghes_notify_init_external,
+ ghes_notify_remove_external},
+ [ACPI_HEST_NOTIFY_LOCAL] = {"LOCAL_IRQ", NULL, NULL},
+ [ACPI_HEST_NOTIFY_SCI] = {"SCI",
+ ghes_notify_init_sci,
+ ghes_notify_remove_sci},
+ [ACPI_HEST_NOTIFY_NMI] = {"NMI",
+ ghes_notify_init_nmi,
+ ghes_notify_remove_nmi},
+ [ACPI_HEST_NOTIFY_CMCI] = {"CMCI", NULL, NULL},
+ [ACPI_HEST_NOTIFY_MCE] = {"MCE", NULL, NULL},
+};
+
static int ghes_probe(struct platform_device *ghes_dev)
{
struct acpi_hest_generic *generic;
struct ghes *ghes = NULL;
- unsigned long len;
int rc = -EINVAL;
+ int (*ghes_init_call)(struct ghes *ghes);
+ const char *ghes_notif_name;

generic = *(struct acpi_hest_generic **)ghes_dev->dev.platform_data;
if (!generic->enabled)
return -ENODEV;

- switch (generic->notify.type) {
- case ACPI_HEST_NOTIFY_POLLED:
- case ACPI_HEST_NOTIFY_EXTERNAL:
- case ACPI_HEST_NOTIFY_SCI:
- case ACPI_HEST_NOTIFY_NMI:
- break;
- case ACPI_HEST_NOTIFY_LOCAL:
- pr_warning(GHES_PFX "Generic hardware error source: %d notified via local interrupt is not supported!\n",
- generic->header.source_id);
- goto err;
- default:
- pr_warning(FW_WARN GHES_PFX "Unknown notification type: %u for generic hardware error source: %d\n",
+ if (generic->notify.type >= ACPI_HEST_NOTIFY_RESERVED) {
+ pr_warning(FW_WARN GHES_PFX "Unknown notification type: %u for "
+ "generic hardware error source: %d\n",
generic->notify.type, generic->header.source_id);
goto err;
}

+ ghes_init_call = ghes_notify_tab[generic->notify.type].init_call;
+ ghes_notif_name = ghes_notify_tab[generic->notify.type].notif_name;
+ if (!ghes_init_call) {
+ pr_warning(GHES_PFX "Generic hardware error source: %d notified"
+ " via %s is not supported!\n",
+ generic->header.source_id, ghes_notif_name);
+ goto err;
+ }
+
rc = -EIO;
if (generic->error_block_length <
sizeof(struct acpi_generic_status)) {
@@ -944,48 +1066,10 @@ static int ghes_probe(struct platform_device *ghes_dev)
if (rc < 0)
goto err;

- switch (generic->notify.type) {
- case ACPI_HEST_NOTIFY_POLLED:
- ghes->timer.function = ghes_poll_func;
- ghes->timer.data = (unsigned long)ghes;
- init_timer_deferrable(&ghes->timer);
- ghes_add_timer(ghes);
- break;
- case ACPI_HEST_NOTIFY_EXTERNAL:
- /* External interrupt vector is GSI */
- rc = acpi_gsi_to_irq(generic->notify.vector, &ghes->irq);
- if (rc) {
- pr_err(GHES_PFX "Failed to map GSI to IRQ for generic hardware error source: %d\n",
- generic->header.source_id);
- goto err_edac_unreg;
- }
- rc = request_irq(ghes->irq, ghes_irq_func, 0, "GHES IRQ", ghes);
- if (rc) {
- pr_err(GHES_PFX "Failed to register IRQ for generic hardware error source: %d\n",
- generic->header.source_id);
- goto err_edac_unreg;
- }
- break;
- case ACPI_HEST_NOTIFY_SCI:
- mutex_lock(&ghes_list_mutex);
- if (list_empty(&ghes_sci))
- register_acpi_hed_notifier(&ghes_notifier_sci);
- list_add_rcu(&ghes->list, &ghes_sci);
- 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);
- break;
- default:
- BUG();
- }
+ rc = (*ghes_init_call)(ghes);
+ if (rc)
+ goto err_edac_unreg;
+
platform_set_drvdata(ghes_dev, ghes);

return 0;
@@ -1003,44 +1087,16 @@ static int ghes_remove(struct platform_device *ghes_dev)
{
struct ghes *ghes;
struct acpi_hest_generic *generic;
- unsigned long len;
+ void (*ghes_remove_call)(struct ghes *ghes);

ghes = platform_get_drvdata(ghes_dev);
+ ghes->flags |= GHES_EXITING;
+
generic = ghes->generic;
+ ghes_remove_call = ghes_notify_tab[generic->notify.type].remove_call;

- ghes->flags |= GHES_EXITING;
- switch (generic->notify.type) {
- case ACPI_HEST_NOTIFY_POLLED:
- del_timer_sync(&ghes->timer);
- break;
- case ACPI_HEST_NOTIFY_EXTERNAL:
- free_irq(ghes->irq, ghes);
- break;
- case ACPI_HEST_NOTIFY_SCI:
- mutex_lock(&ghes_list_mutex);
- list_del_rcu(&ghes->list);
- if (list_empty(&ghes_sci))
- unregister_acpi_hed_notifier(&ghes_notifier_sci);
- 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);
- break;
- default:
- BUG();
- break;
- }
+ if (ghes_remove_call)
+ (*ghes_remove_call)(ghes);

ghes_fini(ghes);

--
1.7.9.5

2014-04-09 15:15:21

by Tomasz Nowicki

[permalink] [raw]
Subject: [PATCH 6/7] acpi, apei, ghes: Make unmapping functionality independent from architecture.

Till now __flush_tlb_one was used for unmapping virtual memory which
is x86 specific function. Replace it with more generic
flush_tlb_kernel_range.

Signed-off-by: Tomasz Nowicki <[email protected]>
---
drivers/acpi/apei/ghes.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index aaf8db3..624878b 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -185,7 +185,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);
+ flush_tlb_kernel_range(vaddr, vaddr + PAGE_SIZE);
}

static void ghes_iounmap_irq(void __iomem *vaddr_ptr)
@@ -195,7 +195,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);
+ flush_tlb_kernel_range(vaddr, vaddr + PAGE_SIZE);
}

static int ghes_estatus_pool_init(void)
--
1.7.9.5

2014-04-09 15:15:18

by Tomasz Nowicki

[permalink] [raw]
Subject: [PATCH 7/7] acpi, apei, ghes: Factor out ioremap virtual memory for IRQ and NMI context.

GHES currently maps two pages with atomic_ioremap. From now
on, NMI is optional 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 CONFIG_ACPI_APEI_NMI to decide
at runtime how many pages are to be allocated. Finally, put in
sanity checks to avoid accessing unallocated memory.

Signed-off-by: Tomasz Nowicki <[email protected]>
---
drivers/acpi/apei/ghes.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index 624878b..20377ac 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -109,12 +109,11 @@ 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)
+#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;
@@ -141,7 +140,8 @@ static struct ghes_notify_setup ghes_notify_tab[];

static int ghes_ioremap_init(void)
{
- ghes_ioremap_area = __get_vm_area(PAGE_SIZE * GHES_IOREMAP_PAGES,
+ ghes_ioremap_area = __get_vm_area(
+ PAGE_SIZE * (IS_ENABLED(CONFIG_ACPI_APEI_NMI) ? 2 : 1),
VM_IOREMAP, VMALLOC_START, VMALLOC_END);
if (!ghes_ioremap_area) {
pr_err(GHES_PFX "Failed to allocate virtual memory area for atomic ioremap.\n");
@@ -310,6 +310,8 @@ static void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len,
u64 offset;
u32 trunk;

+ BUG_ON(in_nmi && !IS_ENABLED(CONFIG_ACPI_APEI_NMI));
+
while (len > 0) {
offset = paddr - (paddr & PAGE_MASK);
if (in_nmi) {
--
1.7.9.5

2014-04-09 15:17:44

by Tomasz Nowicki

[permalink] [raw]
Subject: [PATCH 3/7] ACPI, APEI, GHES: Introduce ACPI_APEI_NMI to make NMI error notification a GHES feature.

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 ACPI_APEI_NMI which will be used in next patches to isolate
NMI related code in ghes.c file. Only NMI error notification feature
depends on x86 and it is selected by default for x86 arch.

Signed-off-by: Tomasz Nowicki <[email protected]>
---
drivers/acpi/apei/Kconfig | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/drivers/acpi/apei/Kconfig b/drivers/acpi/apei/Kconfig
index c4dac71..3ae248a 100644
--- a/drivers/acpi/apei/Kconfig
+++ b/drivers/acpi/apei/Kconfig
@@ -3,7 +3,6 @@ config ACPI_APEI
select MISC_FILESYSTEMS
select PSTORE
select UEFI_CPER
- depends on X86
help
APEI allows to report errors (for example from the chipset)
to the operating system. This improves NMI handling
@@ -16,6 +15,7 @@ config ACPI_APEI_GHES
select ACPI_HED
select IRQ_WORK
select GENERIC_ALLOCATOR
+ select ACPI_APEI_NMI if X86
help
Generic Hardware Error Source provides a way to report
platform hardware errors (such as that from chipset). It
@@ -26,6 +26,14 @@ config ACPI_APEI_GHES
by firmware to produce more valuable hardware error
information for Linux.

+config ACPI_APEI_NMI
+ bool "NMI error notification support"
+ depends on ACPI_APEI_GHES
+ help
+ Firmware first mode can use NMI notification mechanism to report errors
+ to operating system. This feature is currently supported by X86
+ architecture only.
+
config ACPI_APEI_PCIEAER
bool "APEI PCIe AER logging/recovering support"
depends on ACPI_APEI && PCIEAER
--
1.7.9.5

2014-04-09 15:18:53

by Tomasz Nowicki

[permalink] [raw]
Subject: [PATCH 1/7] apei, mce: Call MCE-specific code only for X86 architecture.

This commit is dealing with MCE code in:
- hest.c
Move acpi_disable_cmcff flag to hest_parse_cmc() and makes
that depend on CONFIG_X86_MCE so that we do not have to maintain
acpi_disable_cmcff for architectures which do not support MCE.
Also, wrap architectural MCE header inside #ifdef CONFIG_X86_MCE.

- ghes.c
Wrap architectural MCE header inside #ifdef CONFIG_X86_MCE similar to rest
of the MCE code in this file.

Signed-off-by: Tomasz Nowicki <[email protected]>
---
drivers/acpi/apei/ghes.c | 2 ++
drivers/acpi/apei/hest.c | 8 ++++++--
2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index dab7cb7..f7edffc 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -49,7 +49,9 @@
#include <linux/aer.h>

#include <acpi/ghes.h>
+#ifdef CONFIG_X86_MCE
#include <asm/mce.h>
+#endif
#include <asm/tlbflush.h>
#include <asm/nmi.h>

diff --git a/drivers/acpi/apei/hest.c b/drivers/acpi/apei/hest.c
index f5e37f3..98db702 100644
--- a/drivers/acpi/apei/hest.c
+++ b/drivers/acpi/apei/hest.c
@@ -36,7 +36,9 @@
#include <linux/io.h>
#include <linux/platform_device.h>
#include <acpi/apei.h>
+#ifdef CONFIG_X86_MCE
#include <asm/mce.h>
+#endif

#include "apei-internal.h"

@@ -133,6 +135,9 @@ static int __init hest_parse_cmc(struct acpi_hest_header *hest_hdr, void *data)
struct acpi_hest_ia_corrected *cmc;
struct acpi_hest_ia_error_bank *mc_bank;

+ if (acpi_disable_cmcff)
+ return 1;
+
if (hest_hdr->type != ACPI_HEST_TYPE_IA32_CORRECTED_CHECK)
return 0;

@@ -263,8 +268,7 @@ void __init acpi_hest_init(void)
goto err;
}

- if (!acpi_disable_cmcff)
- apei_hest_parse(hest_parse_cmc, NULL);
+ apei_hest_parse(hest_parse_cmc, NULL);

if (!ghes_disable) {
rc = apei_hest_parse(hest_parse_ghes_count, &ghes_count);
--
1.7.9.5