GenWQE: Add support for EEH error recovery
authorKleber Sacilotto de Souza <klebers@linux.vnet.ibm.com>
Wed, 4 Jun 2014 13:57:51 +0000 (10:57 -0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 9 Jul 2014 21:14:27 +0000 (14:14 -0700)
This patch implements the callbacks and functions necessary to have EEH
recovery support.

It adds a config option to enable or disable explicit calls to trigger
platform specific mechanisms on error recovery paths. This option is
enabled by default only on PPC64 systems and can be overritten via
debugfs. If this option is enabled, on the error recovery path the
driver will call pci_channel_offline() to check for error condition and
issue non-raw MMIO reads to trigger early EEH detection in case of
hardware failures. This is necessary since the driver MMIO helper
funtions use raw accessors.

Signed-off-by: Kleber Sacilotto de Souza <klebers@linux.vnet.ibm.com>
Acked-by: Frank Haverkamp <haver@linux.vnet.ibm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/misc/genwqe/Kconfig
drivers/misc/genwqe/card_base.c
drivers/misc/genwqe/card_base.h
drivers/misc/genwqe/card_ddcb.c
drivers/misc/genwqe/card_debugfs.c
drivers/misc/genwqe/card_dev.c
drivers/misc/genwqe/card_utils.c

index 6069d8cd79d7671aded85c95aeb5279292a02a58..4c0a033cbfdbc1b06b38555838fc7d9b5e97fdfe 100644 (file)
@@ -11,3 +11,9 @@ menuconfig GENWQE
          Enables PCIe card driver for IBM GenWQE accelerators.
          The user-space interface is described in
          include/linux/genwqe/genwqe_card.h.
+
+config GENWQE_PLATFORM_ERROR_RECOVERY
+       int "Use platform recovery procedures (0=off, 1=on)"
+       depends on GENWQE
+       default 1 if PPC64
+       default 0
index e6cc3e1e73261c302b575b0eb5e3f0d569926b89..87ebaba9b133f1a32b055530e3f0ca5bd9074287 100644 (file)
@@ -140,6 +140,12 @@ static struct genwqe_dev *genwqe_dev_alloc(void)
        cd->class_genwqe = class_genwqe;
        cd->debugfs_genwqe = debugfs_genwqe;
 
+       /*
+        * This comes from kernel config option and can be overritten via
+        * debugfs.
+        */
+       cd->use_platform_recovery = CONFIG_GENWQE_PLATFORM_ERROR_RECOVERY;
+
        init_waitqueue_head(&cd->queue_waitq);
 
        spin_lock_init(&cd->file_lock);
@@ -943,6 +949,19 @@ static int genwqe_health_thread(void *data)
        return 0;
 
  fatal_error:
+       if (cd->use_platform_recovery) {
+               /*
+                * Since we use raw accessors, EEH errors won't be detected
+                * by the platform until we do a non-raw MMIO or config space
+                * read
+                */
+               readq(cd->mmio + IO_SLC_CFGREG_GFIR);
+
+               /* We do nothing if the card is going over PCI recovery */
+               if (pci_channel_offline(pci_dev))
+                       return -EIO;
+       }
+
        dev_err(&pci_dev->dev,
                "[%s] card unusable. Please trigger unbind!\n", __func__);
 
@@ -1048,6 +1067,9 @@ static int genwqe_pci_setup(struct genwqe_dev *cd)
        pci_set_master(pci_dev);
        pci_enable_pcie_error_reporting(pci_dev);
 
+       /* EEH recovery requires PCIe fundamental reset */
+       pci_dev->needs_freset = 1;
+
        /* request complete BAR-0 space (length = 0) */
        cd->mmio_len = pci_resource_len(pci_dev, 0);
        cd->mmio = pci_iomap(pci_dev, 0, 0);
@@ -1186,23 +1208,40 @@ static pci_ers_result_t genwqe_err_error_detected(struct pci_dev *pci_dev,
 
        dev_err(&pci_dev->dev, "[%s] state=%d\n", __func__, state);
 
-       if (pci_dev == NULL)
-               return PCI_ERS_RESULT_NEED_RESET;
-
        cd = dev_get_drvdata(&pci_dev->dev);
        if (cd == NULL)
-               return PCI_ERS_RESULT_NEED_RESET;
+               return PCI_ERS_RESULT_DISCONNECT;
 
-       switch (state) {
-       case pci_channel_io_normal:
-               return PCI_ERS_RESULT_CAN_RECOVER;
-       case pci_channel_io_frozen:
-               return PCI_ERS_RESULT_NEED_RESET;
-       case pci_channel_io_perm_failure:
+       /* Stop the card */
+       genwqe_health_check_stop(cd);
+       genwqe_stop(cd);
+
+       /*
+        * On permanent failure, the PCI code will call device remove
+        * after the return of this function.
+        * genwqe_stop() can be called twice.
+        */
+       if (state == pci_channel_io_perm_failure) {
                return PCI_ERS_RESULT_DISCONNECT;
+       } else {
+               genwqe_pci_remove(cd);
+               return PCI_ERS_RESULT_NEED_RESET;
        }
+}
+
+static pci_ers_result_t genwqe_err_slot_reset(struct pci_dev *pci_dev)
+{
+       int rc;
+       struct genwqe_dev *cd = dev_get_drvdata(&pci_dev->dev);
 
-       return PCI_ERS_RESULT_NEED_RESET;
+       rc = genwqe_pci_setup(cd);
+       if (!rc) {
+               return PCI_ERS_RESULT_RECOVERED;
+       } else {
+               dev_err(&pci_dev->dev,
+                       "err: problems with PCI setup (err=%d)\n", rc);
+               return PCI_ERS_RESULT_DISCONNECT;
+       }
 }
 
 static pci_ers_result_t genwqe_err_result_none(struct pci_dev *dev)
@@ -1210,8 +1249,22 @@ static pci_ers_result_t genwqe_err_result_none(struct pci_dev *dev)
        return PCI_ERS_RESULT_NONE;
 }
 
-static void genwqe_err_resume(struct pci_dev *dev)
+static void genwqe_err_resume(struct pci_dev *pci_dev)
 {
+       int rc;
+       struct genwqe_dev *cd = dev_get_drvdata(&pci_dev->dev);
+
+       rc = genwqe_start(cd);
+       if (!rc) {
+               rc = genwqe_health_check_start(cd);
+               if (rc)
+                       dev_err(&pci_dev->dev,
+                               "err: cannot start health checking! (err=%d)\n",
+                               rc);
+       } else {
+               dev_err(&pci_dev->dev,
+                       "err: cannot start card services! (err=%d)\n", rc);
+       }
 }
 
 static int genwqe_sriov_configure(struct pci_dev *dev, int numvfs)
@@ -1234,7 +1287,7 @@ static struct pci_error_handlers genwqe_err_handler = {
        .error_detected = genwqe_err_error_detected,
        .mmio_enabled   = genwqe_err_result_none,
        .link_reset     = genwqe_err_result_none,
-       .slot_reset     = genwqe_err_result_none,
+       .slot_reset     = genwqe_err_slot_reset,
        .resume         = genwqe_err_resume,
 };
 
index 0e608a288603033ae7a5f77764aa049eb4dab671..67abd8cb2247bba9c7c9dd459d57aae730dbdacf 100644 (file)
@@ -291,6 +291,8 @@ struct genwqe_dev {
        struct task_struct *health_thread;
        wait_queue_head_t health_waitq;
 
+       int use_platform_recovery;      /* use platform recovery mechanisms */
+
        /* char device */
        dev_t  devnum_genwqe;           /* major/minor num card */
        struct class *class_genwqe;     /* reference to class object */
index c8046db2d5a230114b7e3fb9a6145de87f0ec2cb..f0de6153bea22aaa1a3249f63b88d97eba95a2ba 100644 (file)
@@ -1118,7 +1118,21 @@ static irqreturn_t genwqe_pf_isr(int irq, void *dev_id)
         * safer, but slower for the good-case ... See above.
         */
        gfir = __genwqe_readq(cd, IO_SLC_CFGREG_GFIR);
-       if ((gfir & GFIR_ERR_TRIGGER) != 0x0) {
+       if (((gfir & GFIR_ERR_TRIGGER) != 0x0) &&
+           !pci_channel_offline(pci_dev)) {
+
+               if (cd->use_platform_recovery) {
+                       /*
+                        * Since we use raw accessors, EEH errors won't be
+                        * detected by the platform until we do a non-raw
+                        * MMIO or config space read
+                        */
+                       readq(cd->mmio + IO_SLC_CFGREG_GFIR);
+
+                       /* Don't do anything if the PCI channel is frozen */
+                       if (pci_channel_offline(pci_dev))
+                               goto exit;
+               }
 
                wake_up_interruptible(&cd->health_waitq);
 
@@ -1126,12 +1140,12 @@ static irqreturn_t genwqe_pf_isr(int irq, void *dev_id)
                 * By default GFIRs causes recovery actions. This
                 * count is just for debug when recovery is masked.
                 */
-               printk_ratelimited(KERN_ERR
-                                  "%s %s: [%s] GFIR=%016llx\n",
-                                  GENWQE_DEVNAME, dev_name(&pci_dev->dev),
-                                  __func__, gfir);
+               dev_err_ratelimited(&pci_dev->dev,
+                                   "[%s] GFIR=%016llx\n",
+                                   __func__, gfir);
        }
 
+ exit:
        return IRQ_HANDLED;
 }
 
index 0a33ade64109d4f147ba72f8abfedb50e35f0f2f..c9b4d6d0eb99a5ec47beda7336c5b9336025bb56 100644 (file)
@@ -485,6 +485,13 @@ int genwqe_init_debugfs(struct genwqe_dev *cd)
                goto err1;
        }
 
+       file = debugfs_create_u32("use_platform_recovery", 0666, root,
+                                 &cd->use_platform_recovery);
+       if (!file) {
+               ret = -ENOMEM;
+               goto err1;
+       }
+
        cd->debugfs_root = root;
        return 0;
 err1:
index 1d2f163a1906c2ca9740dbcc93e88970ea8ecbd0..aae42555e2ca3eba6f6fda2a0fdf6dd3405e721e 100644 (file)
@@ -1048,10 +1048,15 @@ static long genwqe_ioctl(struct file *filp, unsigned int cmd,
        int rc = 0;
        struct genwqe_file *cfile = (struct genwqe_file *)filp->private_data;
        struct genwqe_dev *cd = cfile->cd;
+       struct pci_dev *pci_dev = cd->pci_dev;
        struct genwqe_reg_io __user *io;
        u64 val;
        u32 reg_offs;
 
+       /* Return -EIO if card hit EEH */
+       if (pci_channel_offline(pci_dev))
+               return -EIO;
+
        if (_IOC_TYPE(cmd) != GENWQE_IOC_CODE)
                return -EINVAL;
 
index 62cc6bb3f62e27bc30e03636886acf371d0e5b06..4a500582eef09229532ebbee90cef80efd36538a 100644 (file)
  */
 int __genwqe_writeq(struct genwqe_dev *cd, u64 byte_offs, u64 val)
 {
+       struct pci_dev *pci_dev = cd->pci_dev;
+
        if (cd->err_inject & GENWQE_INJECT_HARDWARE_FAILURE)
                return -EIO;
 
        if (cd->mmio == NULL)
                return -EIO;
 
+       if (pci_channel_offline(pci_dev))
+               return -EIO;
+
        __raw_writeq((__force u64)cpu_to_be64(val), cd->mmio + byte_offs);
        return 0;
 }
@@ -99,12 +104,17 @@ u64 __genwqe_readq(struct genwqe_dev *cd, u64 byte_offs)
  */
 int __genwqe_writel(struct genwqe_dev *cd, u64 byte_offs, u32 val)
 {
+       struct pci_dev *pci_dev = cd->pci_dev;
+
        if (cd->err_inject & GENWQE_INJECT_HARDWARE_FAILURE)
                return -EIO;
 
        if (cd->mmio == NULL)
                return -EIO;
 
+       if (pci_channel_offline(pci_dev))
+               return -EIO;
+
        __raw_writel((__force u32)cpu_to_be32(val), cd->mmio + byte_offs);
        return 0;
 }