[SCSI] be2iscsi: Fix handling timed out MBX completion from FW
authorJayamohan Kallickal <jayamohan.kallickal@emulex.com>
Wed, 29 Jan 2014 07:16:39 +0000 (02:16 -0500)
committerJames Bottomley <JBottomley@Parallels.com>
Sat, 15 Mar 2014 17:19:10 +0000 (10:19 -0700)
When an MBX command timeout happens,the resources associated with
the MBX command were freed. If FW were to give the response to
host after the timeout value set by driver then driver crashes as the MBX Cmd
resources were already freed.

This patch fixes this issue by maintaing a state flag for each of
the MBX command posted/timedout/completed.

Signed-off-by: John Soni Jose <sony.john-n@emulex.com>
Signed-off-by: Jayamohan Kallickal <jayamohan.kallickal@emulex.com>
Reviewed-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
drivers/scsi/be2iscsi/be.h
drivers/scsi/be2iscsi/be_cmds.c
drivers/scsi/be2iscsi/be_cmds.h
drivers/scsi/be2iscsi/be_iscsi.c
drivers/scsi/be2iscsi/be_main.c
drivers/scsi/be2iscsi/be_mgmt.c

index 2e28f6c419fe253231864707225e5a09b68e033d..23c73fe426484534a3c53efbc84a546fe62a50af 100644 (file)
@@ -98,6 +98,14 @@ struct be_mcc_obj {
        struct be_queue_info cq;
 };
 
+struct beiscsi_mcc_tag_state {
+#define MCC_TAG_STATE_COMPLETED 0x00
+#define MCC_TAG_STATE_RUNNING   0x01
+#define MCC_TAG_STATE_TIMEOUT   0x02
+       uint8_t tag_state;
+       struct be_dma_mem tag_mem_state;
+};
+
 struct be_ctrl_info {
        u8 __iomem *csr;
        u8 __iomem *db;         /* Door Bell */
@@ -122,6 +130,8 @@ struct be_ctrl_info {
        unsigned short mcc_alloc_index;
        unsigned short mcc_free_index;
        unsigned int mcc_tag_available;
+
+       struct beiscsi_mcc_tag_state ptag_state[MAX_MCC_CMD + 1];
 };
 
 #include "be_cmds.h"
index 3338391b64de9b1aba50844c014f7e5fc44706b3..b14949a71eda3da4df089eb8e1c6c4de8d55f2a7 100644 (file)
@@ -138,7 +138,7 @@ unsigned int alloc_mcc_tag(struct beiscsi_hba *phba)
  * @phba: Driver private structure
  * @tag: Tag for the MBX Command
  * @wrb: the WRB used for the MBX Command
- * @cmd_hdr: IOCTL Hdr for the MBX Cmd
+ * @mbx_cmd_mem: ptr to memory allocated for MBX Cmd
  *
  * Waits for MBX completion with the passed TAG.
  *
@@ -148,21 +148,26 @@ unsigned int alloc_mcc_tag(struct beiscsi_hba *phba)
  **/
 int beiscsi_mccq_compl(struct beiscsi_hba *phba,
                uint32_t tag, struct be_mcc_wrb **wrb,
-               void *cmd_hdr)
+               struct be_dma_mem *mbx_cmd_mem)
 {
        int rc = 0;
        uint32_t mcc_tag_response;
        uint16_t status = 0, addl_status = 0, wrb_num = 0;
        struct be_mcc_wrb *temp_wrb;
-       struct be_cmd_req_hdr *ioctl_hdr;
-       struct be_cmd_resp_hdr *ioctl_resp_hdr;
+       struct be_cmd_req_hdr *mbx_hdr;
+       struct be_cmd_resp_hdr *mbx_resp_hdr;
        struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
 
        if (beiscsi_error(phba)) {
                free_mcc_tag(&phba->ctrl, tag);
-               return -EIO;
+               return -EPERM;
        }
 
+       /* Set MBX Tag state to Active */
+       spin_lock(&phba->ctrl.mbox_lock);
+       phba->ctrl.ptag_state[tag].tag_state = MCC_TAG_STATE_RUNNING;
+       spin_unlock(&phba->ctrl.mbox_lock);
+
        /* wait for the mccq completion */
        rc = wait_event_interruptible_timeout(
                                phba->ctrl.mcc_wait[tag],
@@ -171,56 +176,71 @@ int beiscsi_mccq_compl(struct beiscsi_hba *phba,
                                BEISCSI_HOST_MBX_TIMEOUT));
 
        if (rc <= 0) {
+               struct be_dma_mem *tag_mem;
+               /* Set MBX Tag state to timeout */
+               spin_lock(&phba->ctrl.mbox_lock);
+               phba->ctrl.ptag_state[tag].tag_state = MCC_TAG_STATE_TIMEOUT;
+               spin_unlock(&phba->ctrl.mbox_lock);
+
+               /* Store resource addr to be freed later */
+               tag_mem = &phba->ctrl.ptag_state[tag].tag_mem_state;
+               if (mbx_cmd_mem) {
+                       tag_mem->size = mbx_cmd_mem->size;
+                       tag_mem->va = mbx_cmd_mem->va;
+                       tag_mem->dma = mbx_cmd_mem->dma;
+               } else
+                       tag_mem->size = 0;
+
                beiscsi_log(phba, KERN_ERR,
                            BEISCSI_LOG_INIT | BEISCSI_LOG_EH |
                            BEISCSI_LOG_CONFIG,
                            "BC_%d : MBX Cmd Completion timed out\n");
-               rc = -EBUSY;
-
-               /* decrement the mccq used count */
-               atomic_dec(&phba->ctrl.mcc_obj.q.used);
-
-               goto release_mcc_tag;
-       } else
+               return -EBUSY;
+       } else {
                rc = 0;
+               /* Set MBX Tag state to completed */
+               spin_lock(&phba->ctrl.mbox_lock);
+               phba->ctrl.ptag_state[tag].tag_state = MCC_TAG_STATE_COMPLETED;
+               spin_unlock(&phba->ctrl.mbox_lock);
+       }
 
        mcc_tag_response = phba->ctrl.mcc_numtag[tag];
        status = (mcc_tag_response & CQE_STATUS_MASK);
        addl_status = ((mcc_tag_response & CQE_STATUS_ADDL_MASK) >>
                        CQE_STATUS_ADDL_SHIFT);
 
