ice: Implement pci_error_handler ops
authorBrett Creeley <brett.creeley@intel.com>
Wed, 13 Feb 2019 18:51:15 +0000 (10:51 -0800)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Tue, 26 Mar 2019 20:57:12 +0000 (13:57 -0700)
This patch implements the following pci_error_handler ops:
.error_detected = ice_pci_err_detected
.slot_reset = ice_pci_err_slot_reset
.reset_notify = ice_pci_err_reset_notify
.reset_prepare = ice_pci_err_reset_prepare
.reset_done = ice_pci_err_reset_done
.resume = ice_pci_err_resume

Signed-off-by: Brett Creeley <brett.creeley@intel.com>
Signed-off-by: Anirudh Venkataramanan <anirudh.venkataramanan@intel.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
drivers/net/ethernet/intel/ice/ice_main.c

index c4626ae7a3b960f881dc597cb5dd8a8e48e33340..54b1db4db7d4b2f47db5e8a56340dbabece4916c 100644 (file)
@@ -1004,6 +1004,18 @@ static void ice_service_task_stop(struct ice_pf *pf)
        clear_bit(__ICE_SERVICE_SCHED, pf->state);
 }
 
+/**
+ * ice_service_task_restart - restart service task and schedule works
+ * @pf: board private structure
+ *
+ * This function is needed for suspend and resume works (e.g WoL scenario)
+ */
+static void ice_service_task_restart(struct ice_pf *pf)
+{
+       clear_bit(__ICE_SERVICE_DIS, pf->state);
+       ice_service_task_schedule(pf);
+}
+
 /**
  * ice_service_timer - timer callback to schedule service task
  * @t: pointer to timer_list
@@ -2395,6 +2407,136 @@ static void ice_remove(struct pci_dev *pdev)
        pci_disable_pcie_error_reporting(pdev);
 }
 
+/**
+ * ice_pci_err_detected - warning that PCI error has been detected
+ * @pdev: PCI device information struct
+ * @err: the type of PCI error
+ *
+ * Called to warn that something happened on the PCI bus and the error handling
+ * is in progress.  Allows the driver to gracefully prepare/handle PCI errors.
+ */
+static pci_ers_result_t
+ice_pci_err_detected(struct pci_dev *pdev, enum pci_channel_state err)
+{
+       struct ice_pf *pf = pci_get_drvdata(pdev);
+
+       if (!pf) {
+               dev_err(&pdev->dev, "%s: unrecoverable device error %d\n",
+                       __func__, err);
+               return PCI_ERS_RESULT_DISCONNECT;
+       }
+
+       if (!test_bit(__ICE_SUSPENDED, pf->state)) {
+               ice_service_task_stop(pf);
+
+               if (!test_bit(__ICE_PREPARED_FOR_RESET, pf->state)) {
+                       set_bit(__ICE_PFR_REQ, pf->state);
+                       ice_prepare_for_reset(pf);
+               }
+       }
+
+       return PCI_ERS_RESULT_NEED_RESET;
+}
+
+/**
+ * ice_pci_err_slot_reset - a PCI slot reset has just happened
+ * @pdev: PCI device information struct
+ *
+ * Called to determine if the driver can recover from the PCI slot reset by
+ * using a register read to determine if the device is recoverable.
+ */
+static pci_ers_result_t ice_pci_err_slot_reset(struct pci_dev *pdev)
+{
+       struct ice_pf *pf = pci_get_drvdata(pdev);
+       pci_ers_result_t result;
+       int err;
+       u32 reg;
+
+       err = pci_enable_device_mem(pdev);
+       if (err) {
+               dev_err(&pdev->dev,
+                       "Cannot re-enable PCI device after reset, error %d\n",
+                       err);
+               result = PCI_ERS_RESULT_DISCONNECT;
+       } else {
+               pci_set_master(pdev);
+               pci_restore_state(pdev);
+               pci_save_state(pdev);
+               pci_wake_from_d3(pdev, false);
+
+               /* Check for life */
+               reg = rd32(&pf->hw, GLGEN_RTRIG);
+               if (!reg)
+                       result = PCI_ERS_RESULT_RECOVERED;
+               else
+                       result = PCI_ERS_RESULT_DISCONNECT;
+       }
+
+       err = pci_cleanup_aer_uncorrect_error_status(pdev);
+       if (err)
+               dev_dbg(&pdev->dev,
+                       "pci_cleanup_aer_uncorrect_error_status failed, error %d\n",
+                       err);
+               /* non-fatal, continue */
+
+       return result;
+}
+
+/**
+ * ice_pci_err_resume - restart operations after PCI error recovery
+ * @pdev: PCI device information struct
+ *
+ * Called to allow the driver to bring things back up after PCI error and/or
+ * reset recovery have finished
+ */
+static void ice_pci_err_resume(struct pci_dev *pdev)
+{
+       struct ice_pf *pf = pci_get_drvdata(pdev);
+
+       if (!pf) {
+               dev_err(&pdev->dev,
+                       "%s failed, device is unrecoverable\n", __func__);
+               return;
+       }
+
+       if (test_bit(__ICE_SUSPENDED, pf->state)) {
+               dev_dbg(&pdev->dev, "%s failed to resume normal operations!\n",
+                       __func__);
+               return;
+       }
+
+       ice_do_reset(pf, ICE_RESET_PFR);
+       ice_service_task_restart(pf);
+       mod_timer(&pf->serv_tmr, round_jiffies(jiffies + pf->serv_tmr_period));
+}
+
+/**
+ * ice_pci_err_reset_prepare - prepare device driver for PCI reset
+ * @pdev: PCI device information struct
+ */
+static void ice_pci_err_reset_prepare(struct pci_dev *pdev)
+{
+       struct ice_pf *pf = pci_get_drvdata(pdev);
+
+       if (!test_bit(__ICE_SUSPENDED, pf->state)) {
+               ice_service_task_stop(pf);
+
+               if (!test_bit(__ICE_PREPARED_FOR_RESET, pf->state)) {
+                       set_bit(__ICE_PFR_REQ, pf->state);
+                       ice_prepare_for_reset(pf);
+               }
+       }
+}
+
+/**
+ * ice_pci_err_reset_done - PCI reset done, device driver reset can begin
+ * @pdev: PCI device information struct
+ */
+static void ice_pci_err_reset_done(struct pci_dev *pdev)
+{
+       ice_pci_err_resume(pdev);
+}
+
 /* ice_pci_tbl - PCI Device ID Table
  *
  * Wildcard entries (PCI_ANY_ID) should come last
@@ -2412,12 +2554,21 @@ static const struct pci_device_id ice_pci_tbl[] = {
 };
 MODULE_DEVICE_TABLE(pci, ice_pci_tbl);
 
+static const struct pci_error_handlers ice_pci_err_handler = {
+       .error_detected = ice_pci_err_detected,
+       .slot_reset = ice_pci_err_slot_reset,
+       .reset_prepare = ice_pci_err_reset_prepare,
+       .reset_done = ice_pci_err_reset_done,
+       .resume = ice_pci_err_resume
+};
+
 static struct pci_driver ice_driver = {
        .name = KBUILD_MODNAME,
        .id_table = ice_pci_tbl,
        .probe = ice_probe,
        .remove = ice_remove,
        .sriov_configure = ice_sriov_configure,
+       .err_handler = &ice_pci_err_handler
 };
 
 /**