powerpc/eeh: Introduce eeh_ops::err_inject
authorGavin Shan <gwshan@linux.vnet.ibm.com>
Tue, 30 Sep 2014 02:38:56 +0000 (12:38 +1000)
committerMichael Ellerman <mpe@ellerman.id.au>
Tue, 30 Sep 2014 07:15:10 +0000 (17:15 +1000)
The patch introduces eeh_ops::err_inject(), which allows to inject
specified errors to indicated PE for testing purpose. The functionality
isn't support on pSeries platform. On PowerNV, the functionality
relies on OPAL API opal_pci_err_inject().

Signed-off-by: Mike Qiu <qiudayu@linux.vnet.ibm.com>
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
arch/powerpc/include/asm/eeh.h
arch/powerpc/platforms/powernv/eeh-ioda.c
arch/powerpc/platforms/powernv/eeh-powernv.c
arch/powerpc/platforms/powernv/pci.h
arch/powerpc/platforms/pseries/eeh_pseries.c

index 59d3e2a4fc42f8839636dc5a5b5cb151890787ec..4fa15796537a8f6c3d6443f1cc4bc0ff661ac7ae 100644 (file)
@@ -204,6 +204,8 @@ struct eeh_ops {
        int (*wait_state)(struct eeh_pe *pe, int max_wait);
        int (*get_log)(struct eeh_pe *pe, int severity, char *drv_log, unsigned long len);
        int (*configure_bridge)(struct eeh_pe *pe);
+       int (*err_inject)(struct eeh_pe *pe, int type, int func,
+                         unsigned long addr, unsigned long mask);
        int (*read_config)(struct device_node *dn, int where, int size, u32 *val);
        int (*write_config)(struct device_node *dn, int where, int size, u32 val);
        int (*next_error)(struct eeh_pe **pe);
index 1d8f37b93457d40d14707c087db237f6c8753340..1ac80db1489bbfa0ffaa198174d77f28ca3ff1b4 100644 (file)
@@ -673,6 +673,49 @@ static int ioda_eeh_configure_bridge(struct eeh_pe *pe)
        return 0;
 }
 
+static int ioda_eeh_err_inject(struct eeh_pe *pe, int type, int func,
+                              unsigned long addr, unsigned long mask)
+{
+       struct pci_controller *hose = pe->phb;
+       struct pnv_phb *phb = hose->private_data;
+       s64 ret;
+
+       /* Sanity check on error type */
+       if (type != OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR &&
+           type != OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64) {
+               pr_warn("%s: Invalid error type %d\n",
+                       __func__, type);
+               return -ERANGE;
+       }
+
+       if (func < OPAL_ERR_INJECT_FUNC_IOA_LD_MEM_ADDR ||
+           func > OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_TARGET) {
+               pr_warn("%s: Invalid error function %d\n",
+                       __func__, func);
+               return -ERANGE;
+       }
+
+       /* Firmware supports error injection ? */
+       if (!opal_check_token(OPAL_PCI_ERR_INJECT)) {
+               pr_warn("%s: Firmware doesn't support error injection\n",
+                       __func__);
+               return -ENXIO;
+       }
+
+       /* Do error injection */
+       ret = opal_pci_err_inject(phb->opal_id, pe->addr,
+                                 type, func, addr, mask);
+       if (ret != OPAL_SUCCESS) {
+               pr_warn("%s: Failure %lld injecting error "
+                       "%d-%d to PHB#%x-PE#%x\n",
+                       __func__, ret, type, func,
+                       hose->global_number, pe->addr);
+               return -EIO;
+       }
+
+       return 0;
+}
+
 static void ioda_eeh_hub_diag_common(struct OpalIoP7IOCErrorData *data)
 {
        /* GEM */
@@ -994,5 +1037,6 @@ struct pnv_eeh_ops ioda_eeh_ops = {
        .reset                  = ioda_eeh_reset,
        .get_log                = ioda_eeh_get_log,
        .configure_bridge       = ioda_eeh_configure_bridge,
+       .err_inject             = ioda_eeh_err_inject,
        .next_error             = ioda_eeh_next_error
 };
index fd7a16f855edffd1315b055800b7e4de6ab28fc3..3e89cbf5588547660ed9f3543735c6b644ae77e9 100644 (file)
@@ -358,6 +358,31 @@ static int powernv_eeh_configure_bridge(struct eeh_pe *pe)
        return ret;
 }
 
+/**
+ * powernv_pe_err_inject - Inject specified error to the indicated PE
+ * @pe: the indicated PE
+ * @type: error type
+ * @func: specific error type
+ * @addr: address
+ * @mask: address mask
+ *
+ * The routine is called to inject specified error, which is
+ * determined by @type and @func, to the indicated PE for
+ * testing purpose.
+ */
+static int powernv_eeh_err_inject(struct eeh_pe *pe, int type, int func,
+                                 unsigned long addr, unsigned long mask)
+{
+       struct pci_controller *hose = pe->phb;
+       struct pnv_phb *phb = hose->private_data;
+       int ret = -EEXIST;
+
+       if (phb->eeh_ops && phb->eeh_ops->err_inject)
+               ret = phb->eeh_ops->err_inject(pe, type, func, addr, mask);
+
+       return ret;
+}
+
 /**
  * powernv_eeh_next_error - Retrieve next EEH error to handle
  * @pe: Affected PE
@@ -414,6 +439,7 @@ static struct eeh_ops powernv_eeh_ops = {
        .wait_state             = powernv_eeh_wait_state,
        .get_log                = powernv_eeh_get_log,
        .configure_bridge       = powernv_eeh_configure_bridge,
+       .err_inject             = powernv_eeh_err_inject,
        .read_config            = pnv_pci_cfg_read,
        .write_config           = pnv_pci_cfg_write,
        .next_error             = powernv_eeh_next_error,
index 48494d4b60581d43baac6a59a198166786ec4101..27594cf7f76d42f1202dc926180013ded1b627f6 100644 (file)
@@ -85,6 +85,8 @@ struct pnv_eeh_ops {
        int (*get_log)(struct eeh_pe *pe, int severity,
                       char *drv_log, unsigned long len);
        int (*configure_bridge)(struct eeh_pe *pe);
+       int (*err_inject)(struct eeh_pe *pe, int type, int func,
+                         unsigned long addr, unsigned long mask);
        int (*next_error)(struct eeh_pe **pe);
 };
 #endif /* CONFIG_EEH */
index b645dc60e0004637c9046bad341f2681e2d8d234..4fc5ff96f418ec238ddf980924851df38e335d3d 100644 (file)
@@ -731,6 +731,7 @@ static struct eeh_ops pseries_eeh_ops = {
        .wait_state             = pseries_eeh_wait_state,
        .get_log                = pseries_eeh_get_log,
        .configure_bridge       = pseries_eeh_configure_bridge,
+       .err_inject             = NULL,
        .read_config            = pseries_eeh_read_config,
        .write_config           = pseries_eeh_write_config,
        .next_error             = NULL,