-       if (cmd_hdr) {
-               ioctl_hdr = (struct be_cmd_req_hdr *)cmd_hdr;
+       if (mbx_cmd_mem) {
+               mbx_hdr = (struct be_cmd_req_hdr *)mbx_cmd_mem->va;
        } else {
                wrb_num = (mcc_tag_response & CQE_STATUS_WRB_MASK) >>
                           CQE_STATUS_WRB_SHIFT;
                temp_wrb = (struct be_mcc_wrb *)queue_get_wrb(mccq, wrb_num);
-               ioctl_hdr = embedded_payload(temp_wrb);
+               mbx_hdr = embedded_payload(temp_wrb);
 
                if (wrb)
                        *wrb = temp_wrb;
        }
 
        if (status || addl_status) {
-               beiscsi_log(phba, KERN_ERR,
+               beiscsi_log(phba, KERN_WARNING,
                            BEISCSI_LOG_INIT | BEISCSI_LOG_EH |
                            BEISCSI_LOG_CONFIG,
                            "BC_%d : MBX Cmd Failed for "
                            "Subsys : %d Opcode : %d with "
                            "Status : %d and Extd_Status : %d\n",
-                           ioctl_hdr->subsystem,
-                           ioctl_hdr->opcode,
+                           mbx_hdr->subsystem,
+                           mbx_hdr->opcode,
                            status, addl_status);
 
                if (status == MCC_STATUS_INSUFFICIENT_BUFFER) {
-                       ioctl_resp_hdr = (struct be_cmd_resp_hdr *) ioctl_hdr;
+                       mbx_resp_hdr = (struct be_cmd_resp_hdr *) mbx_hdr;
                        beiscsi_log(phba, KERN_WARNING,
                                    BEISCSI_LOG_INIT | BEISCSI_LOG_EH |
                                    BEISCSI_LOG_CONFIG,
                                    "BC_%d : Insufficent Buffer Error "
                                    "Resp_Len : %d Actual_Resp_Len : %d\n",
-                                   ioctl_resp_hdr->response_length,
-                                   ioctl_resp_hdr->actual_resp_len);
+                                   mbx_resp_hdr->response_length,
+                                   mbx_resp_hdr->actual_resp_len);
 
                        rc = -EAGAIN;
                        goto release_mcc_tag;
@@ -319,6 +339,7 @@ static int be_mcc_compl_process(struct be_ctrl_info *ctrl,
 int be_mcc_compl_process_isr(struct be_ctrl_info *ctrl,
                                    struct be_mcc_compl *compl)
 {
+       struct beiscsi_hba *phba = pci_get_drvdata(ctrl->pdev);
        u16 compl_status, extd_status;
        unsigned short tag;
 
@@ -338,7 +359,32 @@ int be_mcc_compl_process_isr(struct be_ctrl_info *ctrl,
        ctrl->mcc_numtag[tag] |= (compl->tag0 & 0x00FF0000);
        ctrl->mcc_numtag[tag] |= (extd_status & 0x000000FF) << 8;
        ctrl->mcc_numtag[tag] |= (compl_status & 0x000000FF);
-       wake_up_interruptible(&ctrl->mcc_wait[tag]);
+
+       if (ctrl->ptag_state[tag].tag_state == MCC_TAG_STATE_RUNNING) {
+               wake_up_interruptible(&ctrl->mcc_wait[tag]);
+       } else if (ctrl->ptag_state[tag].tag_state == MCC_TAG_STATE_TIMEOUT) {
+               struct be_dma_mem *tag_mem;
+               tag_mem = &ctrl->ptag_state[tag].tag_mem_state;
+
+               beiscsi_log(phba, KERN_WARNING,
+                           BEISCSI_LOG_MBOX | BEISCSI_LOG_INIT |
+                           BEISCSI_LOG_CONFIG,
+                           "BC_%d : MBX Completion for timeout Command "
+                           "from FW\n");
+               /* Check if memory needs to be freed */
+               if (tag_mem->size)
+                       pci_free_consistent(ctrl->pdev, tag_mem->size,
+                                           tag_mem->va, tag_mem->dma);
+
+               /* Change tag state */
+               spin_lock(&phba->ctrl.mbox_lock);
+               ctrl->ptag_state[tag].tag_state = MCC_TAG_STATE_COMPLETED;
+               spin_unlock(&phba->ctrl.mbox_lock);
+
+               /* Free MCC Tag */
+               free_mcc_tag(ctrl, tag);
+       }
+
        return 0;
 }
 
index 627ebbe0172c89d6f1fdf501c09cc7f7f854a8f8..770b6c8bcad864d77bd6e54b11e6d743a24491c5 100644 (file)
@@ -709,7 +709,8 @@ unsigned int be_cmd_get_port_speed(struct beiscsi_hba *phba);
 void free_mcc_tag(struct be_ctrl_info *ctrl, unsigned int tag);
 
 int beiscsi_mccq_compl(struct beiscsi_hba *phba,
-                       uint32_t tag, struct be_mcc_wrb **wrb, void *cmd_va);
+                       uint32_t tag, struct be_mcc_wrb **wrb,
+                       struct be_dma_mem *mbx_cmd_mem);
 /*ISCSI Functuions */
 int be_cmd_fw_initialize(struct be_ctrl_info *ctrl);
 int be_cmd_fw_uninit(struct be_ctrl_info *ctrl);
index 889066d9d6fb92a3a43e0e2dd5173f8f9c19d347..bdd0f05a33423fb35aee80febd36a124e53b0d7d 100644 (file)
@@ -1153,16 +1153,18 @@ static int beiscsi_open_conn(struct iscsi_endpoint *ep,
                return -EAGAIN;
        }
 
-       ret = beiscsi_mccq_compl(phba, tag, NULL, nonemb_cmd.va);
+       ret = beiscsi_mccq_compl(phba, tag, NULL, &nonemb_cmd);
        if (ret) {
                beiscsi_log(phba, KERN_ERR,
                            BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
                            "BS_%d : mgmt_open_connection Failed");
 
-               pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
-                           nonemb_cmd.va, nonemb_cmd.dma);
+               if (ret != -EBUSY)
+                       pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
+                                           nonemb_cmd.va, nonemb_cmd.dma);
+
                beiscsi_free_ep(beiscsi_ep);
-               return -EBUSY;
+               return ret;
        }
 
        ptcpcnct_out = (struct tcp_connect_and_offload_out *)nonemb_cmd.va;
index 5642a9b250c2fe11201510460563ec576c70461c..c8f90cef728d458d8dc5798ec1c7bcada9a43246 100644 (file)
@@ -228,6 +228,7 @@ static int beiscsi_eh_abort(struct scsi_cmnd *sc)
        struct invalidate_command_table *inv_tbl;
        struct be_dma_mem nonemb_cmd;
        unsigned int cid, tag, num_invalidate;
+       int rc;
 
        cls_session = starget_to_session(scsi_target(sc->device));
        session = cls_session->dd_data;
@@ -285,9 +286,11 @@ static int beiscsi_eh_abort(struct scsi_cmnd *sc)
                return FAILED;
        }
 
-       beiscsi_mccq_compl(phba, tag, NULL, nonemb_cmd.va);
-       pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
-                           nonemb_cmd.va, nonemb_cmd.dma);
+       rc = beiscsi_mccq_compl(phba, tag, NULL, &nonemb_cmd);
+       if (rc != -EBUSY)
+               pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
+                                   nonemb_cmd.va, nonemb_cmd.dma);
+
        return iscsi_eh_abort(sc);
 }
 
