123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905 |
- /*******************************************************************
- * This file is part of the Emulex Linux Device Driver for *
- * Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2009 Emulex. All rights reserved. *
- * EMULEX and SLI are trademarks of Emulex. *
- * www.emulex.com *
- * *
- * This program is free software; you can redistribute it and/or *
- * modify it under the terms of version 2 of the GNU General *
- * Public License as published by the Free Software Foundation. *
- * This program is distributed in the hope that it will be useful. *
- * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND *
- * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, *
- * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE *
- * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD *
- * TO BE LEGALLY INVALID. See the GNU General Public License for *
- * more details, a copy of which can be found in the file COPYING *
- * included with this package. *
- *******************************************************************/
- #include <linux/interrupt.h>
- #include <linux/mempool.h>
- #include <linux/pci.h>
- #include <scsi/scsi.h>
- #include <scsi/scsi_host.h>
- #include <scsi/scsi_transport_fc.h>
- #include <scsi/scsi_bsg_fc.h>
- #include <scsi/fc/fc_fs.h>
- #include "lpfc_hw4.h"
- #include "lpfc_hw.h"
- #include "lpfc_sli.h"
- #include "lpfc_sli4.h"
- #include "lpfc_nl.h"
- #include "lpfc_disc.h"
- #include "lpfc_scsi.h"
- #include "lpfc.h"
- #include "lpfc_logmsg.h"
- #include "lpfc_crtn.h"
- #include "lpfc_vport.h"
- #include "lpfc_version.h"
- /**
- * lpfc_bsg_rport_ct - send a CT command from a bsg request
- * @job: fc_bsg_job to handle
- */
- static int
- lpfc_bsg_rport_ct(struct fc_bsg_job *job)
- {
- struct Scsi_Host *shost = job->shost;
- struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
- struct lpfc_hba *phba = vport->phba;
- struct lpfc_rport_data *rdata = job->rport->dd_data;
- struct lpfc_nodelist *ndlp = rdata->pnode;
- struct ulp_bde64 *bpl = NULL;
- uint32_t timeout;
- struct lpfc_iocbq *cmdiocbq = NULL;
- struct lpfc_iocbq *rspiocbq = NULL;
- IOCB_t *cmd;
- IOCB_t *rsp;
- struct lpfc_dmabuf *bmp = NULL;
- int request_nseg;
- int reply_nseg;
- struct scatterlist *sgel = NULL;
- int numbde;
- dma_addr_t busaddr;
- int rc = 0;
- /* in case no data is transferred */
- job->reply->reply_payload_rcv_len = 0;
- if (!lpfc_nlp_get(ndlp)) {
- job->reply->result = -ENODEV;
- return 0;
- }
- if (ndlp->nlp_flag & NLP_ELS_SND_MASK) {
- rc = -ENODEV;
- goto free_ndlp_exit;
- }
- spin_lock_irq(shost->host_lock);
- cmdiocbq = lpfc_sli_get_iocbq(phba);
- if (!cmdiocbq) {
- rc = -ENOMEM;
- spin_unlock_irq(shost->host_lock);
- goto free_ndlp_exit;
- }
- cmd = &cmdiocbq->iocb;
- rspiocbq = lpfc_sli_get_iocbq(phba);
- if (!rspiocbq) {
- rc = -ENOMEM;
- goto free_cmdiocbq;
- }
- spin_unlock_irq(shost->host_lock);
- rsp = &rspiocbq->iocb;
- bmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
- if (!bmp) {
- rc = -ENOMEM;
- spin_lock_irq(shost->host_lock);
- goto free_rspiocbq;
- }
- spin_lock_irq(shost->host_lock);
- bmp->virt = lpfc_mbuf_alloc(phba, 0, &bmp->phys);
- if (!bmp->virt) {
- rc = -ENOMEM;
- goto free_bmp;
- }
- spin_unlock_irq(shost->host_lock);
- 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->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->ulpCommand = CMD_GEN_REQUEST64_CR;
- 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_CTL;
- cmd->un.genreq64.w5.hcsw.Type = FC_TYPE_CT;
- cmd->ulpBdeCount = 1;
- cmd->ulpLe = 1;
- cmd->ulpClass = CLASS3;
- cmd->ulpContext = ndlp->nlp_rpi;
- cmd->ulpOwner = OWN_CHIP;
- cmdiocbq->vport = phba->pport;
- cmdiocbq->context1 = NULL;
- cmdiocbq->context2 = NULL;
- cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC;
- timeout = phba->fc_ratov * 2;
- job->dd_data = cmdiocbq;
- rc = lpfc_sli_issue_iocb_wait(phba, LPFC_ELS_RING, cmdiocbq, rspiocbq,
- timeout + LPFC_DRVR_TIMEOUT);
- if (rc != IOCB_TIMEDOUT) {
- 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);
- }
- if (rc == IOCB_TIMEDOUT) {
- lpfc_sli_release_iocbq(phba, rspiocbq);
- rc = -EACCES;
- goto free_ndlp_exit;
- }
- if (rc != IOCB_SUCCESS) {
- rc = -EACCES;
- goto free_outdmp;
- }
- 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;
- }
- goto free_outdmp;
- }
- } else
- job->reply->reply_payload_rcv_len =
- rsp->un.genreq64.bdl.bdeSize;
- free_outdmp:
- spin_lock_irq(shost->host_lock);
- lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
- free_bmp:
- kfree(bmp);
- free_rspiocbq:
- lpfc_sli_release_iocbq(phba, rspiocbq);
- free_cmdiocbq:
- lpfc_sli_release_iocbq(phba, cmdiocbq);
- spin_unlock_irq(shost->host_lock);
- free_ndlp_exit:
- lpfc_nlp_put(ndlp);
- /* make error code available to userspace */
- job->reply->result = rc;
- /* complete the job back to userspace */
- job->job_done(job);
- return 0;
- }
- /**
- * lpfc_bsg_rport_els - send an ELS command from a bsg request
- * @job: fc_bsg_job to handle
- */
- static int
- lpfc_bsg_rport_els(struct fc_bsg_job *job)
- {
- struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
- struct lpfc_hba *phba = vport->phba;
- struct lpfc_rport_data *rdata = job->rport->dd_data;
- struct lpfc_nodelist *ndlp = rdata->pnode;
- uint32_t elscmd;
- uint32_t cmdsize;
- uint32_t rspsize;
- struct lpfc_iocbq *rspiocbq;
- struct lpfc_iocbq *cmdiocbq;
- IOCB_t *rsp;
- uint16_t rpi = 0;
- struct lpfc_dmabuf *pcmd;
- struct lpfc_dmabuf *prsp;
- struct lpfc_dmabuf *pbuflist = NULL;
- struct ulp_bde64 *bpl;
- int iocb_status;
- int request_nseg;
- int reply_nseg;
- struct scatterlist *sgel = NULL;
- int numbde;
- dma_addr_t busaddr;
- int rc = 0;
- /* in case no data is transferred */
- job->reply->reply_payload_rcv_len = 0;
- if (!lpfc_nlp_get(ndlp)) {
- rc = -ENODEV;
- goto out;
- }
- elscmd = job->request->rqst_data.r_els.els_code;
- cmdsize = job->request_payload.payload_len;
- rspsize = job->reply_payload.payload_len;
- rspiocbq = lpfc_sli_get_iocbq(phba);
- if (!rspiocbq) {
- lpfc_nlp_put(ndlp);
- rc = -ENOMEM;
- goto out;
- }
- rsp = &rspiocbq->iocb;
- rpi = ndlp->nlp_rpi;
- cmdiocbq = lpfc_prep_els_iocb(phba->pport, 1, cmdsize, 0, ndlp,
- ndlp->nlp_DID, elscmd);
- if (!cmdiocbq) {
- lpfc_sli_release_iocbq(phba, rspiocbq);
- return -EIO;
- }
- job->dd_data = cmdiocbq;
- pcmd = (struct lpfc_dmabuf *) cmdiocbq->context2;
- prsp = (struct lpfc_dmabuf *) pcmd->list.next;
- lpfc_mbuf_free(phba, pcmd->virt, pcmd->phys);
- kfree(pcmd);
- lpfc_mbuf_free(phba, prsp->virt, prsp->phys);
- 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);
- cmdiocbq->iocb.ulpContext = rpi;
- cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC;
- cmdiocbq->context1 = NULL;
- cmdiocbq->context2 = NULL;
- iocb_status = lpfc_sli_issue_iocb_wait(phba, LPFC_ELS_RING, cmdiocbq,
- rspiocbq, (phba->fc_ratov * 2)
- + LPFC_DRVR_TIMEOUT);
- /* release the new ndlp once the iocb completes */
- lpfc_nlp_put(ndlp);
- if (iocb_status != IOCB_TIMEDOUT) {
- 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);
- }
- if (iocb_status == IOCB_SUCCESS) {
- if (rsp->ulpStatus == IOSTAT_SUCCESS) {
- job->reply->reply_payload_rcv_len =
- rsp->un.elsreq64.bdl.bdeSize;
- rc = 0;
- } else if (rsp->ulpStatus == IOSTAT_LS_RJT) {
- struct fc_bsg_ctels_reply *els_reply;
- /* LS_RJT data returned in word 4 */
- uint8_t *rjt_data = (uint8_t *)&rsp->un.ulpWord[4];
- els_reply = &job->reply->reply_data.ctels_reply;
- job->reply->result = 0;
- els_reply->status = FC_CTELS_STATUS_REJECT;
- els_reply->rjt_data.action = rjt_data[0];
- els_reply->rjt_data.reason_code = rjt_data[1];
- els_reply->rjt_data.reason_explanation = rjt_data[2];
- els_reply->rjt_data.vendor_unique = rjt_data[3];
- } else
- rc = -EIO;
- } else
- rc = -EIO;
- if (iocb_status != IOCB_TIMEDOUT)
- lpfc_els_free_iocb(phba, cmdiocbq);
- lpfc_sli_release_iocbq(phba, rspiocbq);
- out:
- /* make error code available to userspace */
- job->reply->result = rc;
- /* complete the job back to userspace */
- job->job_done(job);
- return 0;
- }
- struct lpfc_ct_event {
- struct list_head node;
- int ref;
- wait_queue_head_t wq;
- /* Event type and waiter identifiers */
- uint32_t type_mask;
- uint32_t req_id;
- uint32_t reg_id;
- /* next two flags are here for the auto-delete logic */
- unsigned long wait_time_stamp;
- int waiting;
- /* seen and not seen events */
- struct list_head events_to_get;
- struct list_head events_to_see;
- };
- struct event_data {
- struct list_head node;
- uint32_t type;
- uint32_t immed_dat;
- void *data;
- uint32_t len;
- };
- static struct lpfc_ct_event *
- lpfc_ct_event_new(int ev_reg_id, uint32_t ev_req_id)
- {
- struct lpfc_ct_event *evt = kzalloc(sizeof(*evt), GFP_KERNEL);
- if (!evt)
- return NULL;
- INIT_LIST_HEAD(&evt->events_to_get);
- INIT_LIST_HEAD(&evt->events_to_see);
- evt->req_id = ev_req_id;
- evt->reg_id = ev_reg_id;
- evt->wait_time_stamp = jiffies;
- init_waitqueue_head(&evt->wq);
- return evt;
- }
- static void
- lpfc_ct_event_free(struct lpfc_ct_event *evt)
- {
- struct event_data *ed;
- list_del(&evt->node);
- while (!list_empty(&evt->events_to_get)) {
- ed = list_entry(evt->events_to_get.next, typeof(*ed), node);
- list_del(&ed->node);
- kfree(ed->data);
- kfree(ed);
- }
- while (!list_empty(&evt->events_to_see)) {
- ed = list_entry(evt->events_to_see.next, typeof(*ed), node);
- list_del(&ed->node);
- kfree(ed->data);
- kfree(ed);
- }
- kfree(evt);
- }
- static inline void
- lpfc_ct_event_ref(struct lpfc_ct_event *evt)
- {
- evt->ref++;
- }
- static inline void
- lpfc_ct_event_unref(struct lpfc_ct_event *evt)
- {
- if (--evt->ref < 0)
- lpfc_ct_event_free(evt);
- }
- #define SLI_CT_ELX_LOOPBACK 0x10
- enum ELX_LOOPBACK_CMD {
- ELX_LOOPBACK_XRI_SETUP,
- ELX_LOOPBACK_DATA,
- };
- /**
- * lpfc_bsg_ct_unsol_event - process an unsolicited CT command
- * @phba:
- * @pring:
- * @piocbq:
- *
- * This function is called when an unsolicited CT command is received. It
- * forwards the event to any processes registerd to receive CT events.
- */
- void
- lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
- struct lpfc_iocbq *piocbq)
- {
- uint32_t evt_req_id = 0;
- uint32_t cmd;
- uint32_t len;
- struct lpfc_dmabuf *dmabuf = NULL;
- struct lpfc_ct_event *evt;
- struct event_data *evt_dat = NULL;
- struct lpfc_iocbq *iocbq;
- size_t offset = 0;
- struct list_head head;
- struct ulp_bde64 *bde;
- dma_addr_t dma_addr;
- int i;
- struct lpfc_dmabuf *bdeBuf1 = piocbq->context2;
- struct lpfc_dmabuf *bdeBuf2 = piocbq->context3;
- struct lpfc_hbq_entry *hbqe;
- struct lpfc_sli_ct_request *ct_req;
- INIT_LIST_HEAD(&head);
- list_add_tail(&head, &piocbq->list);
- if (piocbq->iocb.ulpBdeCount == 0 ||
- piocbq->iocb.un.cont64[0].tus.f.bdeSize == 0)
- goto error_ct_unsol_exit;
- if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED)
- dmabuf = bdeBuf1;
- else {
- dma_addr = getPaddr(piocbq->iocb.un.cont64[0].addrHigh,
- piocbq->iocb.un.cont64[0].addrLow);
- dmabuf = lpfc_sli_ringpostbuf_get(phba, pring, dma_addr);
- }
- ct_req = (struct lpfc_sli_ct_request *)dmabuf->virt;
- evt_req_id = ct_req->FsType;
- cmd = ct_req->CommandResponse.bits.CmdRsp;
- len = ct_req->CommandResponse.bits.Size;
- if (!(phba->sli3_options & LPFC_SLI3_HBQ_ENABLED))
- lpfc_sli_ringpostbuf_put(phba, pring, dmabuf);
- mutex_lock(&phba->ct_event_mutex);
- list_for_each_entry(evt, &phba->ct_ev_waiters, node) {
- if (evt->req_id != evt_req_id)
- continue;
- lpfc_ct_event_ref(evt);
- evt_dat = kzalloc(sizeof(*evt_dat), GFP_KERNEL);
- if (!evt_dat) {
- lpfc_ct_event_unref(evt);
- lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
- "2614 Memory allocation failed for "
- "CT event\n");
- break;
- }
- mutex_unlock(&phba->ct_event_mutex);
- if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) {
- /* take accumulated byte count from the last iocbq */
- iocbq = list_entry(head.prev, typeof(*iocbq), list);
- evt_dat->len = iocbq->iocb.unsli3.rcvsli3.acc_len;
- } else {
- list_for_each_entry(iocbq, &head, list) {
- for (i = 0; i < iocbq->iocb.ulpBdeCount; i++)
- evt_dat->len +=
- iocbq->iocb.un.cont64[i].tus.f.bdeSize;
- }
- }
- evt_dat->data = kzalloc(evt_dat->len, GFP_KERNEL);
- if (!evt_dat->data) {
- lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
- "2615 Memory allocation failed for "
- "CT event data, size %d\n",
- evt_dat->len);
- kfree(evt_dat);
- mutex_lock(&phba->ct_event_mutex);
- lpfc_ct_event_unref(evt);
- mutex_unlock(&phba->ct_event_mutex);
- goto error_ct_unsol_exit;
- }
- list_for_each_entry(iocbq, &head, list) {
- if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) {
- bdeBuf1 = iocbq->context2;
- bdeBuf2 = iocbq->context3;
- }
- for (i = 0; i < iocbq->iocb.ulpBdeCount; i++) {
- int size = 0;
- if (phba->sli3_options &
- LPFC_SLI3_HBQ_ENABLED) {
- if (i == 0) {
- hbqe = (struct lpfc_hbq_entry *)
- &iocbq->iocb.un.ulpWord[0];
- size = hbqe->bde.tus.f.bdeSize;
- dmabuf = bdeBuf1;
- } else if (i == 1) {
- hbqe = (struct lpfc_hbq_entry *)
- &iocbq->iocb.unsli3.
- sli3Words[4];
- size = hbqe->bde.tus.f.bdeSize;
- dmabuf = bdeBuf2;
- }
- if ((offset + size) > evt_dat->len)
- size = evt_dat->len - offset;
- } else {
- size = iocbq->iocb.un.cont64[i].
- tus.f.bdeSize;
- bde = &iocbq->iocb.un.cont64[i];
- dma_addr = getPaddr(bde->addrHigh,
- bde->addrLow);
- dmabuf = lpfc_sli_ringpostbuf_get(phba,
- pring, dma_addr);
- }
- if (!dmabuf) {
- lpfc_printf_log(phba, KERN_ERR,
- LOG_LIBDFC, "2616 No dmabuf "
- "found for iocbq 0x%p\n",
- iocbq);
- kfree(evt_dat->data);
- kfree(evt_dat);
- mutex_lock(&phba->ct_event_mutex);
- lpfc_ct_event_unref(evt);
- mutex_unlock(&phba->ct_event_mutex);
- goto error_ct_unsol_exit;
- }
- memcpy((char *)(evt_dat->data) + offset,
- dmabuf->virt, size);
- offset += size;
- if (evt_req_id != SLI_CT_ELX_LOOPBACK &&
- !(phba->sli3_options &
- LPFC_SLI3_HBQ_ENABLED)) {
- lpfc_sli_ringpostbuf_put(phba, pring,
- dmabuf);
- } else {
- switch (cmd) {
- case ELX_LOOPBACK_XRI_SETUP:
- if (!(phba->sli3_options &
- LPFC_SLI3_HBQ_ENABLED))
- lpfc_post_buffer(phba,
- pring,
- 1);
- else
- lpfc_in_buf_free(phba,
- dmabuf);
- break;
- default:
- if (!(phba->sli3_options &
- LPFC_SLI3_HBQ_ENABLED))
- lpfc_post_buffer(phba,
- pring,
- 1);
- break;
- }
- }
- }
- }
- mutex_lock(&phba->ct_event_mutex);
- if (phba->sli_rev == LPFC_SLI_REV4) {
- evt_dat->immed_dat = phba->ctx_idx;
- phba->ctx_idx = (phba->ctx_idx + 1) % 64;
- phba->ct_ctx[evt_dat->immed_dat].oxid =
- piocbq->iocb.ulpContext;
- phba->ct_ctx[evt_dat->immed_dat].SID =
- piocbq->iocb.un.rcvels.remoteID;
- } else
- evt_dat->immed_dat = piocbq->iocb.ulpContext;
- evt_dat->type = FC_REG_CT_EVENT;
- list_add(&evt_dat->node, &evt->events_to_see);
- wake_up_interruptible(&evt->wq);
- lpfc_ct_event_unref(evt);
- if (evt_req_id == SLI_CT_ELX_LOOPBACK)
- break;
- }
- mutex_unlock(&phba->ct_event_mutex);
- error_ct_unsol_exit:
- if (!list_empty(&head))
- list_del(&head);
- return;
- }
- /**
- * lpfc_bsg_set_event - process a SET_EVENT bsg vendor command
- * @job: SET_EVENT fc_bsg_job
- */
- static int
- lpfc_bsg_set_event(struct fc_bsg_job *job)
- {
- struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
- struct lpfc_hba *phba = vport->phba;
- struct set_ct_event *event_req;
- struct lpfc_ct_event *evt;
- int rc = 0;
- if (job->request_len <
- sizeof(struct fc_bsg_request) + sizeof(struct set_ct_event)) {
- lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
- "2612 Received SET_CT_EVENT below minimum "
- "size\n");
- return -EINVAL;
- }
- event_req = (struct set_ct_event *)
- job->request->rqst_data.h_vendor.vendor_cmd;
- mutex_lock(&phba->ct_event_mutex);
- list_for_each_entry(evt, &phba->ct_ev_waiters, node) {
- if (evt->reg_id == event_req->ev_reg_id) {
- lpfc_ct_event_ref(evt);
- evt->wait_time_stamp = jiffies;
- break;
- }
- }
- mutex_unlock(&phba->ct_event_mutex);
- if (&evt->node == &phba->ct_ev_waiters) {
- /* no event waiting struct yet - first call */
- evt = lpfc_ct_event_new(event_req->ev_reg_id,
- event_req->ev_req_id);
- if (!evt) {
- lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
- "2617 Failed allocation of event "
- "waiter\n");
- return -ENOMEM;
- }
- mutex_lock(&phba->ct_event_mutex);
- list_add(&evt->node, &phba->ct_ev_waiters);
- lpfc_ct_event_ref(evt);
- mutex_unlock(&phba->ct_event_mutex);
- }
- evt->waiting = 1;
- if (wait_event_interruptible(evt->wq,
- !list_empty(&evt->events_to_see))) {
- mutex_lock(&phba->ct_event_mutex);
- lpfc_ct_event_unref(evt); /* release ref */
- lpfc_ct_event_unref(evt); /* delete */
- mutex_unlock(&phba->ct_event_mutex);
- rc = -EINTR;
- goto set_event_out;
- }
- evt->wait_time_stamp = jiffies;
- evt->waiting = 0;
- mutex_lock(&phba->ct_event_mutex);
- list_move(evt->events_to_see.prev, &evt->events_to_get);
- lpfc_ct_event_unref(evt); /* release ref */
- mutex_unlock(&phba->ct_event_mutex);
- set_event_out:
- /* set_event carries no reply payload */
- job->reply->reply_payload_rcv_len = 0;
- /* make error code available to userspace */
- job->reply->result = rc;
- /* complete the job back to userspace */
- job->job_done(job);
- return 0;
- }
- /**
- * lpfc_bsg_get_event - process a GET_EVENT bsg vendor command
- * @job: GET_EVENT fc_bsg_job
- */
- static int
- lpfc_bsg_get_event(struct fc_bsg_job *job)
- {
- struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
- struct lpfc_hba *phba = vport->phba;
- struct get_ct_event *event_req;
- struct get_ct_event_reply *event_reply;
- struct lpfc_ct_event *evt;
- struct event_data *evt_dat = NULL;
- int rc = 0;
- if (job->request_len <
- sizeof(struct fc_bsg_request) + sizeof(struct get_ct_event)) {
- lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
- "2613 Received GET_CT_EVENT request below "
- "minimum size\n");
- return -EINVAL;
- }
- event_req = (struct get_ct_event *)
- job->request->rqst_data.h_vendor.vendor_cmd;
- event_reply = (struct get_ct_event_reply *)
- job->reply->reply_data.vendor_reply.vendor_rsp;
- mutex_lock(&phba->ct_event_mutex);
- list_for_each_entry(evt, &phba->ct_ev_waiters, node) {
- if (evt->reg_id == event_req->ev_reg_id) {
- if (list_empty(&evt->events_to_get))
- break;
- lpfc_ct_event_ref(evt);
- evt->wait_time_stamp = jiffies;
- evt_dat = list_entry(evt->events_to_get.prev,
- struct event_data, node);
- list_del(&evt_dat->node);
- break;
- }
- }
- mutex_unlock(&phba->ct_event_mutex);
- if (!evt_dat) {
- job->reply->reply_payload_rcv_len = 0;
- rc = -ENOENT;
- goto error_get_event_exit;
- }
- if (evt_dat->len > job->reply_payload.payload_len) {
- evt_dat->len = job->reply_payload.payload_len;
- lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
- "2618 Truncated event data at %d "
- "bytes\n",
- job->reply_payload.payload_len);
- }
- event_reply->immed_data = evt_dat->immed_dat;
- if (evt_dat->len > 0)
- job->reply->reply_payload_rcv_len =
- sg_copy_from_buffer(job->reply_payload.sg_list,
- job->reply_payload.sg_cnt,
- evt_dat->data, evt_dat->len);
- else
- job->reply->reply_payload_rcv_len = 0;
- rc = 0;
- if (evt_dat)
- kfree(evt_dat->data);
- kfree(evt_dat);
- mutex_lock(&phba->ct_event_mutex);
- lpfc_ct_event_unref(evt);
- mutex_unlock(&phba->ct_event_mutex);
- error_get_event_exit:
- /* make error code available to userspace */
- job->reply->result = rc;
- /* complete the job back to userspace */
- job->job_done(job);
- return rc;
- }
- /**
- * lpfc_bsg_hst_vendor - process a vendor-specific fc_bsg_job
- * @job: fc_bsg_job to handle
- */
- static int
- lpfc_bsg_hst_vendor(struct fc_bsg_job *job)
- {
- int command = job->request->rqst_data.h_vendor.vendor_cmd[0];
- switch (command) {
- case LPFC_BSG_VENDOR_SET_CT_EVENT:
- return lpfc_bsg_set_event(job);
- break;
- case LPFC_BSG_VENDOR_GET_CT_EVENT:
- return lpfc_bsg_get_event(job);
- break;
- default:
- return -EINVAL;
- }
- }
- /**
- * lpfc_bsg_request - handle a bsg request from the FC transport
- * @job: fc_bsg_job to handle
- */
- int
- lpfc_bsg_request(struct fc_bsg_job *job)
- {
- uint32_t msgcode;
- int rc = -EINVAL;
- msgcode = job->request->msgcode;
- switch (msgcode) {
- case FC_BSG_HST_VENDOR:
- rc = lpfc_bsg_hst_vendor(job);
- break;
- case FC_BSG_RPT_ELS:
- rc = lpfc_bsg_rport_els(job);
- break;
- case FC_BSG_RPT_CT:
- rc = lpfc_bsg_rport_ct(job);
- break;
- default:
- break;
- }
- return rc;
- }
- /**
- * lpfc_bsg_timeout - handle timeout of a bsg request from the FC transport
- * @job: fc_bsg_job that has timed out
- *
- * This function just aborts the job's IOCB. The aborted IOCB will return to
- * the waiting function which will handle passing the error back to userspace
- */
- int
- lpfc_bsg_timeout(struct fc_bsg_job *job)
- {
- struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
- struct lpfc_hba *phba = vport->phba;
- struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *)job->dd_data;
- struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
- if (cmdiocb)
- lpfc_sli_issue_abort_iotag(phba, pring, cmdiocb);
- return 0;
- }
|