Commit 7b594769 authored by Bhanu Prakash Gollapudi's avatar Bhanu Prakash Gollapudi Committed by James Bottomley

[SCSI] bnx2fc: Handle REC_TOV error code from firmware

Driver decides to initiate REC on REC_TOV timer pop. The firmware maintains the
REC timer and informs the driver as a firmware error message, which is an
unsolicited event to the driver. Driver also issues REC on other unsolicited
events from firmware that indicate data loss.
Signed-off-by: default avatarBhanu Prakash Gollapudi <bprakash@broadcom.com>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent 74446954
...@@ -143,6 +143,7 @@ ...@@ -143,6 +143,7 @@
#define SRR_RETRY_COUNT 5 #define SRR_RETRY_COUNT 5
#define REC_RETRY_COUNT 1 #define REC_RETRY_COUNT 1
#define BNX2FC_NUM_ERR_BITS 63
/* bnx2fc driver uses only one instance of fcoe_percpu_s */ /* bnx2fc driver uses only one instance of fcoe_percpu_s */
extern struct fcoe_percpu_s bnx2fc_global; extern struct fcoe_percpu_s bnx2fc_global;
......
...@@ -629,6 +629,8 @@ static void bnx2fc_process_unsol_compl(struct bnx2fc_rport *tgt, u16 wqe) ...@@ -629,6 +629,8 @@ static void bnx2fc_process_unsol_compl(struct bnx2fc_rport *tgt, u16 wqe)
struct bnx2fc_hba *hba = interface->hba; struct bnx2fc_hba *hba = interface->hba;
int task_idx, index; int task_idx, index;
int rc = 0; int rc = 0;
u64 err_warn_bit_map;
u8 err_warn = 0xff;
BNX2FC_TGT_DBG(tgt, "Entered UNSOL COMPLETION wqe = 0x%x\n", wqe); BNX2FC_TGT_DBG(tgt, "Entered UNSOL COMPLETION wqe = 0x%x\n", wqe);
...@@ -691,13 +693,11 @@ static void bnx2fc_process_unsol_compl(struct bnx2fc_rport *tgt, u16 wqe) ...@@ -691,13 +693,11 @@ static void bnx2fc_process_unsol_compl(struct bnx2fc_rport *tgt, u16 wqe)
BNX2FC_TGT_DBG(tgt, "buf_offsets - tx = 0x%x, rx = 0x%x\n", BNX2FC_TGT_DBG(tgt, "buf_offsets - tx = 0x%x, rx = 0x%x\n",
err_entry->data.tx_buf_off, err_entry->data.rx_buf_off); err_entry->data.tx_buf_off, err_entry->data.rx_buf_off);
bnx2fc_return_rqe(tgt, 1);
if (xid > BNX2FC_MAX_XID) { if (xid > BNX2FC_MAX_XID) {
BNX2FC_TGT_DBG(tgt, "xid(0x%x) out of FW range\n", BNX2FC_TGT_DBG(tgt, "xid(0x%x) out of FW range\n",
xid); xid);
spin_unlock_bh(&tgt->tgt_lock); goto ret_err_rqe;
break;
} }
task_idx = xid / BNX2FC_TASKS_PER_PAGE; task_idx = xid / BNX2FC_TASKS_PER_PAGE;
...@@ -707,24 +707,30 @@ static void bnx2fc_process_unsol_compl(struct bnx2fc_rport *tgt, u16 wqe) ...@@ -707,24 +707,30 @@ static void bnx2fc_process_unsol_compl(struct bnx2fc_rport *tgt, u16 wqe)
task = &(task_page[index]); task = &(task_page[index]);
io_req = (struct bnx2fc_cmd *)hba->cmd_mgr->cmds[xid]; io_req = (struct bnx2fc_cmd *)hba->cmd_mgr->cmds[xid];
if (!io_req) { if (!io_req)
spin_unlock_bh(&tgt->tgt_lock); goto ret_err_rqe;
break;
}
if (io_req->cmd_type != BNX2FC_SCSI_CMD) { if (io_req->cmd_type != BNX2FC_SCSI_CMD) {
printk(KERN_ERR PFX "err_warn: Not a SCSI cmd\n"); printk(KERN_ERR PFX "err_warn: Not a SCSI cmd\n");
spin_unlock_bh(&tgt->tgt_lock); goto ret_err_rqe;
break;
} }
if (test_and_clear_bit(BNX2FC_FLAG_IO_CLEANUP, if (test_and_clear_bit(BNX2FC_FLAG_IO_CLEANUP,
&io_req->req_flags)) { &io_req->req_flags)) {
BNX2FC_IO_DBG(io_req, "unsol_err: cleanup in " BNX2FC_IO_DBG(io_req, "unsol_err: cleanup in "
"progress.. ignore unsol err\n"); "progress.. ignore unsol err\n");
spin_unlock_bh(&tgt->tgt_lock); goto ret_err_rqe;
}
err_warn_bit_map = (u64)
((u64)err_entry->data.err_warn_bitmap_hi << 32) |
(u64)err_entry->data.err_warn_bitmap_lo;
for (i = 0; i < BNX2FC_NUM_ERR_BITS; i++) {
if (err_warn_bit_map & (u64)((u64)1 << i)) {
err_warn = i;
break; break;
} }
}
/* /*
* If ABTS is already in progress, and FW error is * If ABTS is already in progress, and FW error is
...@@ -733,26 +739,61 @@ static void bnx2fc_process_unsol_compl(struct bnx2fc_rport *tgt, u16 wqe) ...@@ -733,26 +739,61 @@ static void bnx2fc_process_unsol_compl(struct bnx2fc_rport *tgt, u16 wqe)
* logging out the target, when the ABTS eventually * logging out the target, when the ABTS eventually
* times out. * times out.
*/ */
if (!test_and_set_bit(BNX2FC_FLAG_ISSUE_ABTS, if (test_bit(BNX2FC_FLAG_ISSUE_ABTS, &io_req->req_flags)) {
printk(KERN_ERR PFX "err_warn: io_req (0x%x) already "
"in ABTS processing\n", xid);
goto ret_err_rqe;
}
BNX2FC_TGT_DBG(tgt, "err = 0x%x\n", err_warn);
if (tgt->dev_type != TYPE_TAPE)
goto skip_rec;
switch (err_warn) {
case FCOE_ERROR_CODE_REC_TOV_TIMER_EXPIRATION:
case FCOE_ERROR_CODE_DATA_OOO_RO:
case FCOE_ERROR_CODE_COMMON_INCORRECT_SEQ_CNT:
case FCOE_ERROR_CODE_DATA_SOFI3_SEQ_ACTIVE_SET:
case FCOE_ERROR_CODE_FCP_RSP_OPENED_SEQ:
case FCOE_ERROR_CODE_DATA_SOFN_SEQ_ACTIVE_RESET:
BNX2FC_TGT_DBG(tgt, "REC TOV popped for xid - 0x%x\n",
xid);
memset(&io_req->err_entry, 0,
sizeof(struct fcoe_err_report_entry));
memcpy(&io_req->err_entry, err_entry,
sizeof(struct fcoe_err_report_entry));
if (!test_bit(BNX2FC_FLAG_SRR_SENT,
&io_req->req_flags)) { &io_req->req_flags)) {
spin_unlock_bh(&tgt->tgt_lock);
rc = bnx2fc_send_rec(io_req);
spin_lock_bh(&tgt->tgt_lock);
if (rc)
goto skip_rec;
} else
printk(KERN_ERR PFX "SRR in progress\n");
goto ret_err_rqe;
break;
default:
break;
}
skip_rec:
set_bit(BNX2FC_FLAG_ISSUE_ABTS, &io_req->req_flags);
/* /*
* Cancel the timeout_work, as we received IO * Cancel the timeout_work, as we received IO
* completion with FW error. * completion with FW error.
*/ */
if (cancel_delayed_work(&io_req->timeout_work)) if (cancel_delayed_work(&io_req->timeout_work))
kref_put(&io_req->refcount, kref_put(&io_req->refcount, bnx2fc_cmd_release);
bnx2fc_cmd_release); /* timer hold */
rc = bnx2fc_initiate_abts(io_req); rc = bnx2fc_initiate_abts(io_req);
if (rc != SUCCESS) { if (rc != SUCCESS) {
BNX2FC_IO_DBG(io_req, "err_warn: initiate_abts " printk(KERN_ERR PFX "err_warn: initiate_abts "
"failed. issue cleanup\n"); "failed xid = 0x%x. issue cleanup\n",
rc = bnx2fc_initiate_cleanup(io_req); io_req->xid);
BUG_ON(rc); bnx2fc_initiate_cleanup(io_req);
} }
} else ret_err_rqe:
printk(KERN_ERR PFX "err_warn: io_req (0x%x) already " bnx2fc_return_rqe(tgt, 1);
"in ABTS processing\n", xid);
spin_unlock_bh(&tgt->tgt_lock); spin_unlock_bh(&tgt->tgt_lock);
break; break;
...@@ -773,6 +814,47 @@ static void bnx2fc_process_unsol_compl(struct bnx2fc_rport *tgt, u16 wqe) ...@@ -773,6 +814,47 @@ static void bnx2fc_process_unsol_compl(struct bnx2fc_rport *tgt, u16 wqe)
BNX2FC_TGT_DBG(tgt, "buf_offsets - tx = 0x%x, rx = 0x%x", BNX2FC_TGT_DBG(tgt, "buf_offsets - tx = 0x%x, rx = 0x%x",
err_entry->data.tx_buf_off, err_entry->data.rx_buf_off); err_entry->data.tx_buf_off, err_entry->data.rx_buf_off);
if (xid > BNX2FC_MAX_XID) {
BNX2FC_TGT_DBG(tgt, "xid(0x%x) out of FW range\n", xid);
goto ret_warn_rqe;
}
err_warn_bit_map = (u64)
((u64)err_entry->data.err_warn_bitmap_hi << 32) |
(u64)err_entry->data.err_warn_bitmap_lo;
for (i = 0; i < BNX2FC_NUM_ERR_BITS; i++) {
if (err_warn_bit_map & (u64) (1 << i)) {
err_warn = i;
break;
}
}
BNX2FC_TGT_DBG(tgt, "warn = 0x%x\n", err_warn);
task_idx = xid / BNX2FC_TASKS_PER_PAGE;
index = xid % BNX2FC_TASKS_PER_PAGE;
task_page = (struct fcoe_task_ctx_entry *)
interface->hba->task_ctx[task_idx];
task = &(task_page[index]);
io_req = (struct bnx2fc_cmd *)hba->cmd_mgr->cmds[xid];
if (!io_req)
goto ret_warn_rqe;
if (io_req->cmd_type != BNX2FC_SCSI_CMD) {
printk(KERN_ERR PFX "err_warn: Not a SCSI cmd\n");
goto ret_warn_rqe;
}
memset(&io_req->err_entry, 0,
sizeof(struct fcoe_err_report_entry));
memcpy(&io_req->err_entry, err_entry,
sizeof(struct fcoe_err_report_entry));
if (err_warn == FCOE_ERROR_CODE_REC_TOV_TIMER_EXPIRATION)
/* REC_TOV is not a warning code */
BUG_ON(1);
else
BNX2FC_TGT_DBG(tgt, "Unsolicited warning\n");
ret_warn_rqe:
bnx2fc_return_rqe(tgt, 1); bnx2fc_return_rqe(tgt, 1);
spin_unlock_bh(&tgt->tgt_lock); spin_unlock_bh(&tgt->tgt_lock);
break; break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment