acpi: apei: read ack upon ghes record consumption
authorTyler Baicar <tbaicar@codeaurora.org>
Wed, 21 Jun 2017 18:17:04 +0000 (12:17 -0600)
committerWill Deacon <will.deacon@arm.com>
Thu, 22 Jun 2017 14:43:47 +0000 (15:43 +0100)
A RAS (Reliability, Availability, Serviceability) controller
may be a separate processor running in parallel with OS
execution, and may generate error records for consumption by
the OS. If the RAS controller produces multiple error records,
then they may be overwritten before the OS has consumed them.

The Generic Hardware Error Source (GHES) v2 structure
introduces the capability for the OS to acknowledge the
consumption of the error record generated by the RAS
controller. A RAS controller supporting GHESv2 shall wait for
the acknowledgment before writing a new error record, thus
eliminating the race condition.

Add support for parsing of GHESv2 sub-tables as well.

Signed-off-by: Tyler Baicar <tbaicar@codeaurora.org>
CC: Jonathan (Zhixiong) Zhang <zjzhang@codeaurora.org>
Reviewed-by: James Morse <james.morse@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
drivers/acpi/apei/ghes.c
drivers/acpi/apei/hest.c
include/acpi/ghes.h

index 980515e029fae538de8889782226f7b284fbd727..866d244c63118ee6072f8086a0a596002f41789f 100644 (file)
@@ -46,6 +46,7 @@
 #include <linux/nmi.h>
 #include <linux/sched/clock.h>
 
+#include <acpi/actbl1.h>
 #include <acpi/ghes.h>
 #include <acpi/apei.h>
 #include <asm/tlbflush.h>
        ((struct acpi_hest_generic_status *)                            \
         ((struct ghes_estatus_node *)(estatus_node) + 1))
 
+static inline bool is_hest_type_generic_v2(struct ghes *ghes)
+{
+       return ghes->generic->header.type == ACPI_HEST_TYPE_GENERIC_ERROR_V2;
+}
+
 /*
  * This driver isn't really modular, however for the time being,
  * continuing to use module_param is the easiest way to remain
@@ -240,6 +246,16 @@ static int ghes_estatus_pool_expand(unsigned long len)
        return 0;
 }
 
+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);
+}
+
 static struct ghes *ghes_new(struct acpi_hest_generic *generic)
 {
        struct ghes *ghes;
@@ -249,10 +265,17 @@ static struct ghes *ghes_new(struct acpi_hest_generic *generic)
        ghes = kzalloc(sizeof(*ghes), GFP_KERNEL);
        if (!ghes)
                return ERR_PTR(-ENOMEM);
+
        ghes->generic = generic;
+       if (is_hest_type_generic_v2(ghes)) {
+               rc = map_gen_v2(ghes);
+               if (rc)
+                       goto err_free;
+       }
+
        rc = apei_map_generic_address(&generic->error_status_address);
        if (rc)
-               goto err_free;
+               goto err_unmap_read_ack_addr;
        error_block_length = generic->error_block_length;
        if (error_block_length > GHES_ESTATUS_MAX_SIZE) {
                pr_warning(FW_WARN GHES_PFX
@@ -264,13 +287,16 @@ static struct ghes *ghes_new(struct acpi_hest_generic *generic)
        ghes->estatus = kmalloc(error_block_length, GFP_KERNEL);
        if (!ghes->estatus) {
                rc = -ENOMEM;
-               goto err_unmap;
+               goto err_unmap_status_addr;
        }
 
        return ghes;
 
-err_unmap:
+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);
@@ -280,6 +306,8 @@ static 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)
@@ -649,6 +677,21 @@ static void ghes_estatus_cache_add(
        rcu_read_unlock();
 }
 
+static int ghes_ack_error(struct acpi_hest_generic_v2 *gv2)
+{
+       int rc;
+       u64 val = 0;
+
+       rc = apei_read(&val, &gv2->read_ack_register);
+       if (rc)
+               return rc;
+
+       val &= gv2->read_ack_preserve << gv2->read_ack_register.bit_offset;
+       val |= gv2->read_ack_write    << gv2->read_ack_register.bit_offset;
+
+       return apei_write(val, &gv2->read_ack_register);
+}
+
 static int ghes_proc(struct ghes *ghes)
 {
        int rc;
@@ -661,6 +704,16 @@ static int ghes_proc(struct ghes *ghes)
                        ghes_estatus_cache_add(ghes->generic, ghes->estatus);
        }
        ghes_do_proc(ghes, ghes->estatus);
+
+       /*
+        * 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)) {
+               rc = ghes_ack_error(ghes->generic_v2);
+               if (rc)
+                       return rc;
+       }
 out:
        ghes_clear_estatus(ghes);
        return rc;
index 8f2a98e23bbae906fcf78421b3b48722fd9f67c0..456b488eb1df5bf016606cc2ba412b6335613eea 100644 (file)
@@ -52,6 +52,7 @@ static const int hest_esrc_len_tab[ACPI_HEST_TYPE_RESERVED] = {
        [ACPI_HEST_TYPE_AER_ENDPOINT] = sizeof(struct acpi_hest_aer),
        [ACPI_HEST_TYPE_AER_BRIDGE] = sizeof(struct acpi_hest_aer_bridge),
        [ACPI_HEST_TYPE_GENERIC_ERROR] = sizeof(struct acpi_hest_generic),
+       [ACPI_HEST_TYPE_GENERIC_ERROR_V2] = sizeof(struct acpi_hest_generic_v2),
 };
 
 static int hest_esrc_len(struct acpi_hest_header *hest_hdr)
@@ -141,7 +142,8 @@ static int __init hest_parse_ghes_count(struct acpi_hest_header *hest_hdr, void
 {
        int *count = data;
 
-       if (hest_hdr->type == ACPI_HEST_TYPE_GENERIC_ERROR)
+       if (hest_hdr->type == ACPI_HEST_TYPE_GENERIC_ERROR ||
+           hest_hdr->type == ACPI_HEST_TYPE_GENERIC_ERROR_V2)
                (*count)++;
        return 0;
 }
@@ -152,7 +154,8 @@ static int __init hest_parse_ghes(struct acpi_hest_header *hest_hdr, void *data)
        struct ghes_arr *ghes_arr = data;
        int rc, i;
 
-       if (hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR)
+       if (hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR &&
+           hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR_V2)
                return 0;
 
        if (!((struct acpi_hest_generic *)hest_hdr)->enabled)
index 720446cb243e81f07a02a8763eea0994ce43e772..68f088a92398eb8303945732ca3a141796e02520 100644 (file)
 #define GHES_EXITING           0x0002
 
 struct ghes {
-       struct acpi_hest_generic *generic;
+       union {
+               struct acpi_hest_generic *generic;
+               struct acpi_hest_generic_v2 *generic_v2;
+       };
        struct acpi_hest_generic_status *estatus;
        u64 buffer_paddr;
        unsigned long flags;