[PATCH V1 1/6] acpi: apei: read ack upon ghes record consumption

From: Tyler Baicar
Date: Fri Feb 05 2016 - 14:14:37 EST


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.

Signed-off-by: Jonathan (Zhixiong) Zhang <zjzhang@xxxxxxxxxxxxxx>
Signed-off-by: Richard Ruigrok <rruigrok@xxxxxxxxxxxxxx>
Signed-off-by: Tyler Baicar <tbaicar@xxxxxxxxxxxxxx>
Signed-off-by: Naveen Kaje <nkaje@xxxxxxxxxxxxxx>
---
drivers/acpi/apei/ghes.c | 41 +++++++++++++++++++++++++++++++++++++++++
drivers/acpi/apei/hest.c | 7 +++++--
include/acpi/actbl1.h | 21 ++++++++++++++++++++-
include/acpi/ghes.h | 1 +
4 files changed, 67 insertions(+), 3 deletions(-)

diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index 3dd9c46..db67711 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -45,6 +45,7 @@
#include <linux/aer.h>
#include <linux/nmi.h>

+#include <acpi/actbl1.h>
#include <acpi/ghes.h>
#include <acpi/apei.h>
#include <asm/tlbflush.h>
@@ -239,10 +240,22 @@ static struct ghes *ghes_new(struct acpi_hest_generic *generic)
struct ghes *ghes;
unsigned int error_block_length;
int rc;
+ struct acpi_hest_header *hest_hdr;

ghes = kzalloc(sizeof(*ghes), GFP_KERNEL);
if (!ghes)
return ERR_PTR(-ENOMEM);
+
+ hest_hdr = (struct acpi_hest_header *)generic;
+ if (hest_hdr->type == ACPI_HEST_TYPE_GENERIC_ERROR_V2) {
+ ghes->generic_v2 = (struct acpi_hest_generic_v2 *)generic;
+ rc = apei_map_generic_address(
+ &ghes->generic_v2->read_ack_reg_address);
+ if (rc)
+ goto err_unmap;
+ } else
+ ghes->generic_v2 = NULL;
+
ghes->generic = generic;
rc = apei_map_generic_address(&generic->error_status_address);
if (rc)
@@ -265,6 +278,9 @@ static struct ghes *ghes_new(struct acpi_hest_generic *generic)

err_unmap:
apei_unmap_generic_address(&generic->error_status_address);
+ if (ghes->generic_v2)
+ apei_unmap_generic_address(
+ &ghes->generic_v2->read_ack_reg_address);
err_free:
kfree(ghes);
return ERR_PTR(rc);
@@ -274,6 +290,9 @@ static void ghes_fini(struct ghes *ghes)
{
kfree(ghes->estatus);
apei_unmap_generic_address(&ghes->generic->error_status_address);
+ if (ghes->generic_v2)
+ apei_unmap_generic_address(
+ &ghes->generic_v2->error_status_address);
}

static inline int ghes_severity(int severity)
@@ -643,6 +662,22 @@ static void ghes_estatus_cache_add(
rcu_read_unlock();
}

+static int ghes_do_read_ack(struct acpi_hest_generic_v2 *generic_v2)
+{
+ int rc;
+ u64 val = 0;
+
+ rc = apei_read(&val, &generic_v2->read_ack_reg_address);
+ if (rc)
+ return rc;
+ val &= generic_v2->read_ack_preserve <<
+ generic_v2->read_ack_reg_address.bit_offset;
+ val |= generic_v2->read_ack_write;
+ rc = apei_write(val, &generic_v2->read_ack_reg_address);
+
+ return rc;
+}
+
static int ghes_proc(struct ghes *ghes)
{
int rc;
@@ -655,6 +690,12 @@ static int ghes_proc(struct ghes *ghes)
ghes_estatus_cache_add(ghes->generic, ghes->estatus);
}
ghes_do_proc(ghes, ghes->estatus);
+
+ if (ghes->generic_v2) {
+ rc = ghes_do_read_ack(ghes->generic_v2);
+ if (rc)
+ return rc;
+ }
out:
ghes_clear_estatus(ghes);
return 0;
diff --git a/drivers/acpi/apei/hest.c b/drivers/acpi/apei/hest.c
index c708c95..ae43468 100644
--- a/drivers/acpi/apei/hest.c
+++ b/drivers/acpi/apei/hest.c
@@ -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)
@@ -147,7 +148,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;
}
@@ -158,7 +160,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)
diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h
index 16e0136..82695c9 100644
--- a/include/acpi/actbl1.h
+++ b/include/acpi/actbl1.h
@@ -427,7 +427,8 @@ enum acpi_hest_types {
ACPI_HEST_TYPE_AER_ENDPOINT = 7,
ACPI_HEST_TYPE_AER_BRIDGE = 8,
ACPI_HEST_TYPE_GENERIC_ERROR = 9,
- ACPI_HEST_TYPE_RESERVED = 10 /* 10 and greater are reserved */
+ ACPI_HEST_TYPE_GENERIC_ERROR_V2 = 10,
+ ACPI_HEST_TYPE_RESERVED = 11 /* 11 and greater are reserved */
};

/*
@@ -603,6 +604,24 @@ struct acpi_hest_generic {
u32 error_block_length;
};

+/* 10: Generic Hardware Error Source V2*/
+
+struct acpi_hest_generic_v2 {
+ struct acpi_hest_header header;
+ u16 related_source_id;
+ u8 reserved;
+ u8 enabled;
+ u32 records_to_preallocate;
+ u32 max_sections_per_record;
+ u32 max_raw_data_length;
+ struct acpi_generic_address error_status_address;
+ struct acpi_hest_notify notify;
+ u32 error_block_length;
+ struct acpi_generic_address read_ack_reg_address;
+ u64 read_ack_preserve;
+ u64 read_ack_write;
+};
+
/* Generic Error Status block */

struct acpi_hest_generic_status {
diff --git a/include/acpi/ghes.h b/include/acpi/ghes.h
index 720446c..d0108b6 100644
--- a/include/acpi/ghes.h
+++ b/include/acpi/ghes.h
@@ -14,6 +14,7 @@

struct ghes {
struct acpi_hest_generic *generic;
+ struct acpi_hest_generic_v2 *generic_v2;
struct acpi_hest_generic_status *estatus;
u64 buffer_paddr;
unsigned long flags;
--
1.8.2.1