[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
					
				| @ -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 New Issue
	
	Block a user