[SCSI] be2iscsi: Fix handling timed out MBX completion from FW

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>
This commit is contained in:
Jayamohan Kallickal 2014-01-29 02:16:39 -05:00 committed by James Bottomley
parent 8e616a5ee6
commit 1957aa7f62
6 changed files with 121 additions and 49 deletions

View File

@ -98,6 +98,14 @@ struct be_mcc_obj {
struct be_queue_info cq; 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 { struct be_ctrl_info {
u8 __iomem *csr; u8 __iomem *csr;
u8 __iomem *db; /* Door Bell */ u8 __iomem *db; /* Door Bell */
@ -122,6 +130,8 @@ struct be_ctrl_info {
unsigned short mcc_alloc_index; unsigned short mcc_alloc_index;
unsigned short mcc_free_index; unsigned short mcc_free_index;
unsigned int mcc_tag_available; unsigned int mcc_tag_available;
struct beiscsi_mcc_tag_state ptag_state[MAX_MCC_CMD + 1];
}; };
#include "be_cmds.h" #include "be_cmds.h"

View File

@ -138,7 +138,7 @@ unsigned int alloc_mcc_tag(struct beiscsi_hba *phba)
* @phba: Driver private structure * @phba: Driver private structure
* @tag: Tag for the MBX Command * @tag: Tag for the MBX Command
* @wrb: the WRB used 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. * 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, int beiscsi_mccq_compl(struct beiscsi_hba *phba,
uint32_t tag, struct be_mcc_wrb **wrb, uint32_t tag, struct be_mcc_wrb **wrb,
void *cmd_hdr) struct be_dma_mem *mbx_cmd_mem)
{ {
int rc = 0; int rc = 0;
uint32_t mcc_tag_response; uint32_t mcc_tag_response;
uint16_t status = 0, addl_status = 0, wrb_num = 0; uint16_t status = 0, addl_status = 0, wrb_num = 0;
struct be_mcc_wrb *temp_wrb; struct be_mcc_wrb *temp_wrb;
struct be_cmd_req_hdr *ioctl_hdr; struct be_cmd_req_hdr *mbx_hdr;
struct be_cmd_resp_hdr *ioctl_resp_hdr; struct be_cmd_resp_hdr *mbx_resp_hdr;
struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q; struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
if (beiscsi_error(phba)) { if (beiscsi_error(phba)) {
free_mcc_tag(&phba->ctrl, tag); 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 */ /* wait for the mccq completion */
rc = wait_event_interruptible_timeout( rc = wait_event_interruptible_timeout(
phba->ctrl.mcc_wait[tag], phba->ctrl.mcc_wait[tag],
@ -171,56 +176,71 @@ int beiscsi_mccq_compl(struct beiscsi_hba *phba,
BEISCSI_HOST_MBX_TIMEOUT)); BEISCSI_HOST_MBX_TIMEOUT));
if (rc <= 0) { 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(phba, KERN_ERR,
BEISCSI_LOG_INIT | BEISCSI_LOG_EH | BEISCSI_LOG_INIT | BEISCSI_LOG_EH |
BEISCSI_LOG_CONFIG, BEISCSI_LOG_CONFIG,
"BC_%d : MBX Cmd Completion timed out\n"); "BC_%d : MBX Cmd Completion timed out\n");
rc = -EBUSY; return -EBUSY;
} else {
/* decrement the mccq used count */
atomic_dec(&phba->ctrl.mcc_obj.q.used);
goto release_mcc_tag;
} else
rc = 0; 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]; mcc_tag_response = phba->ctrl.mcc_numtag[tag];
status = (mcc_tag_response & CQE_STATUS_MASK); status = (mcc_tag_response & CQE_STATUS_MASK);
addl_status = ((mcc_tag_response & CQE_STATUS_ADDL_MASK) >> addl_status = ((mcc_tag_response & CQE_STATUS_ADDL_MASK) >>
CQE_STATUS_ADDL_SHIFT); CQE_STATUS_ADDL_SHIFT);
if (cmd_hdr) { if (mbx_cmd_mem) {
ioctl_hdr = (struct be_cmd_req_hdr *)cmd_hdr; mbx_hdr = (struct be_cmd_req_hdr *)mbx_cmd_mem->va;
} else { } else {
wrb_num = (mcc_tag_response & CQE_STATUS_WRB_MASK) >> wrb_num = (mcc_tag_response & CQE_STATUS_WRB_MASK) >>
CQE_STATUS_WRB_SHIFT; CQE_STATUS_WRB_SHIFT;
temp_wrb = (struct be_mcc_wrb *)queue_get_wrb(mccq, wrb_num); 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) if (wrb)
*wrb = temp_wrb; *wrb = temp_wrb;
} }
if (status || addl_status) { if (status || addl_status) {
beiscsi_log(phba, KERN_ERR, beiscsi_log(phba, KERN_WARNING,
BEISCSI_LOG_INIT | BEISCSI_LOG_EH | BEISCSI_LOG_INIT | BEISCSI_LOG_EH |
BEISCSI_LOG_CONFIG, BEISCSI_LOG_CONFIG,
"BC_%d : MBX Cmd Failed for " "BC_%d : MBX Cmd Failed for "
"Subsys : %d Opcode : %d with " "Subsys : %d Opcode : %d with "
"Status : %d and Extd_Status : %d\n", "Status : %d and Extd_Status : %d\n",
ioctl_hdr->subsystem, mbx_hdr->subsystem,
ioctl_hdr->opcode, mbx_hdr->opcode,
status, addl_status); status, addl_status);
if (status == MCC_STATUS_INSUFFICIENT_BUFFER) { 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(phba, KERN_WARNING,
BEISCSI_LOG_INIT | BEISCSI_LOG_EH | BEISCSI_LOG_INIT | BEISCSI_LOG_EH |
BEISCSI_LOG_CONFIG, BEISCSI_LOG_CONFIG,
"BC_%d : Insufficent Buffer Error " "BC_%d : Insufficent Buffer Error "
"Resp_Len : %d Actual_Resp_Len : %d\n", "Resp_Len : %d Actual_Resp_Len : %d\n",
ioctl_resp_hdr->response_length, mbx_resp_hdr->response_length,
ioctl_resp_hdr->actual_resp_len); mbx_resp_hdr->actual_resp_len);
rc = -EAGAIN; rc = -EAGAIN;
goto release_mcc_tag; 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, int be_mcc_compl_process_isr(struct be_ctrl_info *ctrl,
struct be_mcc_compl *compl) struct be_mcc_compl *compl)
{ {
struct beiscsi_hba *phba = pci_get_drvdata(ctrl->pdev);
u16 compl_status, extd_status; u16 compl_status, extd_status;
unsigned short tag; 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] |= (compl->tag0 & 0x00FF0000);
ctrl->mcc_numtag[tag] |= (extd_status & 0x000000FF) << 8; ctrl->mcc_numtag[tag] |= (extd_status & 0x000000FF) << 8;
ctrl->mcc_numtag[tag] |= (compl_status & 0x000000FF); 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; return 0;
} }

View 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); void free_mcc_tag(struct be_ctrl_info *ctrl, unsigned int tag);
int beiscsi_mccq_compl(struct beiscsi_hba *phba, 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 */ /*ISCSI Functuions */
int be_cmd_fw_initialize(struct be_ctrl_info *ctrl); int be_cmd_fw_initialize(struct be_ctrl_info *ctrl);
int be_cmd_fw_uninit(struct be_ctrl_info *ctrl); int be_cmd_fw_uninit(struct be_ctrl_info *ctrl);

View File

@ -1153,16 +1153,18 @@ static int beiscsi_open_conn(struct iscsi_endpoint *ep,
return -EAGAIN; return -EAGAIN;
} }
ret = beiscsi_mccq_compl(phba, tag, NULL, nonemb_cmd.va); ret = beiscsi_mccq_compl(phba, tag, NULL, &nonemb_cmd);
if (ret) { if (ret) {
beiscsi_log(phba, KERN_ERR, beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
"BS_%d : mgmt_open_connection Failed"); "BS_%d : mgmt_open_connection Failed");
pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size, if (ret != -EBUSY)
nonemb_cmd.va, nonemb_cmd.dma); pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
nonemb_cmd.va, nonemb_cmd.dma);
beiscsi_free_ep(beiscsi_ep); beiscsi_free_ep(beiscsi_ep);
return -EBUSY; return ret;
} }
ptcpcnct_out = (struct tcp_connect_and_offload_out *)nonemb_cmd.va; ptcpcnct_out = (struct tcp_connect_and_offload_out *)nonemb_cmd.va;

View File

@ -228,6 +228,7 @@ static int beiscsi_eh_abort(struct scsi_cmnd *sc)
struct invalidate_command_table *inv_tbl; struct invalidate_command_table *inv_tbl;
struct be_dma_mem nonemb_cmd; struct be_dma_mem nonemb_cmd;
unsigned int cid, tag, num_invalidate; unsigned int cid, tag, num_invalidate;
int rc;
cls_session = starget_to_session(scsi_target(sc->device)); cls_session = starget_to_session(scsi_target(sc->device));
session = cls_session->dd_data; session = cls_session->dd_data;
@ -285,9 +286,11 @@ static int beiscsi_eh_abort(struct scsi_cmnd *sc)
return FAILED; return FAILED;
} }
beiscsi_mccq_compl(phba, tag, NULL, nonemb_cmd.va); rc = beiscsi_mccq_compl(phba, tag, NULL, &nonemb_cmd);
pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size, if (rc != -EBUSY)
nonemb_cmd.va, nonemb_cmd.dma); pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
nonemb_cmd.va, nonemb_cmd.dma);
return iscsi_eh_abort(sc); 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 invalidate_command_table *inv_tbl;
struct be_dma_mem nonemb_cmd; struct be_dma_mem nonemb_cmd;
unsigned int cid, tag, i, num_invalidate; unsigned int cid, tag, i, num_invalidate;
int rc;
/* invalidate iocbs */ /* invalidate iocbs */
cls_session = starget_to_session(scsi_target(sc->device)); 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; return FAILED;
} }
beiscsi_mccq_compl(phba, tag, NULL, nonemb_cmd.va); rc = beiscsi_mccq_compl(phba, tag, NULL, &nonemb_cmd);
pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size, if (rc != -EBUSY)
nonemb_cmd.va, nonemb_cmd.dma); pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
nonemb_cmd.va, nonemb_cmd.dma);
return iscsi_eh_device_reset(sc); return iscsi_eh_device_reset(sc);
} }
@ -4360,12 +4365,16 @@ static int beiscsi_get_boot_info(struct beiscsi_hba *phba)
goto boot_freemem; goto boot_freemem;
} }
ret = beiscsi_mccq_compl(phba, tag, NULL, nonemb_cmd.va); ret = beiscsi_mccq_compl(phba, tag, NULL, &nonemb_cmd);
if (ret) { if (ret) {
beiscsi_log(phba, KERN_ERR, beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG,
"BM_%d : beiscsi_get_session_info Failed"); "BM_%d : beiscsi_get_session_info Failed");
goto boot_freemem;
if (ret != -EBUSY)
goto boot_freemem;
else
return ret;
} }
session_resp = nonemb_cmd.va ; 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_tag[i] = i + 1;
phba->ctrl.mcc_numtag[i + 1] = 0; phba->ctrl.mcc_numtag[i + 1] = 0;
phba->ctrl.mcc_tag_available++; 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; phba->ctrl.mcc_alloc_index = phba->ctrl.mcc_free_index = 0;

View File

@ -828,22 +828,25 @@ static int mgmt_exec_nonemb_cmd(struct beiscsi_hba *phba,
be_mcc_notify(phba); be_mcc_notify(phba);
spin_unlock(&ctrl->mbox_lock); 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 (rc) {
/* Check if the IOCTL needs to be re-issued */
if (rc == -EAGAIN)
return rc;
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
"BG_%d : mgmt_exec_nonemb_cmd Failed status\n");
goto free_cmd;
}
if (resp_buf) if (resp_buf)
memcpy(resp_buf, nonemb_cmd->va, resp_buf_len); memcpy(resp_buf, nonemb_cmd->va, resp_buf_len);
if (rc) {
/* Check if the MBX Cmd needs to be re-issued */
if (rc == -EAGAIN)
return rc;
beiscsi_log(phba, KERN_WARNING,
BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
"BG_%d : mgmt_exec_nonemb_cmd Failed status\n");
if (rc != -EBUSY)
goto free_cmd;
else
return rc;
}
free_cmd: free_cmd:
pci_free_consistent(ctrl->pdev, nonemb_cmd->size, pci_free_consistent(ctrl->pdev, nonemb_cmd->size,
nonemb_cmd->va, nonemb_cmd->dma); nonemb_cmd->va, nonemb_cmd->dma);
@ -1348,7 +1351,6 @@ int mgmt_set_vlan(struct beiscsi_hba *phba,
{ {
int rc; int rc;
unsigned int tag; unsigned int tag;
struct be_mcc_wrb *wrb = NULL;
tag = be_cmd_set_vlan(phba, vlan_tag); tag = be_cmd_set_vlan(phba, vlan_tag);
if (!tag) { if (!tag) {
@ -1358,7 +1360,7 @@ int mgmt_set_vlan(struct beiscsi_hba *phba,
return -EBUSY; return -EBUSY;
} }
rc = beiscsi_mccq_compl(phba, tag, &wrb, NULL); rc = beiscsi_mccq_compl(phba, tag, NULL, NULL);
if (rc) { if (rc) {
beiscsi_log(phba, KERN_ERR, beiscsi_log(phba, KERN_ERR,
(BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX), (BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX),