Commit a33c4f7b authored by James Smart's avatar James Smart Committed by James Bottomley

[SCSI] scsilpfc 8.3.38: Fixed bsg timeout handling issues that would result in crashes

Signed-off-by: default avatarJames Smart <james.smart@emulex.com>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent ae05ebe3
...@@ -64,18 +64,14 @@ struct lpfc_bsg_event { ...@@ -64,18 +64,14 @@ struct lpfc_bsg_event {
struct list_head events_to_get; struct list_head events_to_get;
struct list_head events_to_see; struct list_head events_to_see;
/* job waiting for this event to finish */ /* driver data associated with the job */
struct fc_bsg_job *set_job; void *dd_data;
}; };
struct lpfc_bsg_iocb { struct lpfc_bsg_iocb {
struct lpfc_iocbq *cmdiocbq; struct lpfc_iocbq *cmdiocbq;
struct lpfc_iocbq *rspiocbq; struct lpfc_dmabuf *rmp;
struct lpfc_dmabuf *bmp;
struct lpfc_nodelist *ndlp; struct lpfc_nodelist *ndlp;
/* job waiting for this iocb to finish */
struct fc_bsg_job *set_job;
}; };
struct lpfc_bsg_mbox { struct lpfc_bsg_mbox {
...@@ -86,20 +82,13 @@ struct lpfc_bsg_mbox { ...@@ -86,20 +82,13 @@ struct lpfc_bsg_mbox {
uint32_t mbOffset; /* from app */ uint32_t mbOffset; /* from app */
uint32_t inExtWLen; /* from app */ uint32_t inExtWLen; /* from app */
uint32_t outExtWLen; /* from app */ uint32_t outExtWLen; /* from app */
/* job waiting for this mbox command to finish */
struct fc_bsg_job *set_job;
}; };
#define MENLO_DID 0x0000FC0E #define MENLO_DID 0x0000FC0E
struct lpfc_bsg_menlo { struct lpfc_bsg_menlo {
struct lpfc_iocbq *cmdiocbq; struct lpfc_iocbq *cmdiocbq;
struct lpfc_iocbq *rspiocbq; struct lpfc_dmabuf *rmp;
struct lpfc_dmabuf *bmp;
/* job waiting for this iocb to finish */
struct fc_bsg_job *set_job;
}; };
#define TYPE_EVT 1 #define TYPE_EVT 1
...@@ -108,6 +97,7 @@ struct lpfc_bsg_menlo { ...@@ -108,6 +97,7 @@ struct lpfc_bsg_menlo {
#define TYPE_MENLO 4 #define TYPE_MENLO 4
struct bsg_job_data { struct bsg_job_data {
uint32_t type; uint32_t type;
struct fc_bsg_job *set_job; /* job waiting for this iocb to finish */
union { union {
struct lpfc_bsg_event *evt; struct lpfc_bsg_event *evt;
struct lpfc_bsg_iocb iocb; struct lpfc_bsg_iocb iocb;
...@@ -141,6 +131,138 @@ struct lpfc_dmabufext { ...@@ -141,6 +131,138 @@ struct lpfc_dmabufext {
uint32_t flag; uint32_t flag;
}; };
static void
lpfc_free_bsg_buffers(struct lpfc_hba *phba, struct lpfc_dmabuf *mlist)
{
struct lpfc_dmabuf *mlast, *next_mlast;
if (mlist) {
list_for_each_entry_safe(mlast, next_mlast, &mlist->list,
list) {
lpfc_mbuf_free(phba, mlast->virt, mlast->phys);
list_del(&mlast->list);
kfree(mlast);
}
lpfc_mbuf_free(phba, mlist->virt, mlist->phys);
kfree(mlist);
}
return;
}
static struct lpfc_dmabuf *
lpfc_alloc_bsg_buffers(struct lpfc_hba *phba, unsigned int size,
int outbound_buffers, struct ulp_bde64 *bpl,
int *bpl_entries)
{
struct lpfc_dmabuf *mlist = NULL;
struct lpfc_dmabuf *mp;
unsigned int bytes_left = size;
/* Verify we can support the size specified */
if (!size || (size > (*bpl_entries * LPFC_BPL_SIZE)))
return NULL;
/* Determine the number of dma buffers to allocate */
*bpl_entries = (size % LPFC_BPL_SIZE ? size/LPFC_BPL_SIZE + 1 :
size/LPFC_BPL_SIZE);
/* Allocate dma buffer and place in BPL passed */
while (bytes_left) {
/* Allocate dma buffer */
mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
if (!mp) {
if (mlist)
lpfc_free_bsg_buffers(phba, mlist);
return NULL;
}
INIT_LIST_HEAD(&mp->list);
mp->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &(mp->phys));
if (!mp->virt) {
kfree(mp);
if (mlist)
lpfc_free_bsg_buffers(phba, mlist);
return NULL;
}
/* Queue it to a linked list */
if (!mlist)
mlist = mp;
else
list_add_tail(&mp->list, &mlist->list);
/* Add buffer to buffer pointer list */
if (outbound_buffers)
bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
else
bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
bpl->addrLow = le32_to_cpu(putPaddrLow(mp->phys));
bpl->addrHigh = le32_to_cpu(putPaddrHigh(mp->phys));
bpl->tus.f.bdeSize = (uint16_t)
(bytes_left >= LPFC_BPL_SIZE ? LPFC_BPL_SIZE :
bytes_left);
bytes_left -= bpl->tus.f.bdeSize;
bpl->tus.w = le32_to_cpu(bpl->tus.w);
bpl++;
}
return mlist;
}
static unsigned int
lpfc_bsg_copy_data(struct lpfc_dmabuf *dma_buffers,
struct fc_bsg_buffer *bsg_buffers,
unsigned int bytes_to_transfer, int to_buffers)
{
struct lpfc_dmabuf *mp;
unsigned int transfer_bytes, bytes_copied = 0;
unsigned int sg_offset, dma_offset;
unsigned char *dma_address, *sg_address;
struct scatterlist *sgel;
LIST_HEAD(temp_list);
list_splice_init(&dma_buffers->list, &temp_list);
list_add(&dma_buffers->list, &temp_list);
sg_offset = 0;
sgel = bsg_buffers->sg_list;
list_for_each_entry(mp, &temp_list, list) {
dma_offset = 0;
while (bytes_to_transfer && sgel &&
(dma_offset < LPFC_BPL_SIZE)) {
dma_address = mp->virt + dma_offset;
if (sg_offset) {
/* Continue previous partial transfer of sg */
sg_address = sg_virt(sgel) + sg_offset;
transfer_bytes = sgel->length - sg_offset;
} else {
sg_address = sg_virt(sgel);
transfer_bytes = sgel->length;
}
if (bytes_to_transfer < transfer_bytes)
transfer_bytes = bytes_to_transfer;
if (transfer_bytes > (LPFC_BPL_SIZE - dma_offset))
transfer_bytes = LPFC_BPL_SIZE - dma_offset;
if (to_buffers)
memcpy(dma_address, sg_address, transfer_bytes);
else
memcpy(sg_address, dma_address, transfer_bytes);
dma_offset += transfer_bytes;
sg_offset += transfer_bytes;
bytes_to_transfer -= transfer_bytes;
bytes_copied += transfer_bytes;
if (sg_offset >= sgel->length) {
sg_offset = 0;
sgel = sg_next(sgel);
}
}
}
list_del_init(&dma_buffers->list);
list_splice(&temp_list, &dma_buffers->list);
return bytes_copied;
}
/** /**
* lpfc_bsg_send_mgmt_cmd_cmp - lpfc_bsg_send_mgmt_cmd's completion handler * lpfc_bsg_send_mgmt_cmd_cmp - lpfc_bsg_send_mgmt_cmd's completion handler
* @phba: Pointer to HBA context object. * @phba: Pointer to HBA context object.
...@@ -166,33 +288,34 @@ lpfc_bsg_send_mgmt_cmd_cmp(struct lpfc_hba *phba, ...@@ -166,33 +288,34 @@ lpfc_bsg_send_mgmt_cmd_cmp(struct lpfc_hba *phba,
struct bsg_job_data *dd_data; struct bsg_job_data *dd_data;
struct fc_bsg_job *job; struct fc_bsg_job *job;
IOCB_t *rsp; IOCB_t *rsp;
struct lpfc_dmabuf *bmp; struct lpfc_dmabuf *bmp, *cmp, *rmp;
struct lpfc_nodelist *ndlp; struct lpfc_nodelist *ndlp;
struct lpfc_bsg_iocb *iocb; struct lpfc_bsg_iocb *iocb;
unsigned long flags; unsigned long flags;
unsigned int rsp_size;
int rc = 0; int rc = 0;
dd_data = cmdiocbq->context1;
/* Determine if job has been aborted */
spin_lock_irqsave(&phba->ct_ev_lock, flags); spin_lock_irqsave(&phba->ct_ev_lock, flags);
dd_data = cmdiocbq->context2; job = dd_data->set_job;
if (!dd_data) { if (job) {
spin_unlock_irqrestore(&phba->ct_ev_lock, flags); /* Prevent timeout handling from trying to abort job */
lpfc_sli_release_iocbq(phba, cmdiocbq); job->dd_data = NULL;
return;
} }
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
iocb = &dd_data->context_un.iocb; iocb = &dd_data->context_un.iocb;
job = iocb->set_job; ndlp = iocb->ndlp;
job->dd_data = NULL; /* so timeout handler does not reply */ rmp = iocb->rmp;
cmp = cmdiocbq->context2;
bmp = iocb->bmp; bmp = cmdiocbq->context3;
rsp = &rspiocbq->iocb; rsp = &rspiocbq->iocb;
ndlp = cmdiocbq->context1;
pci_unmap_sg(phba->pcidev, job->request_payload.sg_list, /* Copy the completed data or set the error status */
job->request_payload.sg_cnt, DMA_TO_DEVICE);
pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list,
job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
if (job) {
if (rsp->ulpStatus) { if (rsp->ulpStatus) {
if (rsp->ulpStatus == IOSTAT_LOCAL_REJECT) { if (rsp->ulpStatus == IOSTAT_LOCAL_REJECT) {
switch (rsp->un.ulpWord[4] & IOERR_PARAM_MASK) { switch (rsp->un.ulpWord[4] & IOERR_PARAM_MASK) {
...@@ -206,22 +329,31 @@ lpfc_bsg_send_mgmt_cmd_cmp(struct lpfc_hba *phba, ...@@ -206,22 +329,31 @@ lpfc_bsg_send_mgmt_cmd_cmp(struct lpfc_hba *phba,
rc = -EACCES; rc = -EACCES;
break; break;
} }
} else } else {
rc = -EACCES; rc = -EACCES;
} else }
} else {
rsp_size = rsp->un.genreq64.bdl.bdeSize;
job->reply->reply_payload_rcv_len = job->reply->reply_payload_rcv_len =
rsp->un.genreq64.bdl.bdeSize; lpfc_bsg_copy_data(rmp, &job->reply_payload,
rsp_size, 0);
}
}
lpfc_free_bsg_buffers(phba, cmp);
lpfc_free_bsg_buffers(phba, rmp);
lpfc_mbuf_free(phba, bmp->virt, bmp->phys); lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
kfree(bmp);
lpfc_sli_release_iocbq(phba, cmdiocbq); lpfc_sli_release_iocbq(phba, cmdiocbq);
lpfc_nlp_put(ndlp); lpfc_nlp_put(ndlp);
kfree(bmp);
kfree(dd_data); kfree(dd_data);
/* make error code available to userspace */
/* Complete the job if the job is still active */
if (job) {
job->reply->result = rc; job->reply->result = rc;
/* complete the job back to userspace */
job->job_done(job); job->job_done(job);
spin_unlock_irqrestore(&phba->ct_ev_lock, flags); }
return; return;
} }
...@@ -240,12 +372,9 @@ lpfc_bsg_send_mgmt_cmd(struct fc_bsg_job *job) ...@@ -240,12 +372,9 @@ lpfc_bsg_send_mgmt_cmd(struct fc_bsg_job *job)
uint32_t timeout; uint32_t timeout;
struct lpfc_iocbq *cmdiocbq = NULL; struct lpfc_iocbq *cmdiocbq = NULL;
IOCB_t *cmd; IOCB_t *cmd;
struct lpfc_dmabuf *bmp = NULL; struct lpfc_dmabuf *bmp = NULL, *cmp = NULL, *rmp = NULL;
int request_nseg; int request_nseg;
int reply_nseg; int reply_nseg;
struct scatterlist *sgel = NULL;
int numbde;
dma_addr_t busaddr;
struct bsg_job_data *dd_data; struct bsg_job_data *dd_data;
uint32_t creg_val; uint32_t creg_val;
int rc = 0; int rc = 0;
...@@ -268,54 +397,50 @@ lpfc_bsg_send_mgmt_cmd(struct fc_bsg_job *job) ...@@ -268,54 +397,50 @@ lpfc_bsg_send_mgmt_cmd(struct fc_bsg_job *job)
goto no_ndlp; goto no_ndlp;
} }
bmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
if (!bmp) {
rc = -ENOMEM;
goto free_ndlp;
}
if (ndlp->nlp_flag & NLP_ELS_SND_MASK) { if (ndlp->nlp_flag & NLP_ELS_SND_MASK) {
rc = -ENODEV; rc = -ENODEV;
goto free_bmp; goto free_ndlp;
} }
cmdiocbq = lpfc_sli_get_iocbq(phba); cmdiocbq = lpfc_sli_get_iocbq(phba);
if (!cmdiocbq) { if (!cmdiocbq) {
rc = -ENOMEM; rc = -ENOMEM;
goto free_bmp; goto free_ndlp;
} }
cmd = &cmdiocbq->iocb; cmd = &cmdiocbq->iocb;
bmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
if (!bmp) {
rc = -ENOMEM;
goto free_cmdiocbq;
}
bmp->virt = lpfc_mbuf_alloc(phba, 0, &bmp->phys); bmp->virt = lpfc_mbuf_alloc(phba, 0, &bmp->phys);
if (!bmp->virt) { if (!bmp->virt) {
rc = -ENOMEM; rc = -ENOMEM;
goto free_cmdiocbq; goto free_bmp;
} }
INIT_LIST_HEAD(&bmp->list); INIT_LIST_HEAD(&bmp->list);
bpl = (struct ulp_bde64 *) bmp->virt; bpl = (struct ulp_bde64 *) bmp->virt;
request_nseg = pci_map_sg(phba->pcidev, job->request_payload.sg_list, request_nseg = LPFC_BPL_SIZE/sizeof(struct ulp_bde64);
job->request_payload.sg_cnt, DMA_TO_DEVICE); cmp = lpfc_alloc_bsg_buffers(phba, job->request_payload.payload_len,
for_each_sg(job->request_payload.sg_list, sgel, request_nseg, numbde) { 1, bpl, &request_nseg);
busaddr = sg_dma_address(sgel); if (!cmp) {
bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64; rc = -ENOMEM;
bpl->tus.f.bdeSize = sg_dma_len(sgel); goto free_bmp;
bpl->tus.w = cpu_to_le32(bpl->tus.w);
bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr));
bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr));
bpl++;
} }
lpfc_bsg_copy_data(cmp, &job->request_payload,
job->request_payload.payload_len, 1);
reply_nseg = pci_map_sg(phba->pcidev, job->reply_payload.sg_list, bpl += request_nseg;
job->reply_payload.sg_cnt, DMA_FROM_DEVICE); reply_nseg = LPFC_BPL_SIZE/sizeof(struct ulp_bde64) - request_nseg;
for_each_sg(job->reply_payload.sg_list, sgel, reply_nseg, numbde) { rmp = lpfc_alloc_bsg_buffers(phba, job->reply_payload.payload_len, 0,
busaddr = sg_dma_address(sgel); bpl, &reply_nseg);
bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I; if (!rmp) {
bpl->tus.f.bdeSize = sg_dma_len(sgel); rc = -ENOMEM;
bpl->tus.w = cpu_to_le32(bpl->tus.w); goto free_cmp;
bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr));
bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr));
bpl++;
} }
cmd->un.genreq64.bdl.ulpIoTag32 = 0; cmd->un.genreq64.bdl.ulpIoTag32 = 0;
...@@ -343,17 +468,20 @@ lpfc_bsg_send_mgmt_cmd(struct fc_bsg_job *job) ...@@ -343,17 +468,20 @@ lpfc_bsg_send_mgmt_cmd(struct fc_bsg_job *job)
cmd->ulpTimeout = timeout; cmd->ulpTimeout = timeout;
cmdiocbq->iocb_cmpl = lpfc_bsg_send_mgmt_cmd_cmp; cmdiocbq->iocb_cmpl = lpfc_bsg_send_mgmt_cmd_cmp;
cmdiocbq->context1 = ndlp; cmdiocbq->context1 = dd_data;
cmdiocbq->context2 = dd_data; cmdiocbq->context2 = cmp;
cmdiocbq->context3 = bmp;
dd_data->type = TYPE_IOCB; dd_data->type = TYPE_IOCB;
dd_data->set_job = job;
dd_data->context_un.iocb.cmdiocbq = cmdiocbq; dd_data->context_un.iocb.cmdiocbq = cmdiocbq;
dd_data->context_un.iocb.set_job = job; dd_data->context_un.iocb.ndlp = ndlp;
dd_data->context_un.iocb.bmp = bmp; dd_data->context_un.iocb.rmp = rmp;
job->dd_data = dd_data;
if (phba->cfg_poll & DISABLE_FCP_RING_INT) { if (phba->cfg_poll & DISABLE_FCP_RING_INT) {
if (lpfc_readl(phba->HCregaddr, &creg_val)) { if (lpfc_readl(phba->HCregaddr, &creg_val)) {
rc = -EIO ; rc = -EIO ;
goto free_cmdiocbq; goto free_rmp;
} }
creg_val |= (HC_R0INT_ENA << LPFC_FCP_RING); creg_val |= (HC_R0INT_ENA << LPFC_FCP_RING);
writel(creg_val, phba->HCregaddr); writel(creg_val, phba->HCregaddr);
...@@ -368,19 +496,18 @@ lpfc_bsg_send_mgmt_cmd(struct fc_bsg_job *job) ...@@ -368,19 +496,18 @@ lpfc_bsg_send_mgmt_cmd(struct fc_bsg_job *job)
else else
rc = -EIO; rc = -EIO;
/* iocb failed so cleanup */ /* iocb failed so cleanup */
pci_unmap_sg(phba->pcidev, job->request_payload.sg_list,
job->request_payload.sg_cnt, DMA_TO_DEVICE);
pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list,
job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
free_rmp:
lpfc_free_bsg_buffers(phba, rmp);
free_cmp:
lpfc_free_bsg_buffers(phba, cmp);
free_bmp:
if (bmp->virt)
lpfc_mbuf_free(phba, bmp->virt, bmp->phys); lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
kfree(bmp);
free_cmdiocbq: free_cmdiocbq:
lpfc_sli_release_iocbq(phba, cmdiocbq); lpfc_sli_release_iocbq(phba, cmdiocbq);
free_bmp:
kfree(bmp);
free_ndlp: free_ndlp:
lpfc_nlp_put(ndlp); lpfc_nlp_put(ndlp);
no_ndlp: no_ndlp:
...@@ -418,42 +545,43 @@ lpfc_bsg_rport_els_cmp(struct lpfc_hba *phba, ...@@ -418,42 +545,43 @@ lpfc_bsg_rport_els_cmp(struct lpfc_hba *phba,
struct fc_bsg_job *job; struct fc_bsg_job *job;
IOCB_t *rsp; IOCB_t *rsp;
struct lpfc_nodelist *ndlp; struct lpfc_nodelist *ndlp;
struct lpfc_dmabuf *pbuflist = NULL; struct lpfc_dmabuf *pcmd = NULL, *prsp = NULL;
struct fc_bsg_ctels_reply *els_reply; struct fc_bsg_ctels_reply *els_reply;
uint8_t *rjt_data; uint8_t *rjt_data;
unsigned long flags; unsigned long flags;
unsigned int rsp_size;
int rc = 0; int rc = 0;
spin_lock_irqsave(&phba->ct_ev_lock, flags);
dd_data = cmdiocbq->context1; dd_data = cmdiocbq->context1;
/* normal completion and timeout crossed paths, already done */ ndlp = dd_data->context_un.iocb.ndlp;
if (!dd_data) { cmdiocbq->context1 = ndlp;
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
return;
}
cmdiocbq->iocb_flag |= LPFC_IO_WAKE; /* Determine if job has been aborted */
if (cmdiocbq->context2 && rspiocbq) spin_lock_irqsave(&phba->ct_ev_lock, flags);
memcpy(&((struct lpfc_iocbq *)cmdiocbq->context2)->iocb, job = dd_data->set_job;
&rspiocbq->iocb, sizeof(IOCB_t)); if (job) {
/* Prevent timeout handling from trying to abort job */
job->dd_data = NULL;
}
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
job = dd_data->context_un.iocb.set_job;
cmdiocbq = dd_data->context_un.iocb.cmdiocbq;
rspiocbq = dd_data->context_un.iocb.rspiocbq;
rsp = &rspiocbq->iocb; rsp = &rspiocbq->iocb;
ndlp = dd_data->context_un.iocb.ndlp; pcmd = (struct lpfc_dmabuf *)cmdiocbq->context2;
prsp = (struct lpfc_dmabuf *)pcmd->list.next;
pci_unmap_sg(phba->pcidev, job->request_payload.sg_list, /* Copy the completed job data or determine the job status if job is
job->request_payload.sg_cnt, DMA_TO_DEVICE); * still active
pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list, */
job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
if (job->reply->result == -EAGAIN) if (job) {
rc = -EAGAIN; if (rsp->ulpStatus == IOSTAT_SUCCESS) {
else if (rsp->ulpStatus == IOSTAT_SUCCESS) rsp_size = rsp->un.elsreq64.bdl.bdeSize;
job->reply->reply_payload_rcv_len = job->reply->reply_payload_rcv_len =
rsp->un.elsreq64.bdl.bdeSize; sg_copy_from_buffer(job->reply_payload.sg_list,
else if (rsp->ulpStatus == IOSTAT_LS_RJT) { job->reply_payload.sg_cnt,
prsp->virt,
rsp_size);
} else if (rsp->ulpStatus == IOSTAT_LS_RJT) {
job->reply->reply_payload_rcv_len = job->reply->reply_payload_rcv_len =
sizeof(struct fc_bsg_ctels_reply); sizeof(struct fc_bsg_ctels_reply);
/* LS_RJT data returned in word 4 */ /* LS_RJT data returned in word 4 */
...@@ -464,21 +592,21 @@ lpfc_bsg_rport_els_cmp(struct lpfc_hba *phba, ...@@ -464,21 +592,21 @@ lpfc_bsg_rport_els_cmp(struct lpfc_hba *phba,
els_reply->rjt_data.reason_code = rjt_data[2]; els_reply->rjt_data.reason_code = rjt_data[2];
els_reply->rjt_data.reason_explanation = rjt_data[1]; els_reply->rjt_data.reason_explanation = rjt_data[1];
els_reply->rjt_data.vendor_unique = rjt_data[0]; els_reply->rjt_data.vendor_unique = rjt_data[0];
} else } else {
rc = -EIO; rc = -EIO;
}
}
pbuflist = (struct lpfc_dmabuf *) cmdiocbq->context3;
lpfc_mbuf_free(phba, pbuflist->virt, pbuflist->phys);
lpfc_sli_release_iocbq(phba, rspiocbq);
lpfc_sli_release_iocbq(phba, cmdiocbq);
lpfc_nlp_put(ndlp); lpfc_nlp_put(ndlp);
lpfc_els_free_iocb(phba, cmdiocbq);
kfree(dd_data); kfree(dd_data);
/* make error code available to userspace */
/* Complete the job if the job is still active */
if (job) {
job->reply->result = rc; job->reply->result = rc;
job->dd_data = NULL;
/* complete the job back to userspace */
job->job_done(job); job->job_done(job);
spin_unlock_irqrestore(&phba->ct_ev_lock, flags); }
return; return;
} }
...@@ -496,19 +624,8 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job) ...@@ -496,19 +624,8 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job)
uint32_t elscmd; uint32_t elscmd;
uint32_t cmdsize; uint32_t cmdsize;
uint32_t rspsize; uint32_t rspsize;
struct lpfc_iocbq *rspiocbq;
struct lpfc_iocbq *cmdiocbq; struct lpfc_iocbq *cmdiocbq;
IOCB_t *rsp;
uint16_t rpi = 0; uint16_t rpi = 0;
struct lpfc_dmabuf *pcmd;
struct lpfc_dmabuf *prsp;
struct lpfc_dmabuf *pbuflist = NULL;
struct ulp_bde64 *bpl;
int request_nseg;
int reply_nseg;
struct scatterlist *sgel = NULL;
int numbde;
dma_addr_t busaddr;
struct bsg_job_data *dd_data; struct bsg_job_data *dd_data;
uint32_t creg_val; uint32_t creg_val;
int rc = 0; int rc = 0;
...@@ -516,6 +633,15 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job) ...@@ -516,6 +633,15 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job)
/* in case no data is transferred */ /* in case no data is transferred */
job->reply->reply_payload_rcv_len = 0; job->reply->reply_payload_rcv_len = 0;
/* verify the els command is not greater than the
* maximum ELS transfer size.
*/
if (job->request_payload.payload_len > FCELSSIZE) {
rc = -EINVAL;
goto no_dd_data;
}
/* allocate our bsg tracking structure */ /* allocate our bsg tracking structure */
dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL); dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
if (!dd_data) { if (!dd_data) {
...@@ -525,88 +651,51 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job) ...@@ -525,88 +651,51 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job)
goto no_dd_data; goto no_dd_data;
} }
if (!lpfc_nlp_get(ndlp)) {
rc = -ENODEV;
goto free_dd_data;
}
elscmd = job->request->rqst_data.r_els.els_code; elscmd = job->request->rqst_data.r_els.els_code;
cmdsize = job->request_payload.payload_len; cmdsize = job->request_payload.payload_len;
rspsize = job->reply_payload.payload_len; rspsize = job->reply_payload.payload_len;
rspiocbq = lpfc_sli_get_iocbq(phba);
if (!rspiocbq) { if (!lpfc_nlp_get(ndlp)) {
lpfc_nlp_put(ndlp); rc = -ENODEV;
rc = -ENOMEM;
goto free_dd_data; goto free_dd_data;
} }
rsp = &rspiocbq->iocb; /* We will use the allocated dma buffers by prep els iocb for command
rpi = ndlp->nlp_rpi; * and response to ensure if the job times out and the request is freed,
* we won't be dma into memory that is no longer allocated to for the
* request.
*/
cmdiocbq = lpfc_prep_els_iocb(vport, 1, cmdsize, 0, ndlp, cmdiocbq = lpfc_prep_els_iocb(vport, 1, cmdsize, 0, ndlp,
ndlp->nlp_DID, elscmd); ndlp->nlp_DID, elscmd);
if (!cmdiocbq) { if (!cmdiocbq) {
rc = -EIO; rc = -EIO;
goto free_rspiocbq; goto release_ndlp;
} }
/* prep els iocb set context1 to the ndlp, context2 to the command rpi = ndlp->nlp_rpi;
* dmabuf, context3 holds the data dmabuf
*/ /* Transfer the request payload to allocated command dma buffer */
pcmd = (struct lpfc_dmabuf *) cmdiocbq->context2;
prsp = (struct lpfc_dmabuf *) pcmd->list.next; sg_copy_to_buffer(job->request_payload.sg_list,
lpfc_mbuf_free(phba, pcmd->virt, pcmd->phys); job->request_payload.sg_cnt,
kfree(pcmd); ((struct lpfc_dmabuf *)cmdiocbq->context2)->virt,
lpfc_mbuf_free(phba, prsp->virt, prsp->phys); cmdsize);
kfree(prsp);
cmdiocbq->context2 = NULL;
pbuflist = (struct lpfc_dmabuf *) cmdiocbq->context3;
bpl = (struct ulp_bde64 *) pbuflist->virt;
request_nseg = pci_map_sg(phba->pcidev, job->request_payload.sg_list,
job->request_payload.sg_cnt, DMA_TO_DEVICE);
for_each_sg(job->request_payload.sg_list, sgel, request_nseg, numbde) {
busaddr = sg_dma_address(sgel);
bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
bpl->tus.f.bdeSize = sg_dma_len(sgel);
bpl->tus.w = cpu_to_le32(bpl->tus.w);
bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr));
bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr));
bpl++;
}
reply_nseg = pci_map_sg(phba->pcidev, job->reply_payload.sg_list,
job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
for_each_sg(job->reply_payload.sg_list, sgel, reply_nseg, numbde) {
busaddr = sg_dma_address(sgel);
bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
bpl->tus.f.bdeSize = sg_dma_len(sgel);
bpl->tus.w = cpu_to_le32(bpl->tus.w);
bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr));
bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr));
bpl++;
}
cmdiocbq->iocb.un.elsreq64.bdl.bdeSize =
(request_nseg + reply_nseg) * sizeof(struct ulp_bde64);
if (phba->sli_rev == LPFC_SLI_REV4) if (phba->sli_rev == LPFC_SLI_REV4)
cmdiocbq->iocb.ulpContext = phba->sli4_hba.rpi_ids[rpi]; cmdiocbq->iocb.ulpContext = phba->sli4_hba.rpi_ids[rpi];
else else
cmdiocbq->iocb.ulpContext = rpi; cmdiocbq->iocb.ulpContext = rpi;
cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC; cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC;
cmdiocbq->context1 = NULL;
cmdiocbq->context2 = NULL;
cmdiocbq->iocb_cmpl = lpfc_bsg_rport_els_cmp;
cmdiocbq->context1 = dd_data; cmdiocbq->context1 = dd_data;
cmdiocbq->context_un.ndlp = ndlp; cmdiocbq->context_un.ndlp = ndlp;
cmdiocbq->context2 = rspiocbq; cmdiocbq->iocb_cmpl = lpfc_bsg_rport_els_cmp;
dd_data->type = TYPE_IOCB; dd_data->type = TYPE_IOCB;
dd_data->set_job = job;
dd_data->context_un.iocb.cmdiocbq = cmdiocbq; dd_data->context_un.iocb.cmdiocbq = cmdiocbq;
dd_data->context_un.iocb.rspiocbq = rspiocbq;
dd_data->context_un.iocb.set_job = job;
dd_data->context_un.iocb.bmp = NULL;
dd_data->context_un.iocb.ndlp = ndlp; dd_data->context_un.iocb.ndlp = ndlp;
dd_data->context_un.iocb.rmp = NULL;
job->dd_data = dd_data;
if (phba->cfg_poll & DISABLE_FCP_RING_INT) { if (phba->cfg_poll & DISABLE_FCP_RING_INT) {
if (lpfc_readl(phba->HCregaddr, &creg_val)) { if (lpfc_readl(phba->HCregaddr, &creg_val)) {
...@@ -617,8 +706,9 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job) ...@@ -617,8 +706,9 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job)
writel(creg_val, phba->HCregaddr); writel(creg_val, phba->HCregaddr);
readl(phba->HCregaddr); /* flush */ readl(phba->HCregaddr); /* flush */
} }
rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq, 0); rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq, 0);
lpfc_nlp_put(ndlp);
if (rc == IOCB_SUCCESS) if (rc == IOCB_SUCCESS)
return 0; /* done for now */ return 0; /* done for now */
else if (rc == IOCB_BUSY) else if (rc == IOCB_BUSY)
...@@ -627,17 +717,12 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job) ...@@ -627,17 +717,12 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job)
rc = -EIO; rc = -EIO;
linkdown_err: linkdown_err:
pci_unmap_sg(phba->pcidev, job->request_payload.sg_list,
job->request_payload.sg_cnt, DMA_TO_DEVICE);
pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list,
job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
lpfc_mbuf_free(phba, pbuflist->virt, pbuflist->phys); cmdiocbq->context1 = ndlp;
lpfc_els_free_iocb(phba, cmdiocbq);
lpfc_sli_release_iocbq(phba, cmdiocbq);
free_rspiocbq: release_ndlp:
lpfc_sli_release_iocbq(phba, rspiocbq); lpfc_nlp_put(ndlp);
free_dd_data: free_dd_data:
kfree(dd_data); kfree(dd_data);
...@@ -680,6 +765,7 @@ lpfc_bsg_event_free(struct kref *kref) ...@@ -680,6 +765,7 @@ lpfc_bsg_event_free(struct kref *kref)
kfree(ed); kfree(ed);
} }
kfree(evt->dd_data);
kfree(evt); kfree(evt);
} }
...@@ -723,6 +809,7 @@ lpfc_bsg_event_new(uint32_t ev_mask, int ev_reg_id, uint32_t ev_req_id) ...@@ -723,6 +809,7 @@ lpfc_bsg_event_new(uint32_t ev_mask, int ev_reg_id, uint32_t ev_req_id)
evt->req_id = ev_req_id; evt->req_id = ev_req_id;
evt->reg_id = ev_reg_id; evt->reg_id = ev_reg_id;
evt->wait_time_stamp = jiffies; evt->wait_time_stamp = jiffies;
evt->dd_data = NULL;
init_waitqueue_head(&evt->wq); init_waitqueue_head(&evt->wq);
kref_init(&evt->kref); kref_init(&evt->kref);
return evt; return evt;
...@@ -790,6 +877,7 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, ...@@ -790,6 +877,7 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
struct lpfc_hbq_entry *hbqe; struct lpfc_hbq_entry *hbqe;
struct lpfc_sli_ct_request *ct_req; struct lpfc_sli_ct_request *ct_req;
struct fc_bsg_job *job = NULL; struct fc_bsg_job *job = NULL;
struct bsg_job_data *dd_data = NULL;
unsigned long flags; unsigned long flags;
int size = 0; int size = 0;
...@@ -986,10 +1074,11 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, ...@@ -986,10 +1074,11 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
} }
list_move(evt->events_to_see.prev, &evt->events_to_get); list_move(evt->events_to_see.prev, &evt->events_to_get);
lpfc_bsg_event_unref(evt);
job = evt->set_job; dd_data = (struct bsg_job_data *)evt->dd_data;
evt->set_job = NULL; job = dd_data->set_job;
dd_data->set_job = NULL;
lpfc_bsg_event_unref(evt);
if (job) { if (job) {
job->reply->reply_payload_rcv_len = size; job->reply->reply_payload_rcv_len = size;
/* make error code available to userspace */ /* make error code available to userspace */
...@@ -1078,14 +1167,6 @@ lpfc_bsg_hba_set_event(struct fc_bsg_job *job) ...@@ -1078,14 +1167,6 @@ lpfc_bsg_hba_set_event(struct fc_bsg_job *job)
goto job_error; goto job_error;
} }
dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
if (dd_data == NULL) {
lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
"2734 Failed allocation of dd_data\n");
rc = -ENOMEM;
goto job_error;
}
event_req = (struct set_ct_event *) event_req = (struct set_ct_event *)
job->request->rqst_data.h_vendor.vendor_cmd; job->request->rqst_data.h_vendor.vendor_cmd;
ev_mask = ((uint32_t)(unsigned long)event_req->type_mask & ev_mask = ((uint32_t)(unsigned long)event_req->type_mask &
...@@ -1095,6 +1176,7 @@ lpfc_bsg_hba_set_event(struct fc_bsg_job *job) ...@@ -1095,6 +1176,7 @@ lpfc_bsg_hba_set_event(struct fc_bsg_job *job)
if (evt->reg_id == event_req->ev_reg_id) { if (evt->reg_id == event_req->ev_reg_id) {
lpfc_bsg_event_ref(evt); lpfc_bsg_event_ref(evt);
evt->wait_time_stamp = jiffies; evt->wait_time_stamp = jiffies;
dd_data = (struct bsg_job_data *)evt->dd_data;
break; break;
} }
} }
...@@ -1102,6 +1184,13 @@ lpfc_bsg_hba_set_event(struct fc_bsg_job *job) ...@@ -1102,6 +1184,13 @@ lpfc_bsg_hba_set_event(struct fc_bsg_job *job)
if (&evt->node == &phba->ct_ev_waiters) { if (&evt->node == &phba->ct_ev_waiters) {
/* no event waiting struct yet - first call */ /* no event waiting struct yet - first call */
dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
if (dd_data == NULL) {
lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
"2734 Failed allocation of dd_data\n");
rc = -ENOMEM;
goto job_error;
}
evt = lpfc_bsg_event_new(ev_mask, event_req->ev_reg_id, evt = lpfc_bsg_event_new(ev_mask, event_req->ev_reg_id,
event_req->ev_req_id); event_req->ev_req_id);
if (!evt) { if (!evt) {
...@@ -1111,7 +1200,10 @@ lpfc_bsg_hba_set_event(struct fc_bsg_job *job) ...@@ -1111,7 +1200,10 @@ lpfc_bsg_hba_set_event(struct fc_bsg_job *job)
rc = -ENOMEM; rc = -ENOMEM;
goto job_error; goto job_error;
} }
dd_data->type = TYPE_EVT;
dd_data->set_job = NULL;
dd_data->context_un.evt = evt;
evt->dd_data = (void *)dd_data;
spin_lock_irqsave(&phba->ct_ev_lock, flags); spin_lock_irqsave(&phba->ct_ev_lock, flags);
list_add(&evt->node, &phba->ct_ev_waiters); list_add(&evt->node, &phba->ct_ev_waiters);
lpfc_bsg_event_ref(evt); lpfc_bsg_event_ref(evt);
...@@ -1121,9 +1213,7 @@ lpfc_bsg_hba_set_event(struct fc_bsg_job *job) ...@@ -1121,9 +1213,7 @@ lpfc_bsg_hba_set_event(struct fc_bsg_job *job)
spin_lock_irqsave(&phba->ct_ev_lock, flags); spin_lock_irqsave(&phba->ct_ev_lock, flags);
evt->waiting = 1; evt->waiting = 1;
dd_data->type = TYPE_EVT; dd_data->set_job = job; /* for unsolicited command */
dd_data->context_un.evt = evt;
evt->set_job = job; /* for unsolicited command */
job->dd_data = dd_data; /* for fc transport timeout callback*/ job->dd_data = dd_data; /* for fc transport timeout callback*/
spin_unlock_irqrestore(&phba->ct_ev_lock, flags); spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
return 0; /* call job done later */ return 0; /* call job done later */
...@@ -1252,27 +1342,30 @@ lpfc_issue_ct_rsp_cmp(struct lpfc_hba *phba, ...@@ -1252,27 +1342,30 @@ lpfc_issue_ct_rsp_cmp(struct lpfc_hba *phba,
struct bsg_job_data *dd_data; struct bsg_job_data *dd_data;
struct fc_bsg_job *job; struct fc_bsg_job *job;
IOCB_t *rsp; IOCB_t *rsp;
struct lpfc_dmabuf *bmp; struct lpfc_dmabuf *bmp, *cmp;
struct lpfc_nodelist *ndlp; struct lpfc_nodelist *ndlp;
unsigned long flags; unsigned long flags;
int rc = 0; int rc = 0;
dd_data = cmdiocbq->context1;
/* Determine if job has been aborted */
spin_lock_irqsave(&phba->ct_ev_lock, flags); spin_lock_irqsave(&phba->ct_ev_lock, flags);
dd_data = cmdiocbq->context2; job = dd_data->set_job;
/* normal completion and timeout crossed paths, already done */ if (job) {
if (!dd_data) { /* Prevent timeout handling from trying to abort job */
spin_unlock_irqrestore(&phba->ct_ev_lock, flags); job->dd_data = NULL;
return;
} }
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
job = dd_data->context_un.iocb.set_job;
bmp = dd_data->context_un.iocb.bmp;
rsp = &rspiocbq->iocb;
ndlp = dd_data->context_un.iocb.ndlp; ndlp = dd_data->context_un.iocb.ndlp;
cmp = cmdiocbq->context2;
bmp = cmdiocbq->context3;
rsp = &rspiocbq->iocb;
pci_unmap_sg(phba->pcidev, job->request_payload.sg_list, /* Copy the completed job data or set the error status */
job->request_payload.sg_cnt, DMA_TO_DEVICE);
if (job) {
if (rsp->ulpStatus) { if (rsp->ulpStatus) {
if (rsp->ulpStatus == IOSTAT_LOCAL_REJECT) { if (rsp->ulpStatus == IOSTAT_LOCAL_REJECT) {
switch (rsp->un.ulpWord[4] & IOERR_PARAM_MASK) { switch (rsp->un.ulpWord[4] & IOERR_PARAM_MASK) {
...@@ -1286,23 +1379,27 @@ lpfc_issue_ct_rsp_cmp(struct lpfc_hba *phba, ...@@ -1286,23 +1379,27 @@ lpfc_issue_ct_rsp_cmp(struct lpfc_hba *phba,
rc = -EACCES; rc = -EACCES;
break; break;
} }
} else } else {
rc = -EACCES; rc = -EACCES;
} else }
job->reply->reply_payload_rcv_len = } else {
rsp->un.genreq64.bdl.bdeSize; job->reply->reply_payload_rcv_len = 0;
}
}
lpfc_free_bsg_buffers(phba, cmp);
lpfc_mbuf_free(phba, bmp->virt, bmp->phys); lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
kfree(bmp);
lpfc_sli_release_iocbq(phba, cmdiocbq); lpfc_sli_release_iocbq(phba, cmdiocbq);
lpfc_nlp_put(ndlp); lpfc_nlp_put(ndlp);
kfree(bmp);
kfree(dd_data); kfree(dd_data);
/* make error code available to userspace */
/* Complete the job if the job is still active */
if (job) {
job->reply->result = rc; job->reply->result = rc;
job->dd_data = NULL;
/* complete the job back to userspace */
job->job_done(job); job->job_done(job);
spin_unlock_irqrestore(&phba->ct_ev_lock, flags); }
return; return;
} }
...@@ -1316,7 +1413,8 @@ lpfc_issue_ct_rsp_cmp(struct lpfc_hba *phba, ...@@ -1316,7 +1413,8 @@ lpfc_issue_ct_rsp_cmp(struct lpfc_hba *phba,
**/ **/
static int static int
lpfc_issue_ct_rsp(struct lpfc_hba *phba, struct fc_bsg_job *job, uint32_t tag, lpfc_issue_ct_rsp(struct lpfc_hba *phba, struct fc_bsg_job *job, uint32_t tag,
struct lpfc_dmabuf *bmp, int num_entry) struct lpfc_dmabuf *cmp, struct lpfc_dmabuf *bmp,
int num_entry)
{ {
IOCB_t *icmd; IOCB_t *icmd;
struct lpfc_iocbq *ctiocb = NULL; struct lpfc_iocbq *ctiocb = NULL;
...@@ -1377,7 +1475,7 @@ lpfc_issue_ct_rsp(struct lpfc_hba *phba, struct fc_bsg_job *job, uint32_t tag, ...@@ -1377,7 +1475,7 @@ lpfc_issue_ct_rsp(struct lpfc_hba *phba, struct fc_bsg_job *job, uint32_t tag,
/* Check if the ndlp is active */ /* Check if the ndlp is active */
if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) { if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) {
rc = -IOCB_ERROR; rc = IOCB_ERROR;
goto issue_ct_rsp_exit; goto issue_ct_rsp_exit;
} }
...@@ -1385,7 +1483,7 @@ lpfc_issue_ct_rsp(struct lpfc_hba *phba, struct fc_bsg_job *job, uint32_t tag, ...@@ -1385,7 +1483,7 @@ lpfc_issue_ct_rsp(struct lpfc_hba *phba, struct fc_bsg_job *job, uint32_t tag,
* we respond * we respond
*/ */
if (!lpfc_nlp_get(ndlp)) { if (!lpfc_nlp_get(ndlp)) {
rc = -IOCB_ERROR; rc = IOCB_ERROR;
goto issue_ct_rsp_exit; goto issue_ct_rsp_exit;
} }
...@@ -1407,17 +1505,17 @@ lpfc_issue_ct_rsp(struct lpfc_hba *phba, struct fc_bsg_job *job, uint32_t tag, ...@@ -1407,17 +1505,17 @@ lpfc_issue_ct_rsp(struct lpfc_hba *phba, struct fc_bsg_job *job, uint32_t tag,
ctiocb->iocb_cmpl = NULL; ctiocb->iocb_cmpl = NULL;
ctiocb->iocb_flag |= LPFC_IO_LIBDFC; ctiocb->iocb_flag |= LPFC_IO_LIBDFC;
ctiocb->vport = phba->pport; ctiocb->vport = phba->pport;
ctiocb->context1 = dd_data;
ctiocb->context2 = cmp;
ctiocb->context3 = bmp; ctiocb->context3 = bmp;
ctiocb->iocb_cmpl = lpfc_issue_ct_rsp_cmp; ctiocb->iocb_cmpl = lpfc_issue_ct_rsp_cmp;
ctiocb->context2 = dd_data;
ctiocb->context1 = ndlp;
dd_data->type = TYPE_IOCB; dd_data->type = TYPE_IOCB;
dd_data->set_job = job;
dd_data->context_un.iocb.cmdiocbq = ctiocb; dd_data->context_un.iocb.cmdiocbq = ctiocb;
dd_data->context_un.iocb.rspiocbq = NULL;
dd_data->context_un.iocb.set_job = job;
dd_data->context_un.iocb.bmp = bmp;
dd_data->context_un.iocb.ndlp = ndlp; dd_data->context_un.iocb.ndlp = ndlp;
dd_data->context_un.iocb.rmp = NULL;
job->dd_data = dd_data;
if (phba->cfg_poll & DISABLE_FCP_RING_INT) { if (phba->cfg_poll & DISABLE_FCP_RING_INT) {
if (lpfc_readl(phba->HCregaddr, &creg_val)) { if (lpfc_readl(phba->HCregaddr, &creg_val)) {
...@@ -1454,11 +1552,8 @@ lpfc_bsg_send_mgmt_rsp(struct fc_bsg_job *job) ...@@ -1454,11 +1552,8 @@ lpfc_bsg_send_mgmt_rsp(struct fc_bsg_job *job)
struct send_mgmt_resp *mgmt_resp = (struct send_mgmt_resp *) struct send_mgmt_resp *mgmt_resp = (struct send_mgmt_resp *)
job->request->rqst_data.h_vendor.vendor_cmd; job->request->rqst_data.h_vendor.vendor_cmd;
struct ulp_bde64 *bpl; struct ulp_bde64 *bpl;
struct lpfc_dmabuf *bmp = NULL; struct lpfc_dmabuf *bmp = NULL, *cmp = NULL;
struct scatterlist *sgel = NULL; int bpl_entries;
int request_nseg;
int numbde;
dma_addr_t busaddr;
uint32_t tag = mgmt_resp->tag; uint32_t tag = mgmt_resp->tag;
unsigned long reqbfrcnt = unsigned long reqbfrcnt =
(unsigned long)job->request_payload.payload_len; (unsigned long)job->request_payload.payload_len;
...@@ -1486,30 +1581,28 @@ lpfc_bsg_send_mgmt_rsp(struct fc_bsg_job *job) ...@@ -1486,30 +1581,28 @@ lpfc_bsg_send_mgmt_rsp(struct fc_bsg_job *job)
INIT_LIST_HEAD(&bmp->list); INIT_LIST_HEAD(&bmp->list);
bpl = (struct ulp_bde64 *) bmp->virt; bpl = (struct ulp_bde64 *) bmp->virt;
request_nseg = pci_map_sg(phba->pcidev, job->request_payload.sg_list, bpl_entries = (LPFC_BPL_SIZE/sizeof(struct ulp_bde64));
job->request_payload.sg_cnt, DMA_TO_DEVICE); cmp = lpfc_alloc_bsg_buffers(phba, job->request_payload.payload_len,
for_each_sg(job->request_payload.sg_list, sgel, request_nseg, numbde) { 1, bpl, &bpl_entries);
busaddr = sg_dma_address(sgel); if (!cmp) {
bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64; rc = -ENOMEM;
bpl->tus.f.bdeSize = sg_dma_len(sgel); goto send_mgmt_rsp_free_bmp;
bpl->tus.w = cpu_to_le32(bpl->tus.w);
bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr));
bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr));
bpl++;
} }
lpfc_bsg_copy_data(cmp, &job->request_payload,
job->request_payload.payload_len, 1);
rc = lpfc_issue_ct_rsp(phba, job, tag, bmp, request_nseg); rc = lpfc_issue_ct_rsp(phba, job, tag, cmp, bmp, bpl_entries);
if (rc == IOCB_SUCCESS) if (rc == IOCB_SUCCESS)
return 0; /* done for now */ return 0; /* done for now */
/* TBD need to handle a timeout */
pci_unmap_sg(phba->pcidev, job->request_payload.sg_list,
job->request_payload.sg_cnt, DMA_TO_DEVICE);
rc = -EACCES; rc = -EACCES;
lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
lpfc_free_bsg_buffers(phba, cmp);
send_mgmt_rsp_free_bmp: send_mgmt_rsp_free_bmp:
if (bmp->virt)
lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
kfree(bmp); kfree(bmp);
send_mgmt_rsp_exit: send_mgmt_rsp_exit:
/* make error code available to userspace */ /* make error code available to userspace */
...@@ -3193,13 +3286,7 @@ lpfc_bsg_issue_mbox_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) ...@@ -3193,13 +3286,7 @@ lpfc_bsg_issue_mbox_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
unsigned long flags; unsigned long flags;
uint8_t *pmb, *pmb_buf; uint8_t *pmb, *pmb_buf;
spin_lock_irqsave(&phba->ct_ev_lock, flags);
dd_data = pmboxq->context1; dd_data = pmboxq->context1;
/* job already timed out? */
if (!dd_data) {
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
return;
}
/* /*
* The outgoing buffer is readily referred from the dma buffer, * The outgoing buffer is readily referred from the dma buffer,
...@@ -3209,29 +3296,33 @@ lpfc_bsg_issue_mbox_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) ...@@ -3209,29 +3296,33 @@ lpfc_bsg_issue_mbox_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
pmb_buf = (uint8_t *)dd_data->context_un.mbox.mb; pmb_buf = (uint8_t *)dd_data->context_un.mbox.mb;
memcpy(pmb_buf, pmb, sizeof(MAILBOX_t)); memcpy(pmb_buf, pmb, sizeof(MAILBOX_t));
job = dd_data->context_un.mbox.set_job; /* Determine if job has been aborted */
spin_lock_irqsave(&phba->ct_ev_lock, flags);
job = dd_data->set_job;
if (job) {
/* Prevent timeout handling from trying to abort job */
job->dd_data = NULL;
}
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
/* Copy the mailbox data to the job if it is still active */
if (job) { if (job) {
size = job->reply_payload.payload_len; size = job->reply_payload.payload_len;
job->reply->reply_payload_rcv_len = job->reply->reply_payload_rcv_len =
sg_copy_from_buffer(job->reply_payload.sg_list, sg_copy_from_buffer(job->reply_payload.sg_list,
job->reply_payload.sg_cnt, job->reply_payload.sg_cnt,
pmb_buf, size); pmb_buf, size);
/* need to hold the lock until we set job->dd_data to NULL
* to hold off the timeout handler returning to the mid-layer
* while we are still processing the job.
*/
job->dd_data = NULL;
dd_data->context_un.mbox.set_job = NULL;
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
} else {
dd_data->context_un.mbox.set_job = NULL;
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
} }
dd_data->set_job = NULL;
mempool_free(dd_data->context_un.mbox.pmboxq, phba->mbox_mem_pool); mempool_free(dd_data->context_un.mbox.pmboxq, phba->mbox_mem_pool);
lpfc_bsg_dma_page_free(phba, dd_data->context_un.mbox.dmabuffers); lpfc_bsg_dma_page_free(phba, dd_data->context_un.mbox.dmabuffers);
kfree(dd_data); kfree(dd_data);
/* Complete the job if the job is still active */
if (job) { if (job) {
job->reply->result = 0; job->reply->result = 0;
job->job_done(job); job->job_done(job);
...@@ -3377,19 +3468,22 @@ lpfc_bsg_issue_mbox_ext_handle_job(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) ...@@ -3377,19 +3468,22 @@ lpfc_bsg_issue_mbox_ext_handle_job(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
struct lpfc_sli_config_mbox *sli_cfg_mbx; struct lpfc_sli_config_mbox *sli_cfg_mbx;
uint8_t *pmbx; uint8_t *pmbx;
spin_lock_irqsave(&phba->ct_ev_lock, flags);
dd_data = pmboxq->context1; dd_data = pmboxq->context1;
/* has the job already timed out? */
if (!dd_data) { /* Determine if job has been aborted */
spin_unlock_irqrestore(&phba->ct_ev_lock, flags); spin_lock_irqsave(&phba->ct_ev_lock, flags);
job = NULL; job = dd_data->set_job;
goto job_done_out; if (job) {
/* Prevent timeout handling from trying to abort job */
job->dd_data = NULL;
} }
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
/* /*
* The outgoing buffer is readily referred from the dma buffer, * The outgoing buffer is readily referred from the dma buffer,
* just need to get header part from mailboxq structure. * just need to get header part from mailboxq structure.
*/ */
pmb = (uint8_t *)&pmboxq->u.mb; pmb = (uint8_t *)&pmboxq->u.mb;
pmb_buf = (uint8_t *)dd_data->context_un.mbox.mb; pmb_buf = (uint8_t *)dd_data->context_un.mbox.mb;
/* Copy the byte swapped response mailbox back to the user */ /* Copy the byte swapped response mailbox back to the user */
...@@ -3406,21 +3500,18 @@ lpfc_bsg_issue_mbox_ext_handle_job(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) ...@@ -3406,21 +3500,18 @@ lpfc_bsg_issue_mbox_ext_handle_job(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
sli_cfg_mbx->un.sli_config_emb0_subsys.mse[0].buf_len); sli_cfg_mbx->un.sli_config_emb0_subsys.mse[0].buf_len);
} }
job = dd_data->context_un.mbox.set_job; /* Complete the job if the job is still active */
if (job) { if (job) {
size = job->reply_payload.payload_len; size = job->reply_payload.payload_len;
job->reply->reply_payload_rcv_len = job->reply->reply_payload_rcv_len =
sg_copy_from_buffer(job->reply_payload.sg_list, sg_copy_from_buffer(job->reply_payload.sg_list,
job->reply_payload.sg_cnt, job->reply_payload.sg_cnt,
pmb_buf, size); pmb_buf, size);
/* result for successful */ /* result for successful */
job->reply->result = 0; job->reply->result = 0;
job->dd_data = NULL;
/* need to hold the lock util we set job->dd_data to NULL
* to hold off the timeout handler from midlayer to take
* any action.
*/
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
"2937 SLI_CONFIG ext-buffer maibox command " "2937 SLI_CONFIG ext-buffer maibox command "
"(x%x/x%x) complete bsg job done, bsize:%d\n", "(x%x/x%x) complete bsg job done, bsize:%d\n",
...@@ -3431,20 +3522,18 @@ lpfc_bsg_issue_mbox_ext_handle_job(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) ...@@ -3431,20 +3522,18 @@ lpfc_bsg_issue_mbox_ext_handle_job(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
phba->mbox_ext_buf_ctx.mboxType, phba->mbox_ext_buf_ctx.mboxType,
dma_ebuf, sta_pos_addr, dma_ebuf, sta_pos_addr,
phba->mbox_ext_buf_ctx.mbx_dmabuf, 0); phba->mbox_ext_buf_ctx.mbx_dmabuf, 0);
} else } else {
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
job_done_out:
if (!job)
lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC, lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
"2938 SLI_CONFIG ext-buffer maibox " "2938 SLI_CONFIG ext-buffer maibox "
"command (x%x/x%x) failure, rc:x%x\n", "command (x%x/x%x) failure, rc:x%x\n",
phba->mbox_ext_buf_ctx.nembType, phba->mbox_ext_buf_ctx.nembType,
phba->mbox_ext_buf_ctx.mboxType, rc); phba->mbox_ext_buf_ctx.mboxType, rc);
}
/* state change */ /* state change */
phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_DONE; phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_DONE;
kfree(dd_data); kfree(dd_data);
return job; return job;
} }
...@@ -3461,8 +3550,10 @@ lpfc_bsg_issue_read_mbox_ext_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) ...@@ -3461,8 +3550,10 @@ lpfc_bsg_issue_read_mbox_ext_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
{ {
struct fc_bsg_job *job; struct fc_bsg_job *job;
job = lpfc_bsg_issue_mbox_ext_handle_job(phba, pmboxq);
/* handle the BSG job with mailbox command */ /* handle the BSG job with mailbox command */
if (phba->mbox_ext_buf_ctx.state == LPFC_BSG_MBOX_ABTS) if (!job)
pmboxq->u.mb.mbxStatus = MBXERR_ERROR; pmboxq->u.mb.mbxStatus = MBXERR_ERROR;
lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
...@@ -3470,15 +3561,13 @@ lpfc_bsg_issue_read_mbox_ext_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) ...@@ -3470,15 +3561,13 @@ lpfc_bsg_issue_read_mbox_ext_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
"complete, ctxState:x%x, mbxStatus:x%x\n", "complete, ctxState:x%x, mbxStatus:x%x\n",
phba->mbox_ext_buf_ctx.state, pmboxq->u.mb.mbxStatus); phba->mbox_ext_buf_ctx.state, pmboxq->u.mb.mbxStatus);
job = lpfc_bsg_issue_mbox_ext_handle_job(phba, pmboxq);
if (pmboxq->u.mb.mbxStatus || phba->mbox_ext_buf_ctx.numBuf == 1) if (pmboxq->u.mb.mbxStatus || phba->mbox_ext_buf_ctx.numBuf == 1)
lpfc_bsg_mbox_ext_session_reset(phba); lpfc_bsg_mbox_ext_session_reset(phba);
/* free base driver mailbox structure memory */ /* free base driver mailbox structure memory */
mempool_free(pmboxq, phba->mbox_mem_pool); mempool_free(pmboxq, phba->mbox_mem_pool);
/* complete the bsg job if we have it */ /* if the job is still active, call job done */
if (job) if (job)
job->job_done(job); job->job_done(job);
...@@ -3498,8 +3587,10 @@ lpfc_bsg_issue_write_mbox_ext_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) ...@@ -3498,8 +3587,10 @@ lpfc_bsg_issue_write_mbox_ext_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
{ {
struct fc_bsg_job *job; struct fc_bsg_job *job;
job = lpfc_bsg_issue_mbox_ext_handle_job(phba, pmboxq);
/* handle the BSG job with the mailbox command */ /* handle the BSG job with the mailbox command */
if (phba->mbox_ext_buf_ctx.state == LPFC_BSG_MBOX_ABTS) if (!job)
pmboxq->u.mb.mbxStatus = MBXERR_ERROR; pmboxq->u.mb.mbxStatus = MBXERR_ERROR;
lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
...@@ -3507,13 +3598,11 @@ lpfc_bsg_issue_write_mbox_ext_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) ...@@ -3507,13 +3598,11 @@ lpfc_bsg_issue_write_mbox_ext_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
"complete, ctxState:x%x, mbxStatus:x%x\n", "complete, ctxState:x%x, mbxStatus:x%x\n",
phba->mbox_ext_buf_ctx.state, pmboxq->u.mb.mbxStatus); phba->mbox_ext_buf_ctx.state, pmboxq->u.mb.mbxStatus);
job = lpfc_bsg_issue_mbox_ext_handle_job(phba, pmboxq);
/* free all memory, including dma buffers */ /* free all memory, including dma buffers */
mempool_free(pmboxq, phba->mbox_mem_pool); mempool_free(pmboxq, phba->mbox_mem_pool);
lpfc_bsg_mbox_ext_session_reset(phba); lpfc_bsg_mbox_ext_session_reset(phba);
/* complete the bsg job if we have it */ /* if the job is still active, call job done */
if (job) if (job)
job->job_done(job); job->job_done(job);
...@@ -3759,9 +3848,9 @@ lpfc_bsg_sli_cfg_read_cmd_ext(struct lpfc_hba *phba, struct fc_bsg_job *job, ...@@ -3759,9 +3848,9 @@ lpfc_bsg_sli_cfg_read_cmd_ext(struct lpfc_hba *phba, struct fc_bsg_job *job,
/* context fields to callback function */ /* context fields to callback function */
pmboxq->context1 = dd_data; pmboxq->context1 = dd_data;
dd_data->type = TYPE_MBOX; dd_data->type = TYPE_MBOX;
dd_data->set_job = job;
dd_data->context_un.mbox.pmboxq = pmboxq; dd_data->context_un.mbox.pmboxq = pmboxq;
dd_data->context_un.mbox.mb = (MAILBOX_t *)pmbx; dd_data->context_un.mbox.mb = (MAILBOX_t *)pmbx;
dd_data->context_un.mbox.set_job = job;
job->dd_data = dd_data; job->dd_data = dd_data;
/* state change */ /* state change */
...@@ -3928,14 +4017,14 @@ lpfc_bsg_sli_cfg_write_cmd_ext(struct lpfc_hba *phba, struct fc_bsg_job *job, ...@@ -3928,14 +4017,14 @@ lpfc_bsg_sli_cfg_write_cmd_ext(struct lpfc_hba *phba, struct fc_bsg_job *job,
/* context fields to callback function */ /* context fields to callback function */
pmboxq->context1 = dd_data; pmboxq->context1 = dd_data;
dd_data->type = TYPE_MBOX; dd_data->type = TYPE_MBOX;
dd_data->set_job = job;
dd_data->context_un.mbox.pmboxq = pmboxq; dd_data->context_un.mbox.pmboxq = pmboxq;
dd_data->context_un.mbox.mb = (MAILBOX_t *)mbx; dd_data->context_un.mbox.mb = (MAILBOX_t *)mbx;
dd_data->context_un.mbox.set_job = job;
job->dd_data = dd_data; job->dd_data = dd_data;
/* state change */ /* state change */
phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_PORT;
phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_PORT;
rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT); rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT);
if ((rc == MBX_SUCCESS) || (rc == MBX_BUSY)) { if ((rc == MBX_SUCCESS) || (rc == MBX_BUSY)) {
lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
...@@ -3951,6 +4040,7 @@ lpfc_bsg_sli_cfg_write_cmd_ext(struct lpfc_hba *phba, struct fc_bsg_job *job, ...@@ -3951,6 +4040,7 @@ lpfc_bsg_sli_cfg_write_cmd_ext(struct lpfc_hba *phba, struct fc_bsg_job *job,
} }
/* wait for additoinal external buffers */ /* wait for additoinal external buffers */
job->reply->result = 0; job->reply->result = 0;
job->job_done(job); job->job_done(job);
return SLI_CONFIG_HANDLED; return SLI_CONFIG_HANDLED;
...@@ -4268,9 +4358,9 @@ lpfc_bsg_write_ebuf_set(struct lpfc_hba *phba, struct fc_bsg_job *job, ...@@ -4268,9 +4358,9 @@ lpfc_bsg_write_ebuf_set(struct lpfc_hba *phba, struct fc_bsg_job *job,
/* context fields to callback function */ /* context fields to callback function */
pmboxq->context1 = dd_data; pmboxq->context1 = dd_data;
dd_data->type = TYPE_MBOX; dd_data->type = TYPE_MBOX;
dd_data->set_job = job;
dd_data->context_un.mbox.pmboxq = pmboxq; dd_data->context_un.mbox.pmboxq = pmboxq;
dd_data->context_un.mbox.mb = (MAILBOX_t *)pbuf; dd_data->context_un.mbox.mb = (MAILBOX_t *)pbuf;
dd_data->context_un.mbox.set_job = job;
job->dd_data = dd_data; job->dd_data = dd_data;
/* state change */ /* state change */
...@@ -4455,7 +4545,6 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job, ...@@ -4455,7 +4545,6 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
uint8_t *from; uint8_t *from;
uint32_t size; uint32_t size;
/* in case no data is transferred */ /* in case no data is transferred */
job->reply->reply_payload_rcv_len = 0; job->reply->reply_payload_rcv_len = 0;
...@@ -4681,9 +4770,9 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job, ...@@ -4681,9 +4770,9 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
/* setup context field to pass wait_queue pointer to wake function */ /* setup context field to pass wait_queue pointer to wake function */
pmboxq->context1 = dd_data; pmboxq->context1 = dd_data;
dd_data->type = TYPE_MBOX; dd_data->type = TYPE_MBOX;
dd_data->set_job = job;
dd_data->context_un.mbox.pmboxq = pmboxq; dd_data->context_un.mbox.pmboxq = pmboxq;
dd_data->context_un.mbox.mb = (MAILBOX_t *)pmbx; dd_data->context_un.mbox.mb = (MAILBOX_t *)pmbx;
dd_data->context_un.mbox.set_job = job;
dd_data->context_un.mbox.ext = ext; dd_data->context_un.mbox.ext = ext;
dd_data->context_un.mbox.mbOffset = mbox_req->mbOffset; dd_data->context_un.mbox.mbOffset = mbox_req->mbOffset;
dd_data->context_un.mbox.inExtWLen = mbox_req->inExtWLen; dd_data->context_un.mbox.inExtWLen = mbox_req->inExtWLen;
...@@ -4797,43 +4886,37 @@ lpfc_bsg_menlo_cmd_cmp(struct lpfc_hba *phba, ...@@ -4797,43 +4886,37 @@ lpfc_bsg_menlo_cmd_cmp(struct lpfc_hba *phba,
struct bsg_job_data *dd_data; struct bsg_job_data *dd_data;
struct fc_bsg_job *job; struct fc_bsg_job *job;
IOCB_t *rsp; IOCB_t *rsp;
struct lpfc_dmabuf *bmp; struct lpfc_dmabuf *bmp, *cmp, *rmp;
struct lpfc_bsg_menlo *menlo; struct lpfc_bsg_menlo *menlo;
unsigned long flags; unsigned long flags;
struct menlo_response *menlo_resp; struct menlo_response *menlo_resp;
unsigned int rsp_size;
int rc = 0; int rc = 0;
spin_lock_irqsave(&phba->ct_ev_lock, flags);
dd_data = cmdiocbq->context1; dd_data = cmdiocbq->context1;
if (!dd_data) { cmp = cmdiocbq->context2;
spin_unlock_irqrestore(&phba->ct_ev_lock, flags); bmp = cmdiocbq->context3;
return;
}
menlo = &dd_data->context_un.menlo; menlo = &dd_data->context_un.menlo;
job = menlo->set_job; rmp = menlo->rmp;
job->dd_data = NULL; /* so timeout handler does not reply */
spin_lock(&phba->hbalock);
cmdiocbq->iocb_flag |= LPFC_IO_WAKE;
if (cmdiocbq->context2 && rspiocbq)
memcpy(&((struct lpfc_iocbq *)cmdiocbq->context2)->iocb,
&rspiocbq->iocb, sizeof(IOCB_t));
spin_unlock(&phba->hbalock);
bmp = menlo->bmp;
rspiocbq = menlo->rspiocbq;
rsp = &rspiocbq->iocb; rsp = &rspiocbq->iocb;
pci_unmap_sg(phba->pcidev, job->request_payload.sg_list, /* Determine if job has been aborted */
job->request_payload.sg_cnt, DMA_TO_DEVICE); spin_lock_irqsave(&phba->ct_ev_lock, flags);
pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list, job = dd_data->set_job;
job->reply_payload.sg_cnt, DMA_FROM_DEVICE); if (job) {
/* Prevent timeout handling from trying to abort job */
job->dd_data = NULL;
}
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
/* Copy the job data or set the failing status for the job */
if (job) {
/* always return the xri, this would be used in the case /* always return the xri, this would be used in the case
* of a menlo download to allow the data to be sent as a continuation * of a menlo download to allow the data to be sent as a
* of the exchange. * continuation of the exchange.
*/ */
menlo_resp = (struct menlo_response *) menlo_resp = (struct menlo_response *)
job->reply->reply_data.vendor_reply.vendor_rsp; job->reply->reply_data.vendor_reply.vendor_rsp;
menlo_resp->xri = rsp->ulpContext; menlo_resp->xri = rsp->ulpContext;
...@@ -4850,22 +4933,32 @@ lpfc_bsg_menlo_cmd_cmp(struct lpfc_hba *phba, ...@@ -4850,22 +4933,32 @@ lpfc_bsg_menlo_cmd_cmp(struct lpfc_hba *phba,
rc = -EACCES; rc = -EACCES;
break; break;
} }
} else } else {
rc = -EACCES; rc = -EACCES;
} else }
} else {
rsp_size = rsp->un.genreq64.bdl.bdeSize;
job->reply->reply_payload_rcv_len = job->reply->reply_payload_rcv_len =
rsp->un.genreq64.bdl.bdeSize; lpfc_bsg_copy_data(rmp, &job->reply_payload,
rsp_size, 0);
}
}
lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
lpfc_sli_release_iocbq(phba, rspiocbq);
lpfc_sli_release_iocbq(phba, cmdiocbq); lpfc_sli_release_iocbq(phba, cmdiocbq);
lpfc_free_bsg_buffers(phba, cmp);
lpfc_free_bsg_buffers(phba, rmp);
lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
kfree(bmp); kfree(bmp);
kfree(dd_data); kfree(dd_data);
/* make error code available to userspace */
/* Complete the job if active */
if (job) {
job->reply->result = rc; job->reply->result = rc;
/* complete the job back to userspace */
job->job_done(job); job->job_done(job);
spin_unlock_irqrestore(&phba->ct_ev_lock, flags); }
return; return;
} }
...@@ -4883,17 +4976,14 @@ lpfc_menlo_cmd(struct fc_bsg_job *job) ...@@ -4883,17 +4976,14 @@ lpfc_menlo_cmd(struct fc_bsg_job *job)
{ {
struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
struct lpfc_hba *phba = vport->phba; struct lpfc_hba *phba = vport->phba;
struct lpfc_iocbq *cmdiocbq, *rspiocbq; struct lpfc_iocbq *cmdiocbq;
IOCB_t *cmd, *rsp; IOCB_t *cmd;
int rc = 0; int rc = 0;
struct menlo_command *menlo_cmd; struct menlo_command *menlo_cmd;
struct menlo_response *menlo_resp; struct menlo_response *menlo_resp;
struct lpfc_dmabuf *bmp = NULL; struct lpfc_dmabuf *bmp = NULL, *cmp = NULL, *rmp = NULL;
int request_nseg; int request_nseg;
int reply_nseg; int reply_nseg;
struct scatterlist *sgel = NULL;
int numbde;
dma_addr_t busaddr;
struct bsg_job_data *dd_data; struct bsg_job_data *dd_data;
struct ulp_bde64 *bpl = NULL; struct ulp_bde64 *bpl = NULL;
...@@ -4948,50 +5038,38 @@ lpfc_menlo_cmd(struct fc_bsg_job *job) ...@@ -4948,50 +5038,38 @@ lpfc_menlo_cmd(struct fc_bsg_job *job)
goto free_dd; goto free_dd;
} }
cmdiocbq = lpfc_sli_get_iocbq(phba); bmp->virt = lpfc_mbuf_alloc(phba, 0, &bmp->phys);
if (!cmdiocbq) { if (!bmp->virt) {
rc = -ENOMEM; rc = -ENOMEM;
goto free_bmp; goto free_bmp;
} }
rspiocbq = lpfc_sli_get_iocbq(phba); INIT_LIST_HEAD(&bmp->list);
if (!rspiocbq) {
rc = -ENOMEM;
goto free_cmdiocbq;
}
rsp = &rspiocbq->iocb;
bmp->virt = lpfc_mbuf_alloc(phba, 0, &bmp->phys); bpl = (struct ulp_bde64 *)bmp->virt;
if (!bmp->virt) { request_nseg = LPFC_BPL_SIZE/sizeof(struct ulp_bde64);
cmp = lpfc_alloc_bsg_buffers(phba, job->request_payload.payload_len,
1, bpl, &request_nseg);
if (!cmp) {
rc = -ENOMEM; rc = -ENOMEM;
goto free_rspiocbq; goto free_bmp;
} }
lpfc_bsg_copy_data(cmp, &job->request_payload,
job->request_payload.payload_len, 1);
INIT_LIST_HEAD(&bmp->list); bpl += request_nseg;
bpl = (struct ulp_bde64 *) bmp->virt; reply_nseg = LPFC_BPL_SIZE/sizeof(struct ulp_bde64) - request_nseg;
request_nseg = pci_map_sg(phba->pcidev, job->request_payload.sg_list, rmp = lpfc_alloc_bsg_buffers(phba, job->reply_payload.payload_len, 0,
job->request_payload.sg_cnt, DMA_TO_DEVICE); bpl, &reply_nseg);
for_each_sg(job->request_payload.sg_list, sgel, request_nseg, numbde) { if (!rmp) {
busaddr = sg_dma_address(sgel); rc = -ENOMEM;
bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64; goto free_cmp;
bpl->tus.f.bdeSize = sg_dma_len(sgel);
bpl->tus.w = cpu_to_le32(bpl->tus.w);
bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr));
bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr));
bpl++;
} }
reply_nseg = pci_map_sg(phba->pcidev, job->reply_payload.sg_list, cmdiocbq = lpfc_sli_get_iocbq(phba);
job->reply_payload.sg_cnt, DMA_FROM_DEVICE); if (!cmdiocbq) {
for_each_sg(job->reply_payload.sg_list, sgel, reply_nseg, numbde) { rc = -ENOMEM;
busaddr = sg_dma_address(sgel); goto free_rmp;
bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
bpl->tus.f.bdeSize = sg_dma_len(sgel);
bpl->tus.w = cpu_to_le32(bpl->tus.w);
bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr));
bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr));
bpl++;
} }
cmd = &cmdiocbq->iocb; cmd = &cmdiocbq->iocb;
...@@ -5013,11 +5091,10 @@ lpfc_menlo_cmd(struct fc_bsg_job *job) ...@@ -5013,11 +5091,10 @@ lpfc_menlo_cmd(struct fc_bsg_job *job)
cmdiocbq->vport = phba->pport; cmdiocbq->vport = phba->pport;
/* We want the firmware to timeout before we do */ /* We want the firmware to timeout before we do */
cmd->ulpTimeout = MENLO_TIMEOUT - 5; cmd->ulpTimeout = MENLO_TIMEOUT - 5;
cmdiocbq->context3 = bmp;
cmdiocbq->context2 = rspiocbq;
cmdiocbq->iocb_cmpl = lpfc_bsg_menlo_cmd_cmp; cmdiocbq->iocb_cmpl = lpfc_bsg_menlo_cmd_cmp;
cmdiocbq->context1 = dd_data; cmdiocbq->context1 = dd_data;
cmdiocbq->context2 = rspiocbq; cmdiocbq->context2 = cmp;
cmdiocbq->context3 = bmp;
if (menlo_cmd->cmd == LPFC_BSG_VENDOR_MENLO_CMD) { if (menlo_cmd->cmd == LPFC_BSG_VENDOR_MENLO_CMD) {
cmd->ulpCommand = CMD_GEN_REQUEST64_CR; cmd->ulpCommand = CMD_GEN_REQUEST64_CR;
cmd->ulpPU = MENLO_PU; /* 3 */ cmd->ulpPU = MENLO_PU; /* 3 */
...@@ -5031,29 +5108,25 @@ lpfc_menlo_cmd(struct fc_bsg_job *job) ...@@ -5031,29 +5108,25 @@ lpfc_menlo_cmd(struct fc_bsg_job *job)
} }
dd_data->type = TYPE_MENLO; dd_data->type = TYPE_MENLO;
dd_data->set_job = job;
dd_data->context_un.menlo.cmdiocbq = cmdiocbq; dd_data->context_un.menlo.cmdiocbq = cmdiocbq;
dd_data->context_un.menlo.rspiocbq = rspiocbq; dd_data->context_un.menlo.rmp = rmp;
dd_data->context_un.menlo.set_job = job; job->dd_data = dd_data;
dd_data->context_un.menlo.bmp = bmp;
rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq, rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq,
MENLO_TIMEOUT - 5); MENLO_TIMEOUT - 5);
if (rc == IOCB_SUCCESS) if (rc == IOCB_SUCCESS)
return 0; /* done for now */ return 0; /* done for now */
/* iocb failed so cleanup */
pci_unmap_sg(phba->pcidev, job->request_payload.sg_list,
job->request_payload.sg_cnt, DMA_TO_DEVICE);
pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list,
job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
free_rspiocbq:
lpfc_sli_release_iocbq(phba, rspiocbq);
free_cmdiocbq:
lpfc_sli_release_iocbq(phba, cmdiocbq); lpfc_sli_release_iocbq(phba, cmdiocbq);
free_rmp:
lpfc_free_bsg_buffers(phba, rmp);
free_cmp:
lpfc_free_bsg_buffers(phba, cmp);
free_bmp: free_bmp:
if (bmp->virt)
lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
kfree(bmp); kfree(bmp);
free_dd: free_dd:
kfree(dd_data); kfree(dd_data);
...@@ -5162,70 +5235,94 @@ lpfc_bsg_timeout(struct fc_bsg_job *job) ...@@ -5162,70 +5235,94 @@ lpfc_bsg_timeout(struct fc_bsg_job *job)
struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
struct lpfc_hba *phba = vport->phba; struct lpfc_hba *phba = vport->phba;
struct lpfc_iocbq *cmdiocb; struct lpfc_iocbq *cmdiocb;
struct lpfc_bsg_event *evt;
struct lpfc_bsg_iocb *iocb;
struct lpfc_bsg_mbox *mbox;
struct lpfc_bsg_menlo *menlo;
struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING]; struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
struct bsg_job_data *dd_data; struct bsg_job_data *dd_data;
unsigned long flags; unsigned long flags;
int rc = 0;
LIST_HEAD(completions);
struct lpfc_iocbq *check_iocb, *next_iocb;
/* if job's driver data is NULL, the command completed or is in the
* the process of completing. In this case, return status to request
* so the timeout is retried. This avoids double completion issues
* and the request will be pulled off the timer queue when the
* command's completion handler executes. Otherwise, prevent the
* command's completion handler from executing the job done callback
* and continue processing to abort the outstanding the command.
*/
spin_lock_irqsave(&phba->ct_ev_lock, flags); spin_lock_irqsave(&phba->ct_ev_lock, flags);
dd_data = (struct bsg_job_data *)job->dd_data; dd_data = (struct bsg_job_data *)job->dd_data;
/* timeout and completion crossed paths if no dd_data */ if (dd_data) {
if (!dd_data) { dd_data->set_job = NULL;
job->dd_data = NULL;
} else {
spin_unlock_irqrestore(&phba->ct_ev_lock, flags); spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
return 0; return -EAGAIN;
} }
switch (dd_data->type) { switch (dd_data->type) {
case TYPE_IOCB: case TYPE_IOCB:
iocb = &dd_data->context_un.iocb; /* Check to see if IOCB was issued to the port or not. If not,
cmdiocb = iocb->cmdiocbq; * remove it from the txq queue and call cancel iocbs.
/* hint to completion handler that the job timed out */ * Otherwise, call abort iotag
job->reply->result = -EAGAIN; */
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
/* this will call our completion handler */ cmdiocb = dd_data->context_un.iocb.cmdiocbq;
spin_lock_irq(&phba->hbalock); spin_lock_irq(&phba->hbalock);
list_for_each_entry_safe(check_iocb, next_iocb, &pring->txq,
list) {
if (check_iocb == cmdiocb) {
list_move_tail(&check_iocb->list, &completions);
break;
}
}
if (list_empty(&completions))
lpfc_sli_issue_abort_iotag(phba, pring, cmdiocb); lpfc_sli_issue_abort_iotag(phba, pring, cmdiocb);
spin_unlock_irq(&phba->hbalock); spin_unlock_irq(&phba->hbalock);
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
if (!list_empty(&completions)) {
lpfc_sli_cancel_iocbs(phba, &completions,
IOSTAT_LOCAL_REJECT,
IOERR_SLI_ABORTED);
}
break; break;
case TYPE_EVT: case TYPE_EVT:
evt = dd_data->context_un.evt;
/* this event has no job anymore */
evt->set_job = NULL;
job->dd_data = NULL;
job->reply->reply_payload_rcv_len = 0;
/* Return -EAGAIN which is our way of signallying the
* app to retry.
*/
job->reply->result = -EAGAIN;
spin_unlock_irqrestore(&phba->ct_ev_lock, flags); spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
job->job_done(job);
break; break;
case TYPE_MBOX: case TYPE_MBOX:
mbox = &dd_data->context_un.mbox; /* Update the ext buf ctx state if needed */
/* this mbox has no job anymore */
mbox->set_job = NULL;
job->dd_data = NULL;
job->reply->reply_payload_rcv_len = 0;
job->reply->result = -EAGAIN;
/* the mbox completion handler can now be run */
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
job->job_done(job);
if (phba->mbox_ext_buf_ctx.state == LPFC_BSG_MBOX_PORT) if (phba->mbox_ext_buf_ctx.state == LPFC_BSG_MBOX_PORT)
phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_ABTS; phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_ABTS;
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
break; break;
case TYPE_MENLO: case TYPE_MENLO:
menlo = &dd_data->context_un.menlo; /* Check to see if IOCB was issued to the port or not. If not,
cmdiocb = menlo->cmdiocbq; * remove it from the txq queue and call cancel iocbs.
/* hint to completion handler that the job timed out */ * Otherwise, call abort iotag.
job->reply->result = -EAGAIN; */
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
/* this will call our completion handler */ cmdiocb = dd_data->context_un.menlo.cmdiocbq;
spin_lock_irq(&phba->hbalock); spin_lock_irq(&phba->hbalock);
list_for_each_entry_safe(check_iocb, next_iocb, &pring->txq,
list) {
if (check_iocb == cmdiocb) {
list_move_tail(&check_iocb->list, &completions);
break;
}
}
if (list_empty(&completions))
lpfc_sli_issue_abort_iotag(phba, pring, cmdiocb); lpfc_sli_issue_abort_iotag(phba, pring, cmdiocb);
spin_unlock_irq(&phba->hbalock); spin_unlock_irq(&phba->hbalock);
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
if (!list_empty(&completions)) {
lpfc_sli_cancel_iocbs(phba, &completions,
IOSTAT_LOCAL_REJECT,
IOERR_SLI_ABORTED);
}
break; break;
default: default:
spin_unlock_irqrestore(&phba->ct_ev_lock, flags); spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
...@@ -5236,5 +5333,5 @@ lpfc_bsg_timeout(struct fc_bsg_job *job) ...@@ -5236,5 +5333,5 @@ lpfc_bsg_timeout(struct fc_bsg_job *job)
* otherwise an error message will be displayed on the console * otherwise an error message will be displayed on the console
* so always return success (zero) * so always return success (zero)
*/ */
return 0; return rc;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment