Skip to content

Commit

Permalink
qed: Fix error flow on slowpath start
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Yuval Mintz authored and David S. Miller committed Mar 2, 2016
1 parent 86622ee commit 8c925c4
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 44 deletions.
89 changes: 49 additions & 40 deletions drivers/net/ethernet/qlogic/qed/qed_dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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];
Expand All @@ -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 */
Expand Down
9 changes: 9 additions & 0 deletions drivers/net/ethernet/qlogic/qed/qed_dev_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 -
*
Expand Down
10 changes: 6 additions & 4 deletions drivers/net/ethernet/qlogic/qed/qed_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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);
Expand Down

0 comments on commit 8c925c4

Please sign in to comment.