[Intel-xe] [PATCH 4/4] drm/xe/ras: Add support for reporting CSC HW and FW errors.
Himal Prasad Ghimiray
himal.prasad.ghimiray at intel.com
Thu Apr 6 09:22:23 UTC 2023
From: Akeem G Abodunrin <akeem.g.abodunrin at intel.com>
Add support for Memory sparing warning interrupt.
This is applicable to HBM Sparing - when memory degradation
occurred, and it couldn't be repaired through PCLS during
runtime, a warning interrupt generated by the FW, the KMD
driver reads errors register, count them and communicate
the errors to the userspace via UEVENT.
Report CSC HW errors not related to HBM and send
RESET request via uevent.
---
drivers/gpu/drm/xe/regs/xe_regs.h | 32 ++++
drivers/gpu/drm/xe/xe_device_types.h | 2 +
drivers/gpu/drm/xe/xe_gt_types.h | 34 +++++
drivers/gpu/drm/xe/xe_irq.c | 216 +++++++++++++++++++++++++++
drivers/gpu/drm/xe/xe_pci.c | 3 +
5 files changed, 287 insertions(+)
diff --git a/drivers/gpu/drm/xe/regs/xe_regs.h b/drivers/gpu/drm/xe/regs/xe_regs.h
index 422ed63ab32e..fb206e2dd2e1 100644
--- a/drivers/gpu/drm/xe/regs/xe_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_regs.h
@@ -132,6 +132,7 @@ enum hardware_error {
_DEV_ERR_STAT_NONFATAL))
#define DEV_ERR_STAT_SOC_ERROR REG_BIT(16)
#define DEV_ERR_STAT_SGUNIT_ERROR REG_BIT(12)
+#define DEV_ERR_STAT_GSC_ERROR REG_BIT(8)
#define DEV_ERR_STAT_GT_ERROR REG_BIT(0)
enum gt_vctr_registers {
@@ -323,4 +324,35 @@ enum gt_vctr_registers {
#define SOC_IEH0_LOCAL_ERR_STATUS (0)
#define SOC_HW_ERR_MAX_BITS (32)
+#define PVC_GSC_HECI1_BASE 0x00284000
+#define _GSC_HEC_CORR_ERR_STATUS 0x128
+#define _GSC_HEC_UNCORR_ERR_STATUS 0x118
+#define GSC_HEC_CORR_UNCORR_ERR_STATUS(base, x) _MMIO(_PICK_EVEN((x), \
+ (base) + _GSC_HEC_CORR_ERR_STATUS, \
+ (base) + _GSC_HEC_UNCORR_ERR_STATUS))
+#define GSC_COR_FW_REPORTED_ERR (1)
+#define GSC_COR_SRAM_ECC_SINGLE_BIT_ERR (0)
+
+#define GSC_UNCOR_AON_PARITY_ERR (11)
+#define GSC_UNCOR_SELFMBIST_ERR (10)
+#define GSC_UNCOR_FUSE_CRC_CHECK_ERR (9)
+#define GSC_UNCOR_FUSE_PULL_ERR (8)
+#define GSC_UNCOR_GLITCH_DET_ERR (7)
+#define GSC_UNCOR_FW_REPORTED_ERR (6)
+#define GSC_UNCOR_UCODE_PARITY_ERR (5)
+#define GSC_UNCOR_ROM_PARITY_ERR (4)
+#define GSC_UNCOR_WDG_TIMEOUT_ERR (3)
+#define GSC_UNCOR_SRAM_ECC_ERR (2)
+#define GSC_UNCOR_MIA_INT_ERR (1)
+#define GSC_UNCOR_MIA_SHUTDOWN_ERR (0)
+
+#define GSC_HW_ERROR_MAX_ERR_BITS 12
+
+#define GSC_HEC_CORR_FW_ERR_DW0(base) _MMIO((base) + 0x130)
+#define GSC_HEC_UNCORR_FW_ERR_DW0(base) _MMIO((base) + 0x124)
+
+#define BANK_SPARNG_ERR_MITIGATION_DOWNGRADED 0x1000
+#define BANK_SPARNG_DIS_PCLS_EXCEEDED 0x1001
+#define BANK_SPARNG_ENA_PCLS_UNCORRECTABLE 0x1002
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h
index ecabf4d6690d..68e59bfe4ab8 100644
--- a/drivers/gpu/drm/xe/xe_device_types.h
+++ b/drivers/gpu/drm/xe/xe_device_types.h
@@ -101,6 +101,8 @@ struct xe_device {
bool enable_display;
/** @has_gt_error_vectors: whether platform supports ERROR VECTORS */
bool has_gt_error_vectors;
+ /** @has_mem_sparing: whether platform supports HBM status reporting */
+ bool has_mem_sparing;
#if IS_ENABLED(CONFIG_DRM_XE_DISPLAY)
struct xe_device_display_info {
diff --git a/drivers/gpu/drm/xe/xe_gt_types.h b/drivers/gpu/drm/xe/xe_gt_types.h
index bd4a85959df3..4275e8cf5d8c 100644
--- a/drivers/gpu/drm/xe/xe_gt_types.h
+++ b/drivers/gpu/drm/xe/xe_gt_types.h
@@ -78,6 +78,22 @@ enum intel_soc_ieh_reg_type {
INTEL_SOC_REG_GLOBAL
};
+enum intel_gsc_hw_errors {
+ INTEL_GSC_HW_ERROR_COR_SRAM_ECC = 0,
+ INTEL_GSC_HW_ERROR_UNCOR_MIA_SHUTDOWN,
+ INTEL_GSC_HW_ERROR_UNCOR_MIA_INT,
+ INTEL_GSC_HW_ERROR_UNCOR_SRAM_ECC,
+ INTEL_GSC_HW_ERROR_UNCOR_WDG_TIMEOUT,
+ INTEL_GSC_HW_ERROR_UNCOR_ROM_PARITY,
+ INTEL_GSC_HW_ERROR_UNCOR_UCODE_PARITY,
+ INTEL_GSC_HW_ERROR_UNCOR_GLITCH_DET,
+ INTEL_GSC_HW_ERROR_UNCOR_FUSE_PULL,
+ INTEL_GSC_HW_ERROR_UNCOR_FUSE_CRC_CHECK,
+ INTEL_GSC_HW_ERROR_UNCOR_SELFMBIST,
+ INTEL_GSC_HW_ERROR_UNCOR_AON_PARITY,
+ INTEL_GSC_HW_ERROR_COUNT
+};
+
void xe_gt_log_driver_error(struct xe_gt *gt,
const enum xe_gt_driver_errors error,
const char *fmt, ...);
@@ -125,6 +141,18 @@ enum xe_steering_type {
NUM_STEERING_TYPES
};
+struct xe_mem_sparing_event {
+ struct work_struct mem_health_work;
+ u32 cause;
+ enum {
+ MEM_HEALTH_OKAY = 0,
+ MEM_HEALTH_ALARM,
+ MEM_HEALTH_EC_PENDING,
+ MEM_HEALTH_DEGRADED,
+ MEM_HEALTH_UNKNOWN
+ } health_status;
+};
+
/**
* struct xe_gt - Top level struct of a graphics tile
*
@@ -409,11 +437,17 @@ struct xe_gt {
struct intel_hw_errors {
unsigned long hw[INTEL_GT_HW_ERROR_COUNT];
+ unsigned long gsc_hw[INTEL_GSC_HW_ERROR_COUNT];
struct xarray soc;
unsigned long sgunit[HARDWARE_ERROR_MAX];
unsigned long driver[INTEL_GT_DRIVER_ERROR_COUNT];
} errors;
+ struct work_struct gsc_hw_error_work;
+
+ /* Memory sparing data structure for errors reporting on root tile */
+ struct xe_mem_sparing_event mem_sparing;
+
};
#define SOC_HW_ERR_SHIFT ilog2(SOC_HW_ERR_MAX_BITS)
diff --git a/drivers/gpu/drm/xe/xe_irq.c b/drivers/gpu/drm/xe/xe_irq.c
index c047d9b66a7c..67beeec8c187 100644
--- a/drivers/gpu/drm/xe/xe_irq.c
+++ b/drivers/gpu/drm/xe/xe_irq.c
@@ -19,7 +19,13 @@
#include "xe_hw_engine.h"
#include "xe_mmio.h"
+/*TODO Move these macros to headers later to be used by UAPI */
#define HAS_GT_ERROR_VECTORS(xe) ((xe)->info.has_gt_error_vectors)
+#define HAS_MEM_SPARING_SUPPORT(xe) ((xe)->info.has_mem_sparing)
+
+/* TODO move to correct UAPI header */
+#define XE_MEMORY_HEALTH_UEVENT "MEMORY_HEALTH"
+
static void gen3_assert_iir_is_zero(struct xe_gt *gt, i915_reg_t reg)
{
u32 val = xe_mmio_read32(gt, reg.reg);
@@ -864,6 +870,206 @@ xe_soc_hw_error_handler(struct xe_gt *gt, const enum hardware_error hw_err)
(HARDWARE_ERROR_MAX << 1) + 1);
}
+static void xe_gsc_hw_error_work(struct work_struct *work)
+{
+ struct xe_gt *gt =
+ container_of(work, typeof(*gt), gsc_hw_error_work);
+ char *csc_hw_error_event[3];
+
+ csc_hw_error_event[0] = XE_MEMORY_HEALTH_UEVENT "=1";
+ csc_hw_error_event[1] = "SPARING_STATUS_UNKNOWN=1 RESET_REQUIRED=1";
+ csc_hw_error_event[2] = NULL;
+ gt->mem_sparing.health_status = MEM_HEALTH_UNKNOWN;
+
+ dev_notice(gt_to_xe(gt)->drm.dev, "Unknown memory health status, Reset Required\n");
+ kobject_uevent_env(>_to_xe(gt)->drm.primary->kdev->kobj, KOBJ_CHANGE,
+ csc_hw_error_event);
+}
+
+static void xe_mem_health_work(struct work_struct *work)
+{
+ struct xe_gt *gt =
+ container_of(work, typeof(*gt), mem_sparing.mem_health_work);
+ u32 cause;
+ int event_idx = 0;
+ char *sparing_event[3];
+
+ spin_lock_irq(>_to_xe(gt)->irq.lock);
+ cause = gt->mem_sparing.cause;
+ gt->mem_sparing.cause = 0;
+ spin_unlock_irq(>_to_xe(gt)->irq.lock);
+ if (!cause)
+ return;
+
+ sparing_event[event_idx++] = XE_MEMORY_HEALTH_UEVENT "=1";
+ switch (cause) {
+ case BANK_SPARNG_ERR_MITIGATION_DOWNGRADED:
+ gt->mem_sparing.health_status = MEM_HEALTH_ALARM;
+ sparing_event[event_idx++] = "MEM_HEALTH_ALARM=1";
+ dev_notice(gt_to_xe(gt)->drm.dev,
+ "Memory Health Report: Error occurred - No action required.\n"
+ "Error Cause: 0x%x\n", cause);
+ break;
+ case BANK_SPARNG_DIS_PCLS_EXCEEDED:
+ gt->mem_sparing.health_status = MEM_HEALTH_EC_PENDING;
+ sparing_event[event_idx++] = "RESET_REQUIRED=1 EC_PENDING=1";
+ dev_crit(gt_to_xe(gt)->drm.dev,
+ "Memory Health Report: Error correction pending.\n"
+ "Card need to be reset.\n"
+ "Memory might now be functioning in unreliable state.\n"
+ "Error Cause: 0x%x\n", cause);
+ add_taint(TAINT_MACHINE_CHECK, LOCKDEP_STILL_OK);
+ break;
+ case BANK_SPARNG_ENA_PCLS_UNCORRECTABLE:
+ gt->mem_sparing.health_status = MEM_HEALTH_DEGRADED;
+ sparing_event[event_idx++] = "DEGRADED=1 EC_FAILED=1";
+ dev_crit(gt_to_xe(gt)->drm.dev,
+ "Memory Health Report: Memory Health degraded, and runtime fix not feasible.\n"
+ "Replacing card might be the best option.\n"
+ "Error Cause: 0x%x\n", cause);
+ add_taint(TAINT_MACHINE_CHECK, LOCKDEP_STILL_OK);
+ break;
+ default:
+ gt->mem_sparing.health_status = MEM_HEALTH_UNKNOWN;
+ sparing_event[event_idx++] = "SPARING_STATUS_UNKNOWN=1";
+ dev_notice(gt_to_xe(gt)->drm.dev,
+ "Unknown memory health status\n");
+ break;
+ }
+
+ sparing_event[event_idx++] = NULL;
+
+ kobject_uevent_env(>_to_xe(gt)->drm.primary->kdev->kobj, KOBJ_CHANGE,
+ sparing_event);
+}
+
+static void
+xe_gsc_hw_error_handler(struct xe_gt *gt, const enum hardware_error hw_err)
+{
+ u32 base = PVC_GSC_HECI1_BASE;
+ unsigned long err_status;
+ u32 errbit;
+
+ if (!HAS_MEM_SPARING_SUPPORT(gt_to_xe(gt)))
+ return;
+
+ lockdep_assert_held(>_to_xe(gt)->irq.lock);
+
+ err_status = xe_mmio_read32(gt, GSC_HEC_CORR_UNCORR_ERR_STATUS(base, hw_err).reg);
+ if (unlikely(!err_status))
+ return;
+
+ switch (hw_err) {
+ case HARDWARE_ERROR_CORRECTABLE:
+ for_each_set_bit(errbit, &err_status, GSC_HW_ERROR_MAX_ERR_BITS) {
+ u32 err_type = GSC_HW_ERROR_MAX_ERR_BITS;
+ const char *name;
+
+ switch (errbit) {
+ case GSC_COR_SRAM_ECC_SINGLE_BIT_ERR:
+ name = "Single bit error on CSME SRAM";
+ err_type = INTEL_GSC_HW_ERROR_COR_SRAM_ECC;
+ break;
+ case GSC_COR_FW_REPORTED_ERR:
+ gt->mem_sparing.cause |=
+ xe_mmio_read32(gt, GSC_HEC_CORR_FW_ERR_DW0(base).reg);
+ if (unlikely(!gt->mem_sparing.cause))
+ goto re_enable_interrupt;
+ schedule_work(>->mem_sparing.mem_health_work);
+ break;
+ default:
+ name = "Unknown";
+ break;
+ }
+
+ if (err_type != GSC_HW_ERROR_MAX_ERR_BITS)
+ gt->errors.gsc_hw[err_type]++;
+
+ if (errbit != GSC_COR_FW_REPORTED_ERR)
+ drm_err_ratelimited(>_to_xe(gt)->drm, HW_ERR
+ "%s GSC Correctable Error, GSC_HEC_CORR_ERR_STATUS:0x%08lx\n",
+ name, err_status);
+ }
+ break;
+ case HARDWARE_ERROR_NONFATAL:
+ for_each_set_bit(errbit, &err_status, GSC_HW_ERROR_MAX_ERR_BITS) {
+ u32 err_type = GSC_HW_ERROR_MAX_ERR_BITS;
+ const char *name;
+
+ switch (errbit) {
+ case GSC_UNCOR_MIA_SHUTDOWN_ERR:
+ name = "MinuteIA Unexpected Shutdown";
+ err_type = INTEL_GSC_HW_ERROR_UNCOR_MIA_SHUTDOWN;
+ break;
+ case GSC_UNCOR_MIA_INT_ERR:
+ name = "MinuteIA Internal Error";
+ err_type = INTEL_GSC_HW_ERROR_UNCOR_MIA_INT;
+ break;
+ case GSC_UNCOR_SRAM_ECC_ERR:
+ name = "Double bit error on CSME SRAM";
+ err_type = INTEL_GSC_HW_ERROR_UNCOR_SRAM_ECC;
+ break;
+ case GSC_UNCOR_WDG_TIMEOUT_ERR:
+ name = "WDT 2nd Timeout";
+ err_type = INTEL_GSC_HW_ERROR_UNCOR_WDG_TIMEOUT;
+ break;
+ case GSC_UNCOR_ROM_PARITY_ERR:
+ name = "ROM has a parity error";
+ err_type = INTEL_GSC_HW_ERROR_UNCOR_ROM_PARITY;
+ break;
+ case GSC_UNCOR_UCODE_PARITY_ERR:
+ name = "Ucode has a parity error";
+ err_type = INTEL_GSC_HW_ERROR_UNCOR_UCODE_PARITY;
+ break;
+ case GSC_UNCOR_FW_REPORTED_ERR:
+ name = "Errors Reported to FW and Detected by FW";
+ break;
+ case GSC_UNCOR_GLITCH_DET_ERR:
+ name = "Glitch is detected on voltage rail";
+ err_type = INTEL_GSC_HW_ERROR_UNCOR_GLITCH_DET;
+ break;
+ case GSC_UNCOR_FUSE_PULL_ERR:
+ name = "Fuse Pull Error";
+ err_type = INTEL_GSC_HW_ERROR_UNCOR_FUSE_PULL;
+ break;
+ case GSC_UNCOR_FUSE_CRC_CHECK_ERR:
+ name = "Fuse CRC Check Failed on Fuse Pull";
+ err_type = INTEL_GSC_HW_ERROR_UNCOR_FUSE_CRC_CHECK;
+ break;
+ case GSC_UNCOR_SELFMBIST_ERR:
+ name = "Self Mbist Failed";
+ err_type = INTEL_GSC_HW_ERROR_UNCOR_SELFMBIST;
+ break;
+ case GSC_UNCOR_AON_PARITY_ERR:
+ name = "AON RF has parity error";
+ err_type = INTEL_GSC_HW_ERROR_UNCOR_AON_PARITY;
+ break;
+ default:
+ name = "Unknown";
+ break;
+ }
+
+ if (err_type != GSC_HW_ERROR_MAX_ERR_BITS)
+ gt->errors.gsc_hw[err_type]++;
+
+ schedule_work(>->gsc_hw_error_work);
+ drm_err_ratelimited(>_to_xe(gt)->drm, HW_ERR
+ "%s GSC NON_FATAL Error, GSC_HEC_UNCORR_ERR_STATUS:0x%08lx\n",
+ name, err_status);
+ }
+ break;
+ case HARDWARE_ERROR_FATAL:
+ /* GSC error not handled for Fatal Error status */
+ drm_err_ratelimited(>_to_xe(gt)->drm,
+ HW_ERR "Fatal Memory Error Detected\n");
+ default:
+ break;
+ }
+
+re_enable_interrupt:
+ xe_mmio_write32(gt, GSC_HEC_CORR_UNCORR_ERR_STATUS(base, hw_err).reg, err_status);
+}
+
static void
xe_hw_error_source_handler(struct xe_gt *gt, const enum hardware_error hw_err)
{
@@ -888,6 +1094,10 @@ xe_hw_error_source_handler(struct xe_gt *gt, const enum hardware_error hw_err)
if (errsrc & DEV_ERR_STAT_SOC_ERROR)
xe_soc_hw_error_handler(gt, hw_err);
+ /* Memory health status is being tracked on root tile only */
+ if ((errsrc & DEV_ERR_STAT_GSC_ERROR) && gt->info.id == 0)
+ xe_gsc_hw_error_handler(gt, hw_err);
+
xe_mmio_write32(gt, DEV_ERR_STAT_REG(hw_err).reg, errsrc);
out_unlock:
@@ -1133,10 +1343,16 @@ static void process_hw_errors(struct xe_device *xe)
int xe_irq_install(struct xe_device *xe)
{
+ struct xe_gt *gt0 = xe_device_get_gt(xe, 0);
int irq = to_pci_dev(xe->drm.dev)->irq;
irq_handler_t irq_handler;
int err;
+ if (HAS_MEM_SPARING_SUPPORT(xe)) {
+ INIT_WORK(>0->gsc_hw_error_work, xe_gsc_hw_error_work);
+ INIT_WORK(>0->mem_sparing.mem_health_work, xe_mem_health_work);
+ }
+
if (IS_DGFX(xe))
process_hw_errors(xe);
diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c
index 69098194cef8..6eec7f4490df 100644
--- a/drivers/gpu/drm/xe/xe_pci.c
+++ b/drivers/gpu/drm/xe/xe_pci.c
@@ -74,6 +74,7 @@ struct xe_device_desc {
bool has_asid;
bool has_link_copy_engine;
bool has_gt_error_vectors;
+ bool has_mem_sparing;
};
__diag_push();
@@ -234,6 +235,7 @@ static const struct xe_device_desc pvc_desc = {
.has_asid = true,
.has_link_copy_engine = true,
.has_gt_error_vectors = true,
+ .has_mem_sparing = true,
};
#define MTL_MEDIA_ENGINES \
@@ -421,6 +423,7 @@ static int xe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
xe->info.supports_usm = desc->supports_usm;
xe->info.has_asid = desc->has_asid;
xe->info.has_gt_error_vectors = desc->has_gt_error_vectors;
+ xe->info.has_mem_sparing = desc->has_mem_sparing;
xe->info.has_flat_ccs = desc->has_flat_ccs;
xe->info.has_4tile = desc->has_4tile;
xe->info.has_range_tlb_invalidation = desc->has_range_tlb_invalidation;
--
2.25.1
More information about the Intel-xe
mailing list