diff options
author | Bhanu Prakash Gollapudi <bprakash@broadcom.com> | 2011-07-27 14:32:07 -0400 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2011-07-28 03:47:12 -0400 |
commit | 7b594769120b43b8da1ff8f7b4c31a47fabd6ac0 (patch) | |
tree | 792d229f0e4a029cdba8c0eeddb2997ead7e9d45 /drivers/scsi | |
parent | 744469542951d32979a8dcb1dbed560bfed1745e (diff) |
[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>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/bnx2fc/bnx2fc.h | 1 | ||||
-rw-r--r-- | drivers/scsi/bnx2fc/bnx2fc_hwi.c | 140 |
2 files changed, 112 insertions, 29 deletions
diff --git a/drivers/scsi/bnx2fc/bnx2fc.h b/drivers/scsi/bnx2fc/bnx2fc.h index cd506c0ee2f6..b1b0b3e89a84 100644 --- a/drivers/scsi/bnx2fc/bnx2fc.h +++ b/drivers/scsi/bnx2fc/bnx2fc.h | |||
@@ -143,6 +143,7 @@ | |||
143 | 143 | ||
144 | #define SRR_RETRY_COUNT 5 | 144 | #define SRR_RETRY_COUNT 5 |
145 | #define REC_RETRY_COUNT 1 | 145 | #define REC_RETRY_COUNT 1 |
146 | #define BNX2FC_NUM_ERR_BITS 63 | ||
146 | 147 | ||
147 | /* bnx2fc driver uses only one instance of fcoe_percpu_s */ | 148 | /* bnx2fc driver uses only one instance of fcoe_percpu_s */ |
148 | extern struct fcoe_percpu_s bnx2fc_global; | 149 | extern struct fcoe_percpu_s bnx2fc_global; |
diff --git a/drivers/scsi/bnx2fc/bnx2fc_hwi.c b/drivers/scsi/bnx2fc/bnx2fc_hwi.c index 03ae003d3b85..764c45254dc6 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_hwi.c +++ b/drivers/scsi/bnx2fc/bnx2fc_hwi.c | |||
@@ -629,6 +629,8 @@ static void bnx2fc_process_unsol_compl(struct bnx2fc_rport *tgt, u16 wqe) | |||
629 | struct bnx2fc_hba *hba = interface->hba; | 629 | struct bnx2fc_hba *hba = interface->hba; |
630 | int task_idx, index; | 630 | int task_idx, index; |
631 | int rc = 0; | 631 | int rc = 0; |
632 | u64 err_warn_bit_map; | ||
633 | u8 err_warn = 0xff; | ||
632 | 634 | ||
633 | 635 | ||
634 | BNX2FC_TGT_DBG(tgt, "Entered UNSOL COMPLETION wqe = 0x%x\n", wqe); | 636 | 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 | BNX2FC_TGT_DBG(tgt, "buf_offsets - tx = 0x%x, rx = 0x%x\n", | 693 | BNX2FC_TGT_DBG(tgt, "buf_offsets - tx = 0x%x, rx = 0x%x\n", |
692 | err_entry->data.tx_buf_off, err_entry->data.rx_buf_off); | 694 | err_entry->data.tx_buf_off, err_entry->data.rx_buf_off); |
693 | 695 | ||
694 | bnx2fc_return_rqe(tgt, 1); | ||
695 | 696 | ||
696 | if (xid > BNX2FC_MAX_XID) { | 697 | if (xid > BNX2FC_MAX_XID) { |
697 | BNX2FC_TGT_DBG(tgt, "xid(0x%x) out of FW range\n", | 698 | BNX2FC_TGT_DBG(tgt, "xid(0x%x) out of FW range\n", |
698 | xid); | 699 | xid); |
699 | spin_unlock_bh(&tgt->tgt_lock); | 700 | goto ret_err_rqe; |
700 | break; | ||
701 | } | 701 | } |
702 | 702 | ||
703 | task_idx = xid / BNX2FC_TASKS_PER_PAGE; | 703 | task_idx = xid / BNX2FC_TASKS_PER_PAGE; |
@@ -707,23 +707,29 @@ static void bnx2fc_process_unsol_compl(struct bnx2fc_rport *tgt, u16 wqe) | |||
707 | task = &(task_page[index]); | 707 | task = &(task_page[index]); |
708 | 708 | ||
709 | io_req = (struct bnx2fc_cmd *)hba->cmd_mgr->cmds[xid]; | 709 | io_req = (struct bnx2fc_cmd *)hba->cmd_mgr->cmds[xid]; |
710 | if (!io_req) { | 710 | if (!io_req) |
711 | spin_unlock_bh(&tgt->tgt_lock); | 711 | goto ret_err_rqe; |
712 | break; | ||
713 | } | ||
714 | 712 | ||
715 | if (io_req->cmd_type != BNX2FC_SCSI_CMD) { | 713 | if (io_req->cmd_type != BNX2FC_SCSI_CMD) { |
716 | printk(KERN_ERR PFX "err_warn: Not a SCSI cmd\n"); | 714 | printk(KERN_ERR PFX "err_warn: Not a SCSI cmd\n"); |
717 | spin_unlock_bh(&tgt->tgt_lock); | 715 | goto ret_err_rqe; |
718 | break; | ||
719 | } | 716 | } |
720 | 717 | ||
721 | if (test_and_clear_bit(BNX2FC_FLAG_IO_CLEANUP, | 718 | if (test_and_clear_bit(BNX2FC_FLAG_IO_CLEANUP, |
722 | &io_req->req_flags)) { | 719 | &io_req->req_flags)) { |
723 | BNX2FC_IO_DBG(io_req, "unsol_err: cleanup in " | 720 | BNX2FC_IO_DBG(io_req, "unsol_err: cleanup in " |
724 | "progress.. ignore unsol err\n"); | 721 | "progress.. ignore unsol err\n"); |
725 | spin_unlock_bh(&tgt->tgt_lock); | 722 | goto ret_err_rqe; |
726 | break; | 723 | } |
724 | |||
725 | err_warn_bit_map = (u64) | ||
726 | ((u64)err_entry->data.err_warn_bitmap_hi << 32) | | ||
727 | (u64)err_entry->data.err_warn_bitmap_lo; | ||
728 | for (i = 0; i < BNX2FC_NUM_ERR_BITS; i++) { | ||
729 | if (err_warn_bit_map & (u64)((u64)1 << i)) { | ||
730 | err_warn = i; | ||
731 | break; | ||
732 | } | ||
727 | } | 733 | } |
728 | 734 | ||
729 | /* | 735 | /* |
@@ -733,26 +739,61 @@ static void bnx2fc_process_unsol_compl(struct bnx2fc_rport *tgt, u16 wqe) | |||
733 | * logging out the target, when the ABTS eventually | 739 | * logging out the target, when the ABTS eventually |
734 | * times out. | 740 | * times out. |
735 | */ | 741 | */ |
736 | if (!test_and_set_bit(BNX2FC_FLAG_ISSUE_ABTS, | 742 | if (test_bit(BNX2FC_FLAG_ISSUE_ABTS, &io_req->req_flags)) { |
737 | &io_req->req_flags)) { | ||
738 | /* | ||
739 | * Cancel the timeout_work, as we received IO | ||
740 | * completion with FW error. | ||
741 | */ | ||
742 | if (cancel_delayed_work(&io_req->timeout_work)) | ||
743 | kref_put(&io_req->refcount, | ||
744 | bnx2fc_cmd_release); /* timer hold */ | ||
745 | |||
746 | rc = bnx2fc_initiate_abts(io_req); | ||
747 | if (rc != SUCCESS) { | ||
748 | BNX2FC_IO_DBG(io_req, "err_warn: initiate_abts " | ||
749 | "failed. issue cleanup\n"); | ||
750 | rc = bnx2fc_initiate_cleanup(io_req); | ||
751 | BUG_ON(rc); | ||
752 | } | ||
753 | } else | ||
754 | printk(KERN_ERR PFX "err_warn: io_req (0x%x) already " | 743 | printk(KERN_ERR PFX "err_warn: io_req (0x%x) already " |
755 | "in ABTS processing\n", xid); | 744 | "in ABTS processing\n", xid); |
745 | goto ret_err_rqe; | ||
746 | } | ||
747 | BNX2FC_TGT_DBG(tgt, "err = 0x%x\n", err_warn); | ||
748 | if (tgt->dev_type != TYPE_TAPE) | ||
749 | goto skip_rec; | ||
750 | switch (err_warn) { | ||
751 | case FCOE_ERROR_CODE_REC_TOV_TIMER_EXPIRATION: | ||
752 | case FCOE_ERROR_CODE_DATA_OOO_RO: | ||
753 | case FCOE_ERROR_CODE_COMMON_INCORRECT_SEQ_CNT: | ||
754 | case FCOE_ERROR_CODE_DATA_SOFI3_SEQ_ACTIVE_SET: | ||
755 | case FCOE_ERROR_CODE_FCP_RSP_OPENED_SEQ: | ||
756 | case FCOE_ERROR_CODE_DATA_SOFN_SEQ_ACTIVE_RESET: | ||
757 | BNX2FC_TGT_DBG(tgt, "REC TOV popped for xid - 0x%x\n", | ||
758 | xid); | ||
759 | memset(&io_req->err_entry, 0, | ||
760 | sizeof(struct fcoe_err_report_entry)); | ||
761 | memcpy(&io_req->err_entry, err_entry, | ||
762 | sizeof(struct fcoe_err_report_entry)); | ||
763 | if (!test_bit(BNX2FC_FLAG_SRR_SENT, | ||
764 | &io_req->req_flags)) { | ||
765 | spin_unlock_bh(&tgt->tgt_lock); | ||
766 | rc = bnx2fc_send_rec(io_req); | ||
767 | spin_lock_bh(&tgt->tgt_lock); | ||
768 | |||
769 | if (rc) | ||
770 | goto skip_rec; | ||
771 | } else | ||
772 | printk(KERN_ERR PFX "SRR in progress\n"); | ||
773 | goto ret_err_rqe; | ||
774 | break; | ||
775 | default: | ||
776 | break; | ||
777 | } | ||
778 | |||
779 | skip_rec: | ||
780 | set_bit(BNX2FC_FLAG_ISSUE_ABTS, &io_req->req_flags); | ||
781 | /* | ||
782 | * Cancel the timeout_work, as we received IO | ||
783 | * completion with FW error. | ||
784 | */ | ||
785 | if (cancel_delayed_work(&io_req->timeout_work)) | ||
786 | kref_put(&io_req->refcount, bnx2fc_cmd_release); | ||
787 | |||
788 | rc = bnx2fc_initiate_abts(io_req); | ||
789 | if (rc != SUCCESS) { | ||
790 | printk(KERN_ERR PFX "err_warn: initiate_abts " | ||
791 | "failed xid = 0x%x. issue cleanup\n", | ||
792 | io_req->xid); | ||
793 | bnx2fc_initiate_cleanup(io_req); | ||
794 | } | ||
795 | ret_err_rqe: | ||
796 | bnx2fc_return_rqe(tgt, 1); | ||
756 | spin_unlock_bh(&tgt->tgt_lock); | 797 | spin_unlock_bh(&tgt->tgt_lock); |
757 | break; | 798 | break; |
758 | 799 | ||
@@ -773,6 +814,47 @@ static void bnx2fc_process_unsol_compl(struct bnx2fc_rport *tgt, u16 wqe) | |||
773 | BNX2FC_TGT_DBG(tgt, "buf_offsets - tx = 0x%x, rx = 0x%x", | 814 | BNX2FC_TGT_DBG(tgt, "buf_offsets - tx = 0x%x, rx = 0x%x", |
774 | err_entry->data.tx_buf_off, err_entry->data.rx_buf_off); | 815 | err_entry->data.tx_buf_off, err_entry->data.rx_buf_off); |
775 | 816 | ||
817 | if (xid > BNX2FC_MAX_XID) { | ||
818 | BNX2FC_TGT_DBG(tgt, "xid(0x%x) out of FW range\n", xid); | ||
819 | goto ret_warn_rqe; | ||
820 | } | ||
821 | |||
822 | err_warn_bit_map = (u64) | ||
823 | ((u64)err_entry->data.err_warn_bitmap_hi << 32) | | ||
824 | (u64)err_entry->data.err_warn_bitmap_lo; | ||
825 | for (i = 0; i < BNX2FC_NUM_ERR_BITS; i++) { | ||
826 | if (err_warn_bit_map & (u64) (1 << i)) { | ||
827 | err_warn = i; | ||
828 | break; | ||
829 | } | ||
830 | } | ||
831 | BNX2FC_TGT_DBG(tgt, "warn = 0x%x\n", err_warn); | ||
832 | |||
833 | task_idx = xid / BNX2FC_TASKS_PER_PAGE; | ||
834 | index = xid % BNX2FC_TASKS_PER_PAGE; | ||
835 | task_page = (struct fcoe_task_ctx_entry *) | ||
836 | interface->hba->task_ctx[task_idx]; | ||
837 | task = &(task_page[index]); | ||
838 | io_req = (struct bnx2fc_cmd *)hba->cmd_mgr->cmds[xid]; | ||
839 | if (!io_req) | ||
840 | goto ret_warn_rqe; | ||
841 | |||
842 | if (io_req->cmd_type != BNX2FC_SCSI_CMD) { | ||
843 | printk(KERN_ERR PFX "err_warn: Not a SCSI cmd\n"); | ||
844 | goto ret_warn_rqe; | ||
845 | } | ||
846 | |||
847 | memset(&io_req->err_entry, 0, | ||
848 | sizeof(struct fcoe_err_report_entry)); | ||
849 | memcpy(&io_req->err_entry, err_entry, | ||
850 | sizeof(struct fcoe_err_report_entry)); | ||
851 | |||
852 | if (err_warn == FCOE_ERROR_CODE_REC_TOV_TIMER_EXPIRATION) | ||
853 | /* REC_TOV is not a warning code */ | ||
854 | BUG_ON(1); | ||
855 | else | ||
856 | BNX2FC_TGT_DBG(tgt, "Unsolicited warning\n"); | ||
857 | ret_warn_rqe: | ||
776 | bnx2fc_return_rqe(tgt, 1); | 858 | bnx2fc_return_rqe(tgt, 1); |
777 | spin_unlock_bh(&tgt->tgt_lock); | 859 | spin_unlock_bh(&tgt->tgt_lock); |
778 | break; | 860 | break; |