@@ -303,6 +306,7 @@ static int beiscsi_eh_device_reset(struct scsi_cmnd *sc)
        struct invalidate_command_table *inv_tbl;
        struct be_dma_mem nonemb_cmd;
        unsigned int cid, tag, i, num_invalidate;
+       int rc;
 
        /* invalidate iocbs */
        cls_session = starget_to_session(scsi_target(sc->device));
@@ -363,9 +367,10 @@ static int beiscsi_eh_device_reset(struct scsi_cmnd *sc)
                return FAILED;
        }
 
-       beiscsi_mccq_compl(phba, tag, NULL, nonemb_cmd.va);
-       pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
-                           nonemb_cmd.va, nonemb_cmd.dma);
+       rc = beiscsi_mccq_compl(phba, tag, NULL, &nonemb_cmd);
+       if (rc != -EBUSY)
+               pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
+                                   nonemb_cmd.va, nonemb_cmd.dma);
        return iscsi_eh_device_reset(sc);
 }
 
@@ -4360,12 +4365,16 @@ static int beiscsi_get_boot_info(struct beiscsi_hba *phba)
                goto boot_freemem;
        }
 
-       ret = beiscsi_mccq_compl(phba, tag, NULL, nonemb_cmd.va);
+       ret = beiscsi_mccq_compl(phba, tag, NULL, &nonemb_cmd);
        if (ret) {
                beiscsi_log(phba, KERN_ERR,
                            BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG,
                            "BM_%d : beiscsi_get_session_info Failed");
-               goto boot_freemem;
+
+               if (ret != -EBUSY)
+                       goto boot_freemem;
+               else
+                       return ret;
        }
 
        session_resp = nonemb_cmd.va ;
@@ -5594,6 +5603,8 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev,
                phba->ctrl.mcc_tag[i] = i + 1;
                phba->ctrl.mcc_numtag[i + 1] = 0;
                phba->ctrl.mcc_tag_available++;
+               memset(&phba->ctrl.ptag_state[i].tag_mem_state, 0,
+                      sizeof(struct beiscsi_mcc_tag_state));
        }
 
        phba->ctrl.mcc_alloc_index = phba->ctrl.mcc_free_index = 0;
index b2fcac78feaa995430fe62e4cd2587735313dabb..088bdf752cfafac10630ce830e4b12f6602dbf4a 100644 (file)
@@ -828,22 +828,25 @@ static int mgmt_exec_nonemb_cmd(struct beiscsi_hba *phba,
        be_mcc_notify(phba);
        spin_unlock(&ctrl->mbox_lock);
 
-       rc = beiscsi_mccq_compl(phba, tag, NULL, nonemb_cmd->va);
+       rc = beiscsi_mccq_compl(phba, tag, NULL, nonemb_cmd);
+
+       if (resp_buf)
+               memcpy(resp_buf, nonemb_cmd->va, resp_buf_len);
+
        if (rc) {
-               /* Check if the IOCTL needs to be re-issued */
+               /* Check if the MBX Cmd needs to be re-issued */
                if (rc == -EAGAIN)
                        return rc;
 
-               beiscsi_log(phba, KERN_ERR,
+               beiscsi_log(phba, KERN_WARNING,
                            BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
                            "BG_%d : mgmt_exec_nonemb_cmd Failed status\n");
 
-               goto free_cmd;
+               if (rc != -EBUSY)
+                       goto free_cmd;
+               else
+                       return rc;
        }
-
-       if (resp_buf)
-               memcpy(resp_buf, nonemb_cmd->va, resp_buf_len);
-
 free_cmd:
        pci_free_consistent(ctrl->pdev, nonemb_cmd->size,
                            nonemb_cmd->va, nonemb_cmd->dma);
@@ -1348,7 +1351,6 @@ int mgmt_set_vlan(struct beiscsi_hba *phba,
 {
        int rc;
        unsigned int tag;
-       struct be_mcc_wrb *wrb = NULL;
 
        tag = be_cmd_set_vlan(phba, vlan_tag);
        if (!tag) {
@@ -1358,7 +1360,7 @@ int mgmt_set_vlan(struct beiscsi_hba *phba,
                return -EBUSY;
        }
 
-       rc = beiscsi_mccq_compl(phba, tag, &wrb, NULL);
+       rc = beiscsi_mccq_compl(phba, tag, NULL, NULL);
        if (rc) {
                beiscsi_log(phba, KERN_ERR,
                            (BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX),