summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/lpfc/lpfc_bsg.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/lpfc/lpfc_bsg.c')
-rw-r--r--drivers/scsi/lpfc/lpfc_bsg.c337
1 files changed, 335 insertions, 2 deletions
diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c
index f3f1bf1a0a7..d62b3e46792 100644
--- a/drivers/scsi/lpfc/lpfc_bsg.c
+++ b/drivers/scsi/lpfc/lpfc_bsg.c
@@ -21,6 +21,7 @@
#include <linux/interrupt.h>
#include <linux/mempool.h>
#include <linux/pci.h>
+#include <linux/slab.h>
#include <linux/delay.h>
#include <scsi/scsi.h>
@@ -83,15 +84,28 @@ struct lpfc_bsg_mbox {
struct fc_bsg_job *set_job;
};
+#define MENLO_DID 0x0000FC0E
+
+struct lpfc_bsg_menlo {
+ struct lpfc_iocbq *cmdiocbq;
+ struct lpfc_iocbq *rspiocbq;
+ struct lpfc_dmabuf *bmp;
+
+ /* job waiting for this iocb to finish */
+ struct fc_bsg_job *set_job;
+};
+
#define TYPE_EVT 1
#define TYPE_IOCB 2
#define TYPE_MBOX 3
+#define TYPE_MENLO 4
struct bsg_job_data {
uint32_t type;
union {
struct lpfc_bsg_event *evt;
struct lpfc_bsg_iocb iocb;
struct lpfc_bsg_mbox mbox;
+ struct lpfc_bsg_menlo menlo;
} context_un;
};
@@ -419,7 +433,7 @@ lpfc_bsg_rport_els_cmp(struct lpfc_hba *phba,
dd_data = cmdiocbq->context1;
/* normal completion and timeout crossed paths, already done */
if (!dd_data) {
- spin_unlock_irqrestore(&phba->hbalock, flags);
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
return;
}
@@ -1182,7 +1196,7 @@ lpfc_issue_ct_rsp_cmp(struct lpfc_hba *phba,
dd_data = cmdiocbq->context1;
/* normal completion and timeout crossed paths, already done */
if (!dd_data) {
- spin_unlock_irqrestore(&phba->hbalock, flags);
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
return;
}
@@ -2456,6 +2470,18 @@ static int lpfc_bsg_check_cmd_access(struct lpfc_hba *phba,
case MBX_PORT_IOV_CONTROL:
break;
case MBX_SET_VARIABLE:
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "1226 mbox: set_variable 0x%x, 0x%x\n",
+ mb->un.varWords[0],
+ mb->un.varWords[1]);
+ if ((mb->un.varWords[0] == SETVAR_MLOMNT)
+ && (mb->un.varWords[1] == 1)) {
+ phba->wait_4_mlo_maint_flg = 1;
+ } else if (mb->un.varWords[0] == SETVAR_MLORST) {
+ phba->link_flag &= ~LS_LOOPBACK_MODE;
+ phba->fc_topology = TOPOLOGY_PT_PT;
+ }
+ break;
case MBX_RUN_BIU_DIAG64:
case MBX_READ_EVENT_LOG:
case MBX_READ_SPARM64:
@@ -2638,6 +2664,297 @@ job_error:
}
/**
+ * lpfc_bsg_menlo_cmd_cmp - lpfc_menlo_cmd completion handler
+ * @phba: Pointer to HBA context object.
+ * @cmdiocbq: Pointer to command iocb.
+ * @rspiocbq: Pointer to response iocb.
+ *
+ * This function is the completion handler for iocbs issued using
+ * lpfc_menlo_cmd function. This function is called by the
+ * ring event handler function without any lock held. This function
+ * can be called from both worker thread context and interrupt
+ * context. This function also can be called from another thread which
+ * cleans up the SLI layer objects.
+ * This function copies the contents of the response iocb to the
+ * response iocb memory object provided by the caller of
+ * lpfc_sli_issue_iocb_wait and then wakes up the thread which
+ * sleeps for the iocb completion.
+ **/
+static void
+lpfc_bsg_menlo_cmd_cmp(struct lpfc_hba *phba,
+ struct lpfc_iocbq *cmdiocbq,
+ struct lpfc_iocbq *rspiocbq)
+{
+ struct bsg_job_data *dd_data;
+ struct fc_bsg_job *job;
+ IOCB_t *rsp;
+ struct lpfc_dmabuf *bmp;
+ struct lpfc_bsg_menlo *menlo;
+ unsigned long flags;
+ struct menlo_response *menlo_resp;
+ int rc = 0;
+
+ spin_lock_irqsave(&phba->ct_ev_lock, flags);
+ dd_data = cmdiocbq->context1;
+ if (!dd_data) {
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+ return;
+ }
+
+ menlo = &dd_data->context_un.menlo;
+ job = menlo->set_job;
+ job->dd_data = NULL; /* so timeout handler does not reply */
+
+ spin_lock_irqsave(&phba->hbalock, flags);
+ cmdiocbq->iocb_flag |= LPFC_IO_WAKE;
+ if (cmdiocbq->context2 && rspiocbq)
+ memcpy(&((struct lpfc_iocbq *)cmdiocbq->context2)->iocb,
+ &rspiocbq->iocb, sizeof(IOCB_t));
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+
+ bmp = menlo->bmp;
+ rspiocbq = menlo->rspiocbq;
+ rsp = &rspiocbq->iocb;
+
+ 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);
+
+ /* 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 the exchange.
+ */
+ menlo_resp = (struct menlo_response *)
+ job->reply->reply_data.vendor_reply.vendor_rsp;
+ menlo_resp->xri = rsp->ulpContext;
+ if (rsp->ulpStatus) {
+ if (rsp->ulpStatus == IOSTAT_LOCAL_REJECT) {
+ switch (rsp->un.ulpWord[4] & 0xff) {
+ case IOERR_SEQUENCE_TIMEOUT:
+ rc = -ETIMEDOUT;
+ break;
+ case IOERR_INVALID_RPI:
+ rc = -EFAULT;
+ break;
+ default:
+ rc = -EACCES;
+ break;
+ }
+ } else
+ rc = -EACCES;
+ } else
+ job->reply->reply_payload_rcv_len =
+ rsp->un.genreq64.bdl.bdeSize;
+
+ lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
+ lpfc_sli_release_iocbq(phba, rspiocbq);
+ lpfc_sli_release_iocbq(phba, cmdiocbq);
+ kfree(bmp);
+ kfree(dd_data);
+ /* make error code available to userspace */
+ job->reply->result = rc;
+ /* complete the job back to userspace */
+ job->job_done(job);
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+ return;
+}
+
+/**
+ * lpfc_menlo_cmd - send an ioctl for menlo hardware
+ * @job: fc_bsg_job to handle
+ *
+ * This function issues a gen request 64 CR ioctl for all menlo cmd requests,
+ * all the command completions will return the xri for the command.
+ * For menlo data requests a gen request 64 CX is used to continue the exchange
+ * supplied in the menlo request header xri field.
+ **/
+static int
+lpfc_menlo_cmd(struct fc_bsg_job *job)
+{
+ struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_iocbq *cmdiocbq, *rspiocbq;
+ IOCB_t *cmd, *rsp;
+ int rc = 0;
+ struct menlo_command *menlo_cmd;
+ struct menlo_response *menlo_resp;
+ struct lpfc_dmabuf *bmp = NULL;
+ int request_nseg;
+ int reply_nseg;
+ struct scatterlist *sgel = NULL;
+ int numbde;
+ dma_addr_t busaddr;
+ struct bsg_job_data *dd_data;
+ struct ulp_bde64 *bpl = NULL;
+
+ /* in case no data is returned return just the return code */
+ job->reply->reply_payload_rcv_len = 0;
+
+ if (job->request_len <
+ sizeof(struct fc_bsg_request) +
+ sizeof(struct menlo_command)) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "2784 Received MENLO_CMD request below "
+ "minimum size\n");
+ rc = -ERANGE;
+ goto no_dd_data;
+ }
+
+ if (job->reply_len <
+ sizeof(struct fc_bsg_request) + sizeof(struct menlo_response)) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "2785 Received MENLO_CMD reply below "
+ "minimum size\n");
+ rc = -ERANGE;
+ goto no_dd_data;
+ }
+
+ if (!(phba->menlo_flag & HBA_MENLO_SUPPORT)) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "2786 Adapter does not support menlo "
+ "commands\n");
+ rc = -EPERM;
+ goto no_dd_data;
+ }
+
+ menlo_cmd = (struct menlo_command *)
+ job->request->rqst_data.h_vendor.vendor_cmd;
+
+ menlo_resp = (struct menlo_response *)
+ job->reply->reply_data.vendor_reply.vendor_rsp;
+
+ /* allocate our bsg tracking structure */
+ dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
+ if (!dd_data) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "2787 Failed allocation of dd_data\n");
+ rc = -ENOMEM;
+ goto no_dd_data;
+ }
+
+ bmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+ if (!bmp) {
+ rc = -ENOMEM;
+ goto free_dd;
+ }
+
+ cmdiocbq = lpfc_sli_get_iocbq(phba);
+ if (!cmdiocbq) {
+ rc = -ENOMEM;
+ goto free_bmp;
+ }
+
+ rspiocbq = lpfc_sli_get_iocbq(phba);
+ if (!rspiocbq) {
+ rc = -ENOMEM;
+ goto free_cmdiocbq;
+ }
+
+ rsp = &rspiocbq->iocb;
+
+ bmp->virt = lpfc_mbuf_alloc(phba, 0, &bmp->phys);
+ if (!bmp->virt) {
+ rc = -ENOMEM;
+ goto free_rspiocbq;
+ }
+
+ INIT_LIST_HEAD(&bmp->list);
+ bpl = (struct ulp_bde64 *) bmp->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++;
+ }
+
+ cmd = &cmdiocbq->iocb;
+ cmd->un.genreq64.bdl.ulpIoTag32 = 0;
+ cmd->un.genreq64.bdl.addrHigh = putPaddrHigh(bmp->phys);
+ cmd->un.genreq64.bdl.addrLow = putPaddrLow(bmp->phys);
+ cmd->un.genreq64.bdl.bdeFlags = BUFF_TYPE_BLP_64;
+ cmd->un.genreq64.bdl.bdeSize =
+ (request_nseg + reply_nseg) * sizeof(struct ulp_bde64);
+ cmd->un.genreq64.w5.hcsw.Fctl = (SI | LA);
+ cmd->un.genreq64.w5.hcsw.Dfctl = 0;
+ cmd->un.genreq64.w5.hcsw.Rctl = FC_RCTL_DD_UNSOL_CMD;
+ cmd->un.genreq64.w5.hcsw.Type = MENLO_TRANSPORT_TYPE; /* 0xfe */
+ cmd->ulpBdeCount = 1;
+ cmd->ulpClass = CLASS3;
+ cmd->ulpOwner = OWN_CHIP;
+ cmd->ulpLe = 1; /* Limited Edition */
+ cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC;
+ cmdiocbq->vport = phba->pport;
+ /* We want the firmware to timeout before we do */
+ cmd->ulpTimeout = MENLO_TIMEOUT - 5;
+ cmdiocbq->context3 = bmp;
+ cmdiocbq->context2 = rspiocbq;
+ cmdiocbq->iocb_cmpl = lpfc_bsg_menlo_cmd_cmp;
+ cmdiocbq->context1 = dd_data;
+ cmdiocbq->context2 = rspiocbq;
+ if (menlo_cmd->cmd == LPFC_BSG_VENDOR_MENLO_CMD) {
+ cmd->ulpCommand = CMD_GEN_REQUEST64_CR;
+ cmd->ulpPU = MENLO_PU; /* 3 */
+ cmd->un.ulpWord[4] = MENLO_DID; /* 0x0000FC0E */
+ cmd->ulpContext = MENLO_CONTEXT; /* 0 */
+ } else {
+ cmd->ulpCommand = CMD_GEN_REQUEST64_CX;
+ cmd->ulpPU = 1;
+ cmd->un.ulpWord[4] = 0;
+ cmd->ulpContext = menlo_cmd->xri;
+ }
+
+ dd_data->type = TYPE_MENLO;
+ dd_data->context_un.menlo.cmdiocbq = cmdiocbq;
+ dd_data->context_un.menlo.rspiocbq = rspiocbq;
+ dd_data->context_un.menlo.set_job = job;
+ dd_data->context_un.menlo.bmp = bmp;
+
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq,
+ MENLO_TIMEOUT - 5);
+ if (rc == IOCB_SUCCESS)
+ 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);
+free_bmp:
+ kfree(bmp);
+free_dd:
+ kfree(dd_data);
+no_dd_data:
+ /* make error code available to userspace */
+ job->reply->result = rc;
+ job->dd_data = NULL;
+ return rc;
+}
+/**
* lpfc_bsg_hst_vendor - process a vendor-specific fc_bsg_job
* @job: fc_bsg_job to handle
**/
@@ -2669,6 +2986,10 @@ lpfc_bsg_hst_vendor(struct fc_bsg_job *job)
case LPFC_BSG_VENDOR_MBOX:
rc = lpfc_bsg_mbox_cmd(job);
break;
+ case LPFC_BSG_VENDOR_MENLO_CMD:
+ case LPFC_BSG_VENDOR_MENLO_DATA:
+ rc = lpfc_menlo_cmd(job);
+ break;
default:
rc = -EINVAL;
job->reply->reply_payload_rcv_len = 0;
@@ -2728,6 +3049,7 @@ lpfc_bsg_timeout(struct fc_bsg_job *job)
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 bsg_job_data *dd_data;
unsigned long flags;
@@ -2775,6 +3097,17 @@ lpfc_bsg_timeout(struct fc_bsg_job *job)
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
job->job_done(job);
break;
+ case TYPE_MENLO:
+ menlo = &dd_data->context_un.menlo;
+ cmdiocb = menlo->cmdiocbq;
+ /* hint to completion handler that the job timed out */
+ job->reply->result = -EAGAIN;
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+ /* this will call our completion handler */
+ spin_lock_irq(&phba->hbalock);
+ lpfc_sli_issue_abort_iotag(phba, pring, cmdiocb);
+ spin_unlock_irq(&phba->hbalock);
+ break;
default:
spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
break;