[SCSI] Merge scsi-misc-2.6 into scsi-rc-fixes-2.6
[safe/jmp/linux-2.6] / drivers / scsi / lpfc / lpfc_bsg.c
index f3f1bf1..dcf0882 100644 (file)
@@ -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>
@@ -78,20 +79,39 @@ struct lpfc_bsg_iocb {
 struct lpfc_bsg_mbox {
        LPFC_MBOXQ_t *pmboxq;
        MAILBOX_t *mb;
+       struct lpfc_dmabuf *rxbmp; /* for BIU diags */
+       struct lpfc_dmabufext *dmp; /* for BIU diags */
+       uint8_t *ext; /* extended mailbox data */
+       uint32_t mbOffset; /* from app */
+       uint32_t inExtWLen; /* 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
+
+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 +439,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 +1202,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;
        }
 
@@ -1694,21 +1714,26 @@ static int lpfcdiag_loop_get_xri(struct lpfc_hba *phba, uint16_t rpi,
        dmabuf = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
        if (dmabuf) {
                dmabuf->virt = lpfc_mbuf_alloc(phba, 0, &dmabuf->phys);
-               INIT_LIST_HEAD(&dmabuf->list);
-               bpl = (struct ulp_bde64 *) dmabuf->virt;
-               memset(bpl, 0, sizeof(*bpl));
-               ctreq = (struct lpfc_sli_ct_request *)(bpl + 1);
-               bpl->addrHigh =
-                       le32_to_cpu(putPaddrHigh(dmabuf->phys + sizeof(*bpl)));
-               bpl->addrLow =
-                       le32_to_cpu(putPaddrLow(dmabuf->phys + sizeof(*bpl)));
-               bpl->tus.f.bdeFlags = 0;
-               bpl->tus.f.bdeSize = ELX_LOOPBACK_HEADER_SZ;
-               bpl->tus.w = le32_to_cpu(bpl->tus.w);
+               if (dmabuf->virt) {
+                       INIT_LIST_HEAD(&dmabuf->list);
+                       bpl = (struct ulp_bde64 *) dmabuf->virt;
+                       memset(bpl, 0, sizeof(*bpl));
+                       ctreq = (struct lpfc_sli_ct_request *)(bpl + 1);
+                       bpl->addrHigh =
+                               le32_to_cpu(putPaddrHigh(dmabuf->phys +
+                                       sizeof(*bpl)));
+                       bpl->addrLow =
+                               le32_to_cpu(putPaddrLow(dmabuf->phys +
+                                       sizeof(*bpl)));
+                       bpl->tus.f.bdeFlags = 0;
+                       bpl->tus.f.bdeSize = ELX_LOOPBACK_HEADER_SZ;
+                       bpl->tus.w = le32_to_cpu(bpl->tus.w);
+               }
        }
 
        if (cmdiocbq == NULL || rspiocbq == NULL ||
-           dmabuf == NULL || bpl == NULL || ctreq == NULL) {
+           dmabuf == NULL || bpl == NULL || ctreq == NULL ||
+               dmabuf->virt == NULL) {
                ret_val = ENOMEM;
                goto err_get_xri_exit;
        }
@@ -1904,9 +1929,11 @@ static int lpfcdiag_loop_post_rxbufs(struct lpfc_hba *phba, uint16_t rxxri,
        rxbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
        if (rxbmp != NULL) {
                rxbmp->virt = lpfc_mbuf_alloc(phba, 0, &rxbmp->phys);
-               INIT_LIST_HEAD(&rxbmp->list);
-               rxbpl = (struct ulp_bde64 *) rxbmp->virt;
-               rxbuffer = diag_cmd_data_alloc(phba, rxbpl, len, 0);
+               if (rxbmp->virt) {
+                       INIT_LIST_HEAD(&rxbmp->list);
+                       rxbpl = (struct ulp_bde64 *) rxbmp->virt;
+                       rxbuffer = diag_cmd_data_alloc(phba, rxbpl, len, 0);
+               }
        }
 
        if (!cmdiocbq || !rxbmp || !rxbpl || !rxbuffer) {
@@ -2160,14 +2187,16 @@ lpfc_bsg_diag_test(struct fc_bsg_job *job)
 
        if (txbmp) {
                txbmp->virt = lpfc_mbuf_alloc(phba, 0, &txbmp->phys);
-               INIT_LIST_HEAD(&txbmp->list);
-               txbpl = (struct ulp_bde64 *) txbmp->virt;
-               if (txbpl)
+               if (txbmp->virt) {
+                       INIT_LIST_HEAD(&txbmp->list);
+                       txbpl = (struct ulp_bde64 *) txbmp->virt;
                        txbuffer = diag_cmd_data_alloc(phba,
                                                        txbpl, full_size, 0);
+               }
        }
 
-       if (!cmdiocbq || !rspiocbq || !txbmp || !txbpl || !txbuffer) {
+       if (!cmdiocbq || !rspiocbq || !txbmp || !txbpl || !txbuffer ||
+               !txbmp->virt) {
                rc = -ENOMEM;
                goto err_loopback_test_exit;
        }
@@ -2363,35 +2392,90 @@ void
 lpfc_bsg_wake_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
 {
        struct bsg_job_data *dd_data;
-       MAILBOX_t *pmb;
-       MAILBOX_t *mb;
        struct fc_bsg_job *job;
        uint32_t size;
        unsigned long flags;
+       uint8_t *to;
+       uint8_t *from;
 
        spin_lock_irqsave(&phba->ct_ev_lock, flags);
        dd_data = pmboxq->context1;
+       /* job already timed out? */
        if (!dd_data) {
                spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
                return;
        }
 
-       pmb = &dd_data->context_un.mbox.pmboxq->u.mb;
-       mb = dd_data->context_un.mbox.mb;
+       /* build the outgoing buffer to do an sg copy
+        * the format is the response mailbox followed by any extended
+        * mailbox data
+        */
+       from = (uint8_t *)&pmboxq->u.mb;
+       to = (uint8_t *)dd_data->context_un.mbox.mb;
+       memcpy(to, from, sizeof(MAILBOX_t));
+       if (pmboxq->u.mb.mbxStatus == MBX_SUCCESS) {
+               /* copy the extended data if any, count is in words */
+               if (dd_data->context_un.mbox.outExtWLen) {
+                       from = (uint8_t *)dd_data->context_un.mbox.ext;
+                       to += sizeof(MAILBOX_t);
+                       size = dd_data->context_un.mbox.outExtWLen *
+                                       sizeof(uint32_t);
+                       memcpy(to, from, size);
+               } else if (pmboxq->u.mb.mbxCommand == MBX_RUN_BIU_DIAG64) {
+                       from = (uint8_t *)dd_data->context_un.mbox.
+                                               dmp->dma.virt;
+                       to += sizeof(MAILBOX_t);
+                       size = dd_data->context_un.mbox.dmp->size;
+                       memcpy(to, from, size);
+               } else if ((phba->sli_rev == LPFC_SLI_REV4) &&
+                       (pmboxq->u.mb.mbxCommand == MBX_DUMP_MEMORY)) {
+                       from = (uint8_t *)dd_data->context_un.mbox.dmp->dma.
+                                               virt;
+                       to += sizeof(MAILBOX_t);
+                       size = pmboxq->u.mb.un.varWords[5];
+                       memcpy(to, from, size);
+               } else if (pmboxq->u.mb.mbxCommand == MBX_READ_EVENT_LOG) {
+                       from = (uint8_t *)dd_data->context_un.
+                                               mbox.dmp->dma.virt;
+                       to += sizeof(MAILBOX_t);
+                       size = dd_data->context_un.mbox.dmp->size;
+                       memcpy(to, from, size);
+               }
+       }
+
+       from = (uint8_t *)dd_data->context_un.mbox.mb;
        job = dd_data->context_un.mbox.set_job;
-       memcpy(mb, pmb, sizeof(*pmb));
-       size = job->request_payload.payload_len;
+       size = job->reply_payload.payload_len;
        job->reply->reply_payload_rcv_len =
                sg_copy_from_buffer(job->reply_payload.sg_list,
                                job->reply_payload.sg_cnt,
-                               mb, size);
+                               from, size);
        job->reply->result = 0;
+
        dd_data->context_un.mbox.set_job = NULL;
        job->dd_data = NULL;
        job->job_done(job);
+       /* need to hold the lock until we call job done to hold off
+        * the timeout handler returning to the midlayer while
+        * we are stillprocessing the job
+        */
        spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
+
+       kfree(dd_data->context_un.mbox.mb);
        mempool_free(dd_data->context_un.mbox.pmboxq, phba->mbox_mem_pool);
-       kfree(mb);
+       kfree(dd_data->context_un.mbox.ext);
+       if (dd_data->context_un.mbox.dmp) {
+               dma_free_coherent(&phba->pcidev->dev,
+                       dd_data->context_un.mbox.dmp->size,
+                       dd_data->context_un.mbox.dmp->dma.virt,
+                       dd_data->context_un.mbox.dmp->dma.phys);
+               kfree(dd_data->context_un.mbox.dmp);
+       }
+       if (dd_data->context_un.mbox.rxbmp) {
+               lpfc_mbuf_free(phba, dd_data->context_un.mbox.rxbmp->virt,
+                       dd_data->context_un.mbox.rxbmp->phys);
+               kfree(dd_data->context_un.mbox.rxbmp);
+       }
        kfree(dd_data);
        return;
 }
@@ -2450,14 +2534,26 @@ static int lpfc_bsg_check_cmd_access(struct lpfc_hba *phba,
        case MBX_SET_DEBUG:
        case MBX_WRITE_WWN:
        case MBX_SLI4_CONFIG:
+       case MBX_READ_EVENT_LOG:
        case MBX_READ_EVENT_LOG_STATUS:
        case MBX_WRITE_EVENT_LOG:
        case MBX_PORT_CAPABILITIES:
        case MBX_PORT_IOV_CONTROL:
+       case MBX_RUN_BIU_DIAG64:
                break;
        case MBX_SET_VARIABLE:
-       case MBX_RUN_BIU_DIAG64:
-       case MBX_READ_EVENT_LOG:
+               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_READ_SPARM64:
        case MBX_READ_LA:
        case MBX_READ_LA64:
@@ -2492,97 +2588,365 @@ static uint32_t
 lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
        struct lpfc_vport *vport)
 {
-       LPFC_MBOXQ_t *pmboxq;
-       MAILBOX_t *pmb;
-       MAILBOX_t *mb;
-       struct bsg_job_data *dd_data;
+       LPFC_MBOXQ_t *pmboxq = NULL; /* internal mailbox queue */
+       MAILBOX_t *pmb; /* shortcut to the pmboxq mailbox */
+       /* a 4k buffer to hold the mb and extended data from/to the bsg */
+       MAILBOX_t *mb = NULL;
+       struct bsg_job_data *dd_data = NULL; /* bsg data tracking structure */
        uint32_t size;
+       struct lpfc_dmabuf *rxbmp = NULL; /* for biu diag */
+       struct lpfc_dmabufext *dmp = NULL; /* for biu diag */
+       struct ulp_bde64 *rxbpl = NULL;
+       struct dfc_mbox_req *mbox_req = (struct dfc_mbox_req *)
+               job->request->rqst_data.h_vendor.vendor_cmd;
+       uint8_t *ext = NULL;
        int rc = 0;
+       uint8_t *from;
+
+       /* in case no data is transferred */
+       job->reply->reply_payload_rcv_len = 0;
+
+       /* check if requested extended data lengths are valid */
+       if ((mbox_req->inExtWLen > MAILBOX_EXT_SIZE) ||
+               (mbox_req->outExtWLen > MAILBOX_EXT_SIZE)) {
+               rc = -ERANGE;
+               goto job_done;
+       }
 
        /* 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,
                                "2727 Failed allocation of dd_data\n");
-               return -ENOMEM;
+               rc = -ENOMEM;
+               goto job_done;
        }
 
-       mb = kzalloc(PAGE_SIZE, GFP_KERNEL);
+       mb = kzalloc(BSG_MBOX_SIZE, GFP_KERNEL);
        if (!mb) {
-               kfree(dd_data);
-               return -ENOMEM;
+               rc = -ENOMEM;
+               goto job_done;
        }
 
        pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
        if (!pmboxq) {
-               kfree(dd_data);
-               kfree(mb);
-               return -ENOMEM;
+               rc = -ENOMEM;
+               goto job_done;
        }
+       memset(pmboxq, 0, sizeof(LPFC_MBOXQ_t));
 
        size = job->request_payload.payload_len;
-       job->reply->reply_payload_rcv_len =
-               sg_copy_to_buffer(job->request_payload.sg_list,
-                               job->request_payload.sg_cnt,
-                               mb, size);
+       sg_copy_to_buffer(job->request_payload.sg_list,
+                       job->request_payload.sg_cnt,
+                       mb, size);
 
        rc = lpfc_bsg_check_cmd_access(phba, mb, vport);
-       if (rc != 0) {
-               kfree(dd_data);
-               kfree(mb);
-               mempool_free(pmboxq, phba->mbox_mem_pool);
-               return rc; /* must be negative */
-       }
+       if (rc != 0)
+               goto job_done; /* must be negative */
 
-       memset(pmboxq, 0, sizeof(LPFC_MBOXQ_t));
        pmb = &pmboxq->u.mb;
        memcpy(pmb, mb, sizeof(*pmb));
        pmb->mbxOwner = OWN_HOST;
-       pmboxq->context1 = NULL;
        pmboxq->vport = vport;
 
-       if ((vport->fc_flag & FC_OFFLINE_MODE) ||
-           (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE))) {
-               rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL);
-               if (rc != MBX_SUCCESS) {
-                       if (rc != MBX_TIMEOUT) {
-                               kfree(dd_data);
-                               kfree(mb);
-                               mempool_free(pmboxq, phba->mbox_mem_pool);
+       /* If HBA encountered an error attention, allow only DUMP
+        * or RESTART mailbox commands until the HBA is restarted.
+        */
+       if (phba->pport->stopped &&
+           pmb->mbxCommand != MBX_DUMP_MEMORY &&
+           pmb->mbxCommand != MBX_RESTART &&
+           pmb->mbxCommand != MBX_WRITE_VPARMS &&
+           pmb->mbxCommand != MBX_WRITE_WWN)
+               lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX,
+                               "2797 mbox: Issued mailbox cmd "
+                               "0x%x while in stopped state.\n",
+                               pmb->mbxCommand);
+
+       /* Don't allow mailbox commands to be sent when blocked
+        * or when in the middle of discovery
+        */
+       if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO) {
+               rc = -EAGAIN;
+               goto job_done;
+       }
+
+       /* extended mailbox commands will need an extended buffer */
+       if (mbox_req->inExtWLen || mbox_req->outExtWLen) {
+               ext = kzalloc(MAILBOX_EXT_SIZE, GFP_KERNEL);
+               if (!ext) {
+                       rc = -ENOMEM;
+                       goto job_done;
+               }
+
+               /* any data for the device? */
+               if (mbox_req->inExtWLen) {
+                       from = (uint8_t *)mb;
+                       from += sizeof(MAILBOX_t);
+                       memcpy((uint8_t *)ext, from,
+                               mbox_req->inExtWLen * sizeof(uint32_t));
+               }
+
+               pmboxq->context2 = ext;
+               pmboxq->in_ext_byte_len =
+                       mbox_req->inExtWLen *
+                       sizeof(uint32_t);
+               pmboxq->out_ext_byte_len =
+                       mbox_req->outExtWLen *
+                       sizeof(uint32_t);
+               pmboxq->mbox_offset_word =
+                       mbox_req->mbOffset;
+               pmboxq->context2 = ext;
+               pmboxq->in_ext_byte_len =
+                       mbox_req->inExtWLen * sizeof(uint32_t);
+               pmboxq->out_ext_byte_len =
+                       mbox_req->outExtWLen * sizeof(uint32_t);
+               pmboxq->mbox_offset_word = mbox_req->mbOffset;
+       }
+
+       /* biu diag will need a kernel buffer to transfer the data
+        * allocate our own buffer and setup the mailbox command to
+        * use ours
+        */
+       if (pmb->mbxCommand == MBX_RUN_BIU_DIAG64) {
+               uint32_t transmit_length = pmb->un.varWords[1];
+               uint32_t receive_length = pmb->un.varWords[4];
+               /* transmit length cannot be greater than receive length or
+                * mailbox extension size
+                */
+               if ((transmit_length > receive_length) ||
+                       (transmit_length > MAILBOX_EXT_SIZE)) {
+                       rc = -ERANGE;
+                       goto job_done;
+               }
+
+               rxbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+               if (!rxbmp) {
+                       rc = -ENOMEM;
+                       goto job_done;
+               }
+
+               rxbmp->virt = lpfc_mbuf_alloc(phba, 0, &rxbmp->phys);
+               if (!rxbmp->virt) {
+                       rc = -ENOMEM;
+                       goto job_done;
+               }
+
+               INIT_LIST_HEAD(&rxbmp->list);
+               rxbpl = (struct ulp_bde64 *) rxbmp->virt;
+               dmp = diag_cmd_data_alloc(phba, rxbpl, transmit_length, 0);
+               if (!dmp) {
+                       rc = -ENOMEM;
+                       goto job_done;
+               }
+
+               INIT_LIST_HEAD(&dmp->dma.list);
+               pmb->un.varBIUdiag.un.s2.xmit_bde64.addrHigh =
+                       putPaddrHigh(dmp->dma.phys);
+               pmb->un.varBIUdiag.un.s2.xmit_bde64.addrLow =
+                       putPaddrLow(dmp->dma.phys);
+
+               pmb->un.varBIUdiag.un.s2.rcv_bde64.addrHigh =
+                       putPaddrHigh(dmp->dma.phys +
+                               pmb->un.varBIUdiag.un.s2.
+                                       xmit_bde64.tus.f.bdeSize);
+               pmb->un.varBIUdiag.un.s2.rcv_bde64.addrLow =
+                       putPaddrLow(dmp->dma.phys +
+                               pmb->un.varBIUdiag.un.s2.
+                                       xmit_bde64.tus.f.bdeSize);
+
+               /* copy the transmit data found in the mailbox extension area */
+               from = (uint8_t *)mb;
+               from += sizeof(MAILBOX_t);
+               memcpy((uint8_t *)dmp->dma.virt, from, transmit_length);
+       } else if (pmb->mbxCommand == MBX_READ_EVENT_LOG) {
+               struct READ_EVENT_LOG_VAR *rdEventLog =
+                       &pmb->un.varRdEventLog ;
+               uint32_t receive_length = rdEventLog->rcv_bde64.tus.f.bdeSize;
+               uint32_t mode =  bf_get(lpfc_event_log, rdEventLog);
+
+               /* receive length cannot be greater than mailbox
+                * extension size
+                */
+               if (receive_length > MAILBOX_EXT_SIZE) {
+                       rc = -ERANGE;
+                       goto job_done;
+               }
+
+               /* mode zero uses a bde like biu diags command */
+               if (mode == 0) {
+
+                       /* rebuild the command for sli4 using our own buffers
+                       * like we do for biu diags
+                       */
+
+                       rxbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+                       if (!rxbmp) {
+                               rc = -ENOMEM;
+                               goto job_done;
+                       }
+
+                       rxbmp->virt = lpfc_mbuf_alloc(phba, 0, &rxbmp->phys);
+                       rxbpl = (struct ulp_bde64 *) rxbmp->virt;
+                       if (rxbpl) {
+                               INIT_LIST_HEAD(&rxbmp->list);
+                               dmp = diag_cmd_data_alloc(phba, rxbpl,
+                                       receive_length, 0);
+                       }
+
+                       if (!dmp) {
+                               rc = -ENOMEM;
+                               goto job_done;
                        }
-                       return  (rc == MBX_TIMEOUT) ? -ETIME : -ENODEV;
+
+                       INIT_LIST_HEAD(&dmp->dma.list);
+                       pmb->un.varWords[3] = putPaddrLow(dmp->dma.phys);
+                       pmb->un.varWords[4] = putPaddrHigh(dmp->dma.phys);
                }
+       } else if (phba->sli_rev == LPFC_SLI_REV4) {
+               if (pmb->mbxCommand == MBX_DUMP_MEMORY) {
+                       /* rebuild the command for sli4 using our own buffers
+                       * like we do for biu diags
+                       */
+                       uint32_t receive_length = pmb->un.varWords[2];
+                       /* receive length cannot be greater than mailbox
+                        * extension size
+                        */
+                       if ((receive_length == 0) ||
+                               (receive_length > MAILBOX_EXT_SIZE)) {
+                               rc = -ERANGE;
+                               goto job_done;
+                       }
 
-               memcpy(mb, pmb, sizeof(*pmb));
-               job->reply->reply_payload_rcv_len =
-                       sg_copy_from_buffer(job->reply_payload.sg_list,
-                                       job->reply_payload.sg_cnt,
-                                       mb, size);
-               kfree(dd_data);
-               kfree(mb);
-               mempool_free(pmboxq, phba->mbox_mem_pool);
-               /* not waiting mbox already done */
-               return 0;
+                       rxbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+                       if (!rxbmp) {
+                               rc = -ENOMEM;
+                               goto job_done;
+                       }
+
+                       rxbmp->virt = lpfc_mbuf_alloc(phba, 0, &rxbmp->phys);
+                       if (!rxbmp->virt) {
+                               rc = -ENOMEM;
+                               goto job_done;
+                       }
+
+                       INIT_LIST_HEAD(&rxbmp->list);
+                       rxbpl = (struct ulp_bde64 *) rxbmp->virt;
+                       dmp = diag_cmd_data_alloc(phba, rxbpl, receive_length,
+                                               0);
+                       if (!dmp) {
+                               rc = -ENOMEM;
+                               goto job_done;
+                       }
+
+                       INIT_LIST_HEAD(&dmp->dma.list);
+                       pmb->un.varWords[3] = putPaddrLow(dmp->dma.phys);
+                       pmb->un.varWords[4] = putPaddrHigh(dmp->dma.phys);
+               } else if ((pmb->mbxCommand == MBX_UPDATE_CFG) &&
+                       pmb->un.varUpdateCfg.co) {
+                       struct ulp_bde64 *bde =
+                               (struct ulp_bde64 *)&pmb->un.varWords[4];
+
+                       /* bde size cannot be greater than mailbox ext size */
+                       if (bde->tus.f.bdeSize > MAILBOX_EXT_SIZE) {
+                               rc = -ERANGE;
+                               goto job_done;
+                       }
+
+                       rxbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+                       if (!rxbmp) {
+                               rc = -ENOMEM;
+                               goto job_done;
+                       }
+
+                       rxbmp->virt = lpfc_mbuf_alloc(phba, 0, &rxbmp->phys);
+                       if (!rxbmp->virt) {
+                               rc = -ENOMEM;
+                               goto job_done;
+                       }
+
+                       INIT_LIST_HEAD(&rxbmp->list);
+                       rxbpl = (struct ulp_bde64 *) rxbmp->virt;
+                       dmp = diag_cmd_data_alloc(phba, rxbpl,
+                                       bde->tus.f.bdeSize, 0);
+                       if (!dmp) {
+                               rc = -ENOMEM;
+                               goto job_done;
+                       }
+
+                       INIT_LIST_HEAD(&dmp->dma.list);
+                       bde->addrHigh = putPaddrHigh(dmp->dma.phys);
+                       bde->addrLow = putPaddrLow(dmp->dma.phys);
+
+                       /* copy the transmit data found in the mailbox
+                        * extension area
+                        */
+                       from = (uint8_t *)mb;
+                       from += sizeof(MAILBOX_t);
+                       memcpy((uint8_t *)dmp->dma.virt, from,
+                               bde->tus.f.bdeSize);
+               }
        }
 
+       dd_data->context_un.mbox.rxbmp = rxbmp;
+       dd_data->context_un.mbox.dmp = dmp;
+
        /* setup wake call as IOCB callback */
        pmboxq->mbox_cmpl = lpfc_bsg_wake_mbox_wait;
+
        /* setup context field to pass wait_queue pointer to wake function */
        pmboxq->context1 = dd_data;
        dd_data->type = TYPE_MBOX;
        dd_data->context_un.mbox.pmboxq = pmboxq;
        dd_data->context_un.mbox.mb = mb;
        dd_data->context_un.mbox.set_job = job;
+       dd_data->context_un.mbox.ext = ext;
+       dd_data->context_un.mbox.mbOffset = mbox_req->mbOffset;
+       dd_data->context_un.mbox.inExtWLen = mbox_req->inExtWLen;
+       dd_data->context_un.mbox.outExtWLen = mbox_req->outExtWLen;
        job->dd_data = dd_data;
+
+       if ((vport->fc_flag & FC_OFFLINE_MODE) ||
+           (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE))) {
+               rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL);
+               if (rc != MBX_SUCCESS) {
+                       rc = (rc == MBX_TIMEOUT) ? -ETIME : -ENODEV;
+                       goto job_done;
+               }
+
+               /* job finished, copy the data */
+               memcpy(mb, pmb, sizeof(*pmb));
+               job->reply->reply_payload_rcv_len =
+                       sg_copy_from_buffer(job->reply_payload.sg_list,
+                                       job->reply_payload.sg_cnt,
+                                       mb, size);
+               /* not waiting mbox already done */
+               rc = 0;
+               goto job_done;
+       }
+
        rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT);
-       if ((rc != MBX_SUCCESS) && (rc != MBX_BUSY)) {
-               kfree(dd_data);
-               kfree(mb);
+       if ((rc == MBX_SUCCESS) || (rc == MBX_BUSY))
+               return 1; /* job started */
+
+job_done:
+       /* common exit for error or job completed inline */
+       kfree(mb);
+       if (pmboxq)
                mempool_free(pmboxq, phba->mbox_mem_pool);
-               return -EIO;
+       kfree(ext);
+       if (dmp) {
+               dma_free_coherent(&phba->pcidev->dev,
+                       dmp->size, dmp->dma.virt,
+                               dmp->dma.phys);
+               kfree(dmp);
+       }
+       if (rxbmp) {
+               lpfc_mbuf_free(phba, rxbmp->virt, rxbmp->phys);
+               kfree(rxbmp);
        }
+       kfree(dd_data);
 
-       return 1;
+       return rc;
 }
 
 /**
@@ -2607,7 +2971,12 @@ lpfc_bsg_mbox_cmd(struct fc_bsg_job *job)
                goto job_error;
        }
 
-       if (job->request_payload.payload_len != PAGE_SIZE) {
+       if (job->request_payload.payload_len != BSG_MBOX_SIZE) {
+               rc = -EINVAL;
+               goto job_error;
+       }
+
+       if (job->reply_payload.payload_len != BSG_MBOX_SIZE) {
                rc = -EINVAL;
                goto job_error;
        }
@@ -2638,6 +3007,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 +3329,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 +3392,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;
@@ -2772,9 +3437,21 @@ lpfc_bsg_timeout(struct fc_bsg_job *job)
                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);
                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;