qed: Fix error flow on slowpath start
authorYuval Mintz <Yuval.Mintz@qlogic.com>
Wed, 2 Mar 2016 18:26:03 +0000 (20:26 +0200)
committerDavid S. Miller <davem@davemloft.net>
Wed, 2 Mar 2016 19:04:18 +0000 (14:04 -0500)
In case of problems when initializing the chip, the error flows aren't
being properly done. Specifically, it's possible that the chip would be
left in a configuration allowing it [internally] to access the host
memory, causing fatal problems in the device that would require power
cycle to overcome.

Signed-off-by: Yuval Mintz <Yuval.Mintz@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/qlogic/qed/qed_dev.c
drivers/net/ethernet/qlogic/qed/qed_dev_api.h
drivers/net/ethernet/qlogic/qed/qed_main.c

index d08078fd7f82910bb9690432f8b6be8350adc449..c3f293d179918268ae9c261ba35a95e17192ce77 100644 (file)
@@ -756,10 +756,54 @@ int qed_hw_init(struct qed_dev *cdev,
 }
 
 #define QED_HW_STOP_RETRY_LIMIT (10)
+static inline void qed_hw_timers_stop(struct qed_dev *cdev,
+                                     struct qed_hwfn *p_hwfn,
+                                     struct qed_ptt *p_ptt)
+{
+       int i;
+
+       /* close timers */
+       qed_wr(p_hwfn, p_ptt, TM_REG_PF_ENABLE_CONN, 0x0);
+       qed_wr(p_hwfn, p_ptt, TM_REG_PF_ENABLE_TASK, 0x0);
+
+       for (i = 0; i < QED_HW_STOP_RETRY_LIMIT; i++) {
+               if ((!qed_rd(p_hwfn, p_ptt,
+                            TM_REG_PF_SCAN_ACTIVE_CONN)) &&
+                   (!qed_rd(p_hwfn, p_ptt,
+                            TM_REG_PF_SCAN_ACTIVE_TASK)))
+                       break;
+
+               /* Dependent on number of connection/tasks, possibly
+                * 1ms sleep is required between polls
+                */
+               usleep_range(1000, 2000);
+       }
+
+       if (i < QED_HW_STOP_RETRY_LIMIT)
+               return;
+
+       DP_NOTICE(p_hwfn,
+                 "Timers linear scans are not over [Connection %02x Tasks %02x]\n",
+                 (u8)qed_rd(p_hwfn, p_ptt, TM_REG_PF_SCAN_ACTIVE_CONN),
+                 (u8)qed_rd(p_hwfn, p_ptt, TM_REG_PF_SCAN_ACTIVE_TASK));
+}
+
+void qed_hw_timers_stop_all(struct qed_dev *cdev)
+{
+       int j;
+
+       for_each_hwfn(cdev, j) {
+               struct qed_hwfn *p_hwfn = &cdev->hwfns[j];
+               struct qed_ptt *p_ptt = p_hwfn->p_main_ptt;
+
+               qed_hw_timers_stop(cdev, p_hwfn, p_ptt);
+       }
+}
+
 int qed_hw_stop(struct qed_dev *cdev)
 {
        int rc = 0, t_rc;
-       int i, j;
+       int j;
 
        for_each_hwfn(cdev, j) {
                struct qed_hwfn *p_hwfn = &cdev->hwfns[j];
@@ -772,7 +816,8 @@ int qed_hw_stop(struct qed_dev *cdev)
 
                rc = qed_sp_pf_stop(p_hwfn);
                if (rc)
-                       return rc;
+                       DP_NOTICE(p_hwfn,
+                                 "Failed to close PF against FW. Continue to stop HW to prevent illegal host access by the device\n");
 
                qed_wr(p_hwfn, p_ptt,
                       NIG_REG_RX_LLH_BRB_GATE_DNTFWD_PERPF, 0x1);
@@ -783,24 +828,7 @@ int qed_hw_stop(struct qed_dev *cdev)
                qed_wr(p_hwfn, p_ptt, PRS_REG_SEARCH_ROCE, 0x0);
                qed_wr(p_hwfn, p_ptt, PRS_REG_SEARCH_OPENFLOW, 0x0);
 
-               qed_wr(p_hwfn, p_ptt, TM_REG_PF_ENABLE_CONN, 0x0);
-               qed_wr(p_hwfn, p_ptt, TM_REG_PF_ENABLE_TASK, 0x0);
-               for (i = 0; i < QED_HW_STOP_RETRY_LIMIT; i++) {
-                       if ((!qed_rd(p_hwfn, p_ptt,
-                                    TM_REG_PF_SCAN_ACTIVE_CONN)) &&
-                           (!qed_rd(p_hwfn, p_ptt,
-                                    TM_REG_PF_SCAN_ACTIVE_TASK)))
-                               break;
-
-                       usleep_range(1000, 2000);
-               }
-               if (i == QED_HW_STOP_RETRY_LIMIT)
-                       DP_NOTICE(p_hwfn,
-                                 "Timers linear scans are not over [Connection %02x Tasks %02x]\n",
-                                 (u8)qed_rd(p_hwfn, p_ptt,
-                                            TM_REG_PF_SCAN_ACTIVE_CONN),
-                                 (u8)qed_rd(p_hwfn, p_ptt,
-                                            TM_REG_PF_SCAN_ACTIVE_TASK));
+               qed_hw_timers_stop(cdev, p_hwfn, p_ptt);
 
                /* Disable Attention Generation */
                qed_int_igu_disable_int(p_hwfn, p_ptt);
@@ -829,7 +857,7 @@ int qed_hw_stop(struct qed_dev *cdev)
 
 void qed_hw_stop_fastpath(struct qed_dev *cdev)
 {
-       int i, j;
+       int j;
 
        for_each_hwfn(cdev, j) {
                struct qed_hwfn *p_hwfn = &cdev->hwfns[j];
@@ -848,25 +876,6 @@ void qed_hw_stop_fastpath(struct qed_dev *cdev)
                qed_wr(p_hwfn, p_ptt, PRS_REG_SEARCH_ROCE, 0x0);
                qed_wr(p_hwfn, p_ptt, PRS_REG_SEARCH_OPENFLOW, 0x0);
 
-               qed_wr(p_hwfn, p_ptt, TM_REG_PF_ENABLE_CONN, 0x0);
-               qed_wr(p_hwfn, p_ptt, TM_REG_PF_ENABLE_TASK, 0x0);
-               for (i = 0; i < QED_HW_STOP_RETRY_LIMIT; i++) {
-                       if ((!qed_rd(p_hwfn, p_ptt,
-                                    TM_REG_PF_SCAN_ACTIVE_CONN)) &&
-                           (!qed_rd(p_hwfn, p_ptt,
-                                    TM_REG_PF_SCAN_ACTIVE_TASK)))
-                               break;
-
-                       usleep_range(1000, 2000);
-               }
-               if (i == QED_HW_STOP_RETRY_LIMIT)
-                       DP_NOTICE(p_hwfn,
-                                 "Timers linear scans are not over [Connection %02x Tasks %02x]\n",
-                                 (u8)qed_rd(p_hwfn, p_ptt,
-                                            TM_REG_PF_SCAN_ACTIVE_CONN),
-                                 (u8)qed_rd(p_hwfn, p_ptt,
-                                            TM_REG_PF_SCAN_ACTIVE_TASK));
-
                qed_int_igu_init_pure_rt(p_hwfn, p_ptt, false, false);
 
                /* Need to wait 1ms to guarantee SBs are cleared */
index 155f26b938fcaab6629606f635e4d3a7881db5ae..d6c7ddf4f4d4b54bd23563ba43b125bd9cd64af7 100644 (file)
@@ -77,6 +77,15 @@ int qed_hw_init(struct qed_dev *cdev,
                bool allow_npar_tx_switch,
                const u8 *bin_fw_data);
 
+/**
+ * @brief qed_hw_timers_stop_all - stop the timers HW block
+ *
+ * @param cdev
+ *
+ * @return void
+ */
+void qed_hw_timers_stop_all(struct qed_dev *cdev);
+
 /**
  * @brief qed_hw_stop -
  *
index caa689e6575cff9d148354f21d2325c78f9f5b59..26d40db07ddd12a13ae07f7494e3244e03f70af4 100644 (file)
@@ -779,7 +779,7 @@ static int qed_slowpath_start(struct qed_dev *cdev,
        rc = qed_hw_init(cdev, true, cdev->int_params.out.int_mode,
                         true, data);
        if (rc)
-               goto err3;
+               goto err2;
 
        DP_INFO(cdev,
                "HW initialization and function start completed successfully\n");
@@ -798,12 +798,14 @@ static int qed_slowpath_start(struct qed_dev *cdev,
                return rc;
        }
 
+       qed_reset_vport_stats(cdev);
+
        return 0;
 
-err3:
-       qed_free_stream_mem(cdev);
-       qed_slowpath_irq_free(cdev);
 err2:
+       qed_hw_timers_stop_all(cdev);
+       qed_slowpath_irq_free(cdev);
+       qed_free_stream_mem(cdev);
        qed_disable_msix(cdev);
 err1:
        qed_resc_free(cdev);