forked from mirrors/linux
		
	[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: Bhanu Prakash Gollapudi <bprakash@broadcom.com> Signed-off-by: James Bottomley <JBottomley@Parallels.com>
This commit is contained in:
		
							parent
							
								
									7444695429
								
							
						
					
					
						commit
						7b59476912
					
				
					 2 changed files with 112 additions and 29 deletions
				
			
		| 
						 | 
					@ -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)
 | 
				
			||||||
	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)
 | 
				
			||||||
		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,23 +707,29 @@ 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;
 | 
				
			||||||
			break;
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							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;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
| 
						 | 
					@ -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)) {
 | 
				
			||||||
				      &io_req->req_flags)) {
 | 
					 | 
				
			||||||
			/*
 | 
					 | 
				
			||||||
			 * Cancel the timeout_work, as we received IO
 | 
					 | 
				
			||||||
			 * completion with FW error.
 | 
					 | 
				
			||||||
			 */
 | 
					 | 
				
			||||||
			if (cancel_delayed_work(&io_req->timeout_work))
 | 
					 | 
				
			||||||
				kref_put(&io_req->refcount,
 | 
					 | 
				
			||||||
					 bnx2fc_cmd_release); /* timer hold */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			rc = bnx2fc_initiate_abts(io_req);
 | 
					 | 
				
			||||||
			if (rc != SUCCESS) {
 | 
					 | 
				
			||||||
				BNX2FC_IO_DBG(io_req, "err_warn: initiate_abts "
 | 
					 | 
				
			||||||
					"failed. issue cleanup\n");
 | 
					 | 
				
			||||||
				rc = bnx2fc_initiate_cleanup(io_req);
 | 
					 | 
				
			||||||
				BUG_ON(rc);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		} else
 | 
					 | 
				
			||||||
			printk(KERN_ERR PFX "err_warn: io_req (0x%x) already "
 | 
								printk(KERN_ERR PFX "err_warn: io_req (0x%x) already "
 | 
				
			||||||
					    "in ABTS processing\n", xid);
 | 
										    "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)) {
 | 
				
			||||||
 | 
									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
 | 
				
			||||||
 | 
							 * completion with FW error.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							if (cancel_delayed_work(&io_req->timeout_work))
 | 
				
			||||||
 | 
								kref_put(&io_req->refcount, bnx2fc_cmd_release);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							rc = bnx2fc_initiate_abts(io_req);
 | 
				
			||||||
 | 
							if (rc != SUCCESS) {
 | 
				
			||||||
 | 
								printk(KERN_ERR PFX "err_warn: initiate_abts "
 | 
				
			||||||
 | 
									"failed xid = 0x%x. issue cleanup\n",
 | 
				
			||||||
 | 
									io_req->xid);
 | 
				
			||||||
 | 
								bnx2fc_initiate_cleanup(io_req);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					ret_err_rqe:
 | 
				
			||||||
 | 
							bnx2fc_return_rqe(tgt, 1);
 | 
				
			||||||
		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)
 | 
				
			||||||
		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;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue