1f1c3b0fcSJames Smart /******************************************************************* 2f1c3b0fcSJames Smart * This file is part of the Emulex Linux Device Driver for * 3f1c3b0fcSJames Smart * Fibre Channel Host Bus Adapters. * 4*4fede78fSJames Smart * Copyright (C) 2009-2010 Emulex. All rights reserved. * 5f1c3b0fcSJames Smart * EMULEX and SLI are trademarks of Emulex. * 6f1c3b0fcSJames Smart * www.emulex.com * 7f1c3b0fcSJames Smart * * 8f1c3b0fcSJames Smart * This program is free software; you can redistribute it and/or * 9f1c3b0fcSJames Smart * modify it under the terms of version 2 of the GNU General * 10f1c3b0fcSJames Smart * Public License as published by the Free Software Foundation. * 11f1c3b0fcSJames Smart * This program is distributed in the hope that it will be useful. * 12f1c3b0fcSJames Smart * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * 13f1c3b0fcSJames Smart * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * 14f1c3b0fcSJames Smart * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * 15f1c3b0fcSJames Smart * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * 16f1c3b0fcSJames Smart * TO BE LEGALLY INVALID. See the GNU General Public License for * 17f1c3b0fcSJames Smart * more details, a copy of which can be found in the file COPYING * 18f1c3b0fcSJames Smart * included with this package. * 19f1c3b0fcSJames Smart *******************************************************************/ 20f1c3b0fcSJames Smart 21f1c3b0fcSJames Smart #include <linux/interrupt.h> 22f1c3b0fcSJames Smart #include <linux/mempool.h> 23f1c3b0fcSJames Smart #include <linux/pci.h> 24f1c3b0fcSJames Smart 25f1c3b0fcSJames Smart #include <scsi/scsi.h> 26f1c3b0fcSJames Smart #include <scsi/scsi_host.h> 27f1c3b0fcSJames Smart #include <scsi/scsi_transport_fc.h> 28f1c3b0fcSJames Smart #include <scsi/scsi_bsg_fc.h> 296a9c52cfSJames Smart #include <scsi/fc/fc_fs.h> 30f1c3b0fcSJames Smart 31f1c3b0fcSJames Smart #include "lpfc_hw4.h" 32f1c3b0fcSJames Smart #include "lpfc_hw.h" 33f1c3b0fcSJames Smart #include "lpfc_sli.h" 34f1c3b0fcSJames Smart #include "lpfc_sli4.h" 35f1c3b0fcSJames Smart #include "lpfc_nl.h" 36*4fede78fSJames Smart #include "lpfc_bsg.h" 37f1c3b0fcSJames Smart #include "lpfc_disc.h" 38f1c3b0fcSJames Smart #include "lpfc_scsi.h" 39f1c3b0fcSJames Smart #include "lpfc.h" 40f1c3b0fcSJames Smart #include "lpfc_logmsg.h" 41f1c3b0fcSJames Smart #include "lpfc_crtn.h" 42f1c3b0fcSJames Smart #include "lpfc_vport.h" 43f1c3b0fcSJames Smart #include "lpfc_version.h" 44f1c3b0fcSJames Smart 45f1c3b0fcSJames Smart /** 46f1c3b0fcSJames Smart * lpfc_bsg_rport_ct - send a CT command from a bsg request 47f1c3b0fcSJames Smart * @job: fc_bsg_job to handle 48f1c3b0fcSJames Smart */ 49f1c3b0fcSJames Smart static int 50f1c3b0fcSJames Smart lpfc_bsg_rport_ct(struct fc_bsg_job *job) 51f1c3b0fcSJames Smart { 52f1c3b0fcSJames Smart struct Scsi_Host *shost = job->shost; 53f1c3b0fcSJames Smart struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; 54f1c3b0fcSJames Smart struct lpfc_hba *phba = vport->phba; 55f1c3b0fcSJames Smart struct lpfc_rport_data *rdata = job->rport->dd_data; 56f1c3b0fcSJames Smart struct lpfc_nodelist *ndlp = rdata->pnode; 57f1c3b0fcSJames Smart struct ulp_bde64 *bpl = NULL; 58f1c3b0fcSJames Smart uint32_t timeout; 59f1c3b0fcSJames Smart struct lpfc_iocbq *cmdiocbq = NULL; 60f1c3b0fcSJames Smart struct lpfc_iocbq *rspiocbq = NULL; 61f1c3b0fcSJames Smart IOCB_t *cmd; 62f1c3b0fcSJames Smart IOCB_t *rsp; 63f1c3b0fcSJames Smart struct lpfc_dmabuf *bmp = NULL; 64f1c3b0fcSJames Smart int request_nseg; 65f1c3b0fcSJames Smart int reply_nseg; 66f1c3b0fcSJames Smart struct scatterlist *sgel = NULL; 67f1c3b0fcSJames Smart int numbde; 68f1c3b0fcSJames Smart dma_addr_t busaddr; 69f1c3b0fcSJames Smart int rc = 0; 70f1c3b0fcSJames Smart 71f1c3b0fcSJames Smart /* in case no data is transferred */ 72f1c3b0fcSJames Smart job->reply->reply_payload_rcv_len = 0; 73f1c3b0fcSJames Smart 74f1c3b0fcSJames Smart if (!lpfc_nlp_get(ndlp)) { 75f1c3b0fcSJames Smart job->reply->result = -ENODEV; 76f1c3b0fcSJames Smart return 0; 77f1c3b0fcSJames Smart } 78f1c3b0fcSJames Smart 79f1c3b0fcSJames Smart if (ndlp->nlp_flag & NLP_ELS_SND_MASK) { 80f1c3b0fcSJames Smart rc = -ENODEV; 81f1c3b0fcSJames Smart goto free_ndlp_exit; 82f1c3b0fcSJames Smart } 83f1c3b0fcSJames Smart 84f1c3b0fcSJames Smart spin_lock_irq(shost->host_lock); 85f1c3b0fcSJames Smart cmdiocbq = lpfc_sli_get_iocbq(phba); 86f1c3b0fcSJames Smart if (!cmdiocbq) { 87f1c3b0fcSJames Smart rc = -ENOMEM; 88f1c3b0fcSJames Smart spin_unlock_irq(shost->host_lock); 89f1c3b0fcSJames Smart goto free_ndlp_exit; 90f1c3b0fcSJames Smart } 91f1c3b0fcSJames Smart cmd = &cmdiocbq->iocb; 92f1c3b0fcSJames Smart 93f1c3b0fcSJames Smart rspiocbq = lpfc_sli_get_iocbq(phba); 94f1c3b0fcSJames Smart if (!rspiocbq) { 95f1c3b0fcSJames Smart rc = -ENOMEM; 96f1c3b0fcSJames Smart goto free_cmdiocbq; 97f1c3b0fcSJames Smart } 98f1c3b0fcSJames Smart spin_unlock_irq(shost->host_lock); 99f1c3b0fcSJames Smart 100f1c3b0fcSJames Smart rsp = &rspiocbq->iocb; 101f1c3b0fcSJames Smart 102f1c3b0fcSJames Smart bmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); 103f1c3b0fcSJames Smart if (!bmp) { 104f1c3b0fcSJames Smart rc = -ENOMEM; 105f1c3b0fcSJames Smart spin_lock_irq(shost->host_lock); 106f1c3b0fcSJames Smart goto free_rspiocbq; 107f1c3b0fcSJames Smart } 108f1c3b0fcSJames Smart 109f1c3b0fcSJames Smart spin_lock_irq(shost->host_lock); 110f1c3b0fcSJames Smart bmp->virt = lpfc_mbuf_alloc(phba, 0, &bmp->phys); 111f1c3b0fcSJames Smart if (!bmp->virt) { 112f1c3b0fcSJames Smart rc = -ENOMEM; 113f1c3b0fcSJames Smart goto free_bmp; 114f1c3b0fcSJames Smart } 115f1c3b0fcSJames Smart spin_unlock_irq(shost->host_lock); 116f1c3b0fcSJames Smart 117f1c3b0fcSJames Smart INIT_LIST_HEAD(&bmp->list); 118f1c3b0fcSJames Smart bpl = (struct ulp_bde64 *) bmp->virt; 119f1c3b0fcSJames Smart 120f1c3b0fcSJames Smart request_nseg = pci_map_sg(phba->pcidev, job->request_payload.sg_list, 121f1c3b0fcSJames Smart job->request_payload.sg_cnt, DMA_TO_DEVICE); 122f1c3b0fcSJames Smart for_each_sg(job->request_payload.sg_list, sgel, request_nseg, numbde) { 123f1c3b0fcSJames Smart busaddr = sg_dma_address(sgel); 124f1c3b0fcSJames Smart bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64; 125f1c3b0fcSJames Smart bpl->tus.f.bdeSize = sg_dma_len(sgel); 126f1c3b0fcSJames Smart bpl->tus.w = cpu_to_le32(bpl->tus.w); 127f1c3b0fcSJames Smart bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr)); 128f1c3b0fcSJames Smart bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr)); 129f1c3b0fcSJames Smart bpl++; 130f1c3b0fcSJames Smart } 131f1c3b0fcSJames Smart 132f1c3b0fcSJames Smart reply_nseg = pci_map_sg(phba->pcidev, job->reply_payload.sg_list, 133f1c3b0fcSJames Smart job->reply_payload.sg_cnt, DMA_FROM_DEVICE); 134f1c3b0fcSJames Smart for_each_sg(job->reply_payload.sg_list, sgel, reply_nseg, numbde) { 135f1c3b0fcSJames Smart busaddr = sg_dma_address(sgel); 136f1c3b0fcSJames Smart bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I; 137f1c3b0fcSJames Smart bpl->tus.f.bdeSize = sg_dma_len(sgel); 138f1c3b0fcSJames Smart bpl->tus.w = cpu_to_le32(bpl->tus.w); 139f1c3b0fcSJames Smart bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr)); 140f1c3b0fcSJames Smart bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr)); 141f1c3b0fcSJames Smart bpl++; 142f1c3b0fcSJames Smart } 143f1c3b0fcSJames Smart 144f1c3b0fcSJames Smart cmd->un.genreq64.bdl.ulpIoTag32 = 0; 145f1c3b0fcSJames Smart cmd->un.genreq64.bdl.addrHigh = putPaddrHigh(bmp->phys); 146f1c3b0fcSJames Smart cmd->un.genreq64.bdl.addrLow = putPaddrLow(bmp->phys); 147f1c3b0fcSJames Smart cmd->un.genreq64.bdl.bdeFlags = BUFF_TYPE_BLP_64; 148f1c3b0fcSJames Smart cmd->un.genreq64.bdl.bdeSize = 149f1c3b0fcSJames Smart (request_nseg + reply_nseg) * sizeof(struct ulp_bde64); 150f1c3b0fcSJames Smart cmd->ulpCommand = CMD_GEN_REQUEST64_CR; 151f1c3b0fcSJames Smart cmd->un.genreq64.w5.hcsw.Fctl = (SI | LA); 152f1c3b0fcSJames Smart cmd->un.genreq64.w5.hcsw.Dfctl = 0; 1536a9c52cfSJames Smart cmd->un.genreq64.w5.hcsw.Rctl = FC_RCTL_DD_UNSOL_CTL; 1546a9c52cfSJames Smart cmd->un.genreq64.w5.hcsw.Type = FC_TYPE_CT; 155f1c3b0fcSJames Smart cmd->ulpBdeCount = 1; 156f1c3b0fcSJames Smart cmd->ulpLe = 1; 157f1c3b0fcSJames Smart cmd->ulpClass = CLASS3; 158f1c3b0fcSJames Smart cmd->ulpContext = ndlp->nlp_rpi; 159f1c3b0fcSJames Smart cmd->ulpOwner = OWN_CHIP; 160f1c3b0fcSJames Smart cmdiocbq->vport = phba->pport; 161f1c3b0fcSJames Smart cmdiocbq->context1 = NULL; 162f1c3b0fcSJames Smart cmdiocbq->context2 = NULL; 163f1c3b0fcSJames Smart cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC; 164f1c3b0fcSJames Smart 165f1c3b0fcSJames Smart timeout = phba->fc_ratov * 2; 166f1c3b0fcSJames Smart job->dd_data = cmdiocbq; 167f1c3b0fcSJames Smart 168f1c3b0fcSJames Smart rc = lpfc_sli_issue_iocb_wait(phba, LPFC_ELS_RING, cmdiocbq, rspiocbq, 169f1c3b0fcSJames Smart timeout + LPFC_DRVR_TIMEOUT); 170f1c3b0fcSJames Smart 171f1c3b0fcSJames Smart if (rc != IOCB_TIMEDOUT) { 172f1c3b0fcSJames Smart pci_unmap_sg(phba->pcidev, job->request_payload.sg_list, 173f1c3b0fcSJames Smart job->request_payload.sg_cnt, DMA_TO_DEVICE); 174f1c3b0fcSJames Smart pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list, 175f1c3b0fcSJames Smart job->reply_payload.sg_cnt, DMA_FROM_DEVICE); 176f1c3b0fcSJames Smart } 177f1c3b0fcSJames Smart 178f1c3b0fcSJames Smart if (rc == IOCB_TIMEDOUT) { 179f1c3b0fcSJames Smart lpfc_sli_release_iocbq(phba, rspiocbq); 180f1c3b0fcSJames Smart rc = -EACCES; 181f1c3b0fcSJames Smart goto free_ndlp_exit; 182f1c3b0fcSJames Smart } 183f1c3b0fcSJames Smart 184f1c3b0fcSJames Smart if (rc != IOCB_SUCCESS) { 185f1c3b0fcSJames Smart rc = -EACCES; 186f1c3b0fcSJames Smart goto free_outdmp; 187f1c3b0fcSJames Smart } 188f1c3b0fcSJames Smart 189f1c3b0fcSJames Smart if (rsp->ulpStatus) { 190f1c3b0fcSJames Smart if (rsp->ulpStatus == IOSTAT_LOCAL_REJECT) { 191f1c3b0fcSJames Smart switch (rsp->un.ulpWord[4] & 0xff) { 192f1c3b0fcSJames Smart case IOERR_SEQUENCE_TIMEOUT: 193f1c3b0fcSJames Smart rc = -ETIMEDOUT; 194f1c3b0fcSJames Smart break; 195f1c3b0fcSJames Smart case IOERR_INVALID_RPI: 196f1c3b0fcSJames Smart rc = -EFAULT; 197f1c3b0fcSJames Smart break; 198f1c3b0fcSJames Smart default: 199f1c3b0fcSJames Smart rc = -EACCES; 200f1c3b0fcSJames Smart break; 201f1c3b0fcSJames Smart } 202f1c3b0fcSJames Smart goto free_outdmp; 203f1c3b0fcSJames Smart } 204f1c3b0fcSJames Smart } else 205f1c3b0fcSJames Smart job->reply->reply_payload_rcv_len = 206f1c3b0fcSJames Smart rsp->un.genreq64.bdl.bdeSize; 207f1c3b0fcSJames Smart 208f1c3b0fcSJames Smart free_outdmp: 209f1c3b0fcSJames Smart spin_lock_irq(shost->host_lock); 210f1c3b0fcSJames Smart lpfc_mbuf_free(phba, bmp->virt, bmp->phys); 211f1c3b0fcSJames Smart free_bmp: 212f1c3b0fcSJames Smart kfree(bmp); 213f1c3b0fcSJames Smart free_rspiocbq: 214f1c3b0fcSJames Smart lpfc_sli_release_iocbq(phba, rspiocbq); 215f1c3b0fcSJames Smart free_cmdiocbq: 216f1c3b0fcSJames Smart lpfc_sli_release_iocbq(phba, cmdiocbq); 217f1c3b0fcSJames Smart spin_unlock_irq(shost->host_lock); 218f1c3b0fcSJames Smart free_ndlp_exit: 219f1c3b0fcSJames Smart lpfc_nlp_put(ndlp); 220f1c3b0fcSJames Smart 221f1c3b0fcSJames Smart /* make error code available to userspace */ 222f1c3b0fcSJames Smart job->reply->result = rc; 223f1c3b0fcSJames Smart /* complete the job back to userspace */ 224f1c3b0fcSJames Smart job->job_done(job); 225f1c3b0fcSJames Smart 226f1c3b0fcSJames Smart return 0; 227f1c3b0fcSJames Smart } 228f1c3b0fcSJames Smart 229f1c3b0fcSJames Smart /** 230f1c3b0fcSJames Smart * lpfc_bsg_rport_els - send an ELS command from a bsg request 231f1c3b0fcSJames Smart * @job: fc_bsg_job to handle 232f1c3b0fcSJames Smart */ 233f1c3b0fcSJames Smart static int 234f1c3b0fcSJames Smart lpfc_bsg_rport_els(struct fc_bsg_job *job) 235f1c3b0fcSJames Smart { 236f1c3b0fcSJames Smart struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; 237f1c3b0fcSJames Smart struct lpfc_hba *phba = vport->phba; 238f1c3b0fcSJames Smart struct lpfc_rport_data *rdata = job->rport->dd_data; 239f1c3b0fcSJames Smart struct lpfc_nodelist *ndlp = rdata->pnode; 240f1c3b0fcSJames Smart 241f1c3b0fcSJames Smart uint32_t elscmd; 242f1c3b0fcSJames Smart uint32_t cmdsize; 243f1c3b0fcSJames Smart uint32_t rspsize; 244f1c3b0fcSJames Smart struct lpfc_iocbq *rspiocbq; 245f1c3b0fcSJames Smart struct lpfc_iocbq *cmdiocbq; 246f1c3b0fcSJames Smart IOCB_t *rsp; 247f1c3b0fcSJames Smart uint16_t rpi = 0; 248f1c3b0fcSJames Smart struct lpfc_dmabuf *pcmd; 249f1c3b0fcSJames Smart struct lpfc_dmabuf *prsp; 250f1c3b0fcSJames Smart struct lpfc_dmabuf *pbuflist = NULL; 251f1c3b0fcSJames Smart struct ulp_bde64 *bpl; 252f1c3b0fcSJames Smart int iocb_status; 253f1c3b0fcSJames Smart int request_nseg; 254f1c3b0fcSJames Smart int reply_nseg; 255f1c3b0fcSJames Smart struct scatterlist *sgel = NULL; 256f1c3b0fcSJames Smart int numbde; 257f1c3b0fcSJames Smart dma_addr_t busaddr; 258f1c3b0fcSJames Smart int rc = 0; 259f1c3b0fcSJames Smart 260f1c3b0fcSJames Smart /* in case no data is transferred */ 261f1c3b0fcSJames Smart job->reply->reply_payload_rcv_len = 0; 262f1c3b0fcSJames Smart 263f1c3b0fcSJames Smart if (!lpfc_nlp_get(ndlp)) { 264f1c3b0fcSJames Smart rc = -ENODEV; 265f1c3b0fcSJames Smart goto out; 266f1c3b0fcSJames Smart } 267f1c3b0fcSJames Smart 268f1c3b0fcSJames Smart elscmd = job->request->rqst_data.r_els.els_code; 269f1c3b0fcSJames Smart cmdsize = job->request_payload.payload_len; 270f1c3b0fcSJames Smart rspsize = job->reply_payload.payload_len; 271f1c3b0fcSJames Smart rspiocbq = lpfc_sli_get_iocbq(phba); 272f1c3b0fcSJames Smart if (!rspiocbq) { 273f1c3b0fcSJames Smart lpfc_nlp_put(ndlp); 274f1c3b0fcSJames Smart rc = -ENOMEM; 275f1c3b0fcSJames Smart goto out; 276f1c3b0fcSJames Smart } 277f1c3b0fcSJames Smart 278f1c3b0fcSJames Smart rsp = &rspiocbq->iocb; 279f1c3b0fcSJames Smart rpi = ndlp->nlp_rpi; 280f1c3b0fcSJames Smart 281f1c3b0fcSJames Smart cmdiocbq = lpfc_prep_els_iocb(phba->pport, 1, cmdsize, 0, ndlp, 282f1c3b0fcSJames Smart ndlp->nlp_DID, elscmd); 283f1c3b0fcSJames Smart 284f1c3b0fcSJames Smart if (!cmdiocbq) { 285f1c3b0fcSJames Smart lpfc_sli_release_iocbq(phba, rspiocbq); 286f1c3b0fcSJames Smart return -EIO; 287f1c3b0fcSJames Smart } 288f1c3b0fcSJames Smart 289f1c3b0fcSJames Smart job->dd_data = cmdiocbq; 290f1c3b0fcSJames Smart pcmd = (struct lpfc_dmabuf *) cmdiocbq->context2; 291f1c3b0fcSJames Smart prsp = (struct lpfc_dmabuf *) pcmd->list.next; 292f1c3b0fcSJames Smart 293f1c3b0fcSJames Smart lpfc_mbuf_free(phba, pcmd->virt, pcmd->phys); 294f1c3b0fcSJames Smart kfree(pcmd); 295f1c3b0fcSJames Smart lpfc_mbuf_free(phba, prsp->virt, prsp->phys); 296f1c3b0fcSJames Smart kfree(prsp); 297f1c3b0fcSJames Smart cmdiocbq->context2 = NULL; 298f1c3b0fcSJames Smart 299f1c3b0fcSJames Smart pbuflist = (struct lpfc_dmabuf *) cmdiocbq->context3; 300f1c3b0fcSJames Smart bpl = (struct ulp_bde64 *) pbuflist->virt; 301f1c3b0fcSJames Smart 302f1c3b0fcSJames Smart request_nseg = pci_map_sg(phba->pcidev, job->request_payload.sg_list, 303f1c3b0fcSJames Smart job->request_payload.sg_cnt, DMA_TO_DEVICE); 304f1c3b0fcSJames Smart 305f1c3b0fcSJames Smart for_each_sg(job->request_payload.sg_list, sgel, request_nseg, numbde) { 306f1c3b0fcSJames Smart busaddr = sg_dma_address(sgel); 307f1c3b0fcSJames Smart bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64; 308f1c3b0fcSJames Smart bpl->tus.f.bdeSize = sg_dma_len(sgel); 309f1c3b0fcSJames Smart bpl->tus.w = cpu_to_le32(bpl->tus.w); 310f1c3b0fcSJames Smart bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr)); 311f1c3b0fcSJames Smart bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr)); 312f1c3b0fcSJames Smart bpl++; 313f1c3b0fcSJames Smart } 314f1c3b0fcSJames Smart 315f1c3b0fcSJames Smart reply_nseg = pci_map_sg(phba->pcidev, job->reply_payload.sg_list, 316f1c3b0fcSJames Smart job->reply_payload.sg_cnt, DMA_FROM_DEVICE); 317f1c3b0fcSJames Smart for_each_sg(job->reply_payload.sg_list, sgel, reply_nseg, numbde) { 318f1c3b0fcSJames Smart busaddr = sg_dma_address(sgel); 319f1c3b0fcSJames Smart bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I; 320f1c3b0fcSJames Smart bpl->tus.f.bdeSize = sg_dma_len(sgel); 321f1c3b0fcSJames Smart bpl->tus.w = cpu_to_le32(bpl->tus.w); 322f1c3b0fcSJames Smart bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr)); 323f1c3b0fcSJames Smart bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr)); 324f1c3b0fcSJames Smart bpl++; 325f1c3b0fcSJames Smart } 326f1c3b0fcSJames Smart 327f1c3b0fcSJames Smart cmdiocbq->iocb.un.elsreq64.bdl.bdeSize = 328f1c3b0fcSJames Smart (request_nseg + reply_nseg) * sizeof(struct ulp_bde64); 329f1c3b0fcSJames Smart cmdiocbq->iocb.ulpContext = rpi; 330f1c3b0fcSJames Smart cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC; 331f1c3b0fcSJames Smart cmdiocbq->context1 = NULL; 332f1c3b0fcSJames Smart cmdiocbq->context2 = NULL; 333f1c3b0fcSJames Smart 334f1c3b0fcSJames Smart iocb_status = lpfc_sli_issue_iocb_wait(phba, LPFC_ELS_RING, cmdiocbq, 335f1c3b0fcSJames Smart rspiocbq, (phba->fc_ratov * 2) 336f1c3b0fcSJames Smart + LPFC_DRVR_TIMEOUT); 337f1c3b0fcSJames Smart 338f1c3b0fcSJames Smart /* release the new ndlp once the iocb completes */ 339f1c3b0fcSJames Smart lpfc_nlp_put(ndlp); 340f1c3b0fcSJames Smart if (iocb_status != IOCB_TIMEDOUT) { 341f1c3b0fcSJames Smart pci_unmap_sg(phba->pcidev, job->request_payload.sg_list, 342f1c3b0fcSJames Smart job->request_payload.sg_cnt, DMA_TO_DEVICE); 343f1c3b0fcSJames Smart pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list, 344f1c3b0fcSJames Smart job->reply_payload.sg_cnt, DMA_FROM_DEVICE); 345f1c3b0fcSJames Smart } 346f1c3b0fcSJames Smart 347f1c3b0fcSJames Smart if (iocb_status == IOCB_SUCCESS) { 348f1c3b0fcSJames Smart if (rsp->ulpStatus == IOSTAT_SUCCESS) { 349f1c3b0fcSJames Smart job->reply->reply_payload_rcv_len = 350f1c3b0fcSJames Smart rsp->un.elsreq64.bdl.bdeSize; 351f1c3b0fcSJames Smart rc = 0; 352f1c3b0fcSJames Smart } else if (rsp->ulpStatus == IOSTAT_LS_RJT) { 353f1c3b0fcSJames Smart struct fc_bsg_ctels_reply *els_reply; 354f1c3b0fcSJames Smart /* LS_RJT data returned in word 4 */ 355f1c3b0fcSJames Smart uint8_t *rjt_data = (uint8_t *)&rsp->un.ulpWord[4]; 356f1c3b0fcSJames Smart 357f1c3b0fcSJames Smart els_reply = &job->reply->reply_data.ctels_reply; 358f1c3b0fcSJames Smart job->reply->result = 0; 359f1c3b0fcSJames Smart els_reply->status = FC_CTELS_STATUS_REJECT; 360f1c3b0fcSJames Smart els_reply->rjt_data.action = rjt_data[0]; 361f1c3b0fcSJames Smart els_reply->rjt_data.reason_code = rjt_data[1]; 362f1c3b0fcSJames Smart els_reply->rjt_data.reason_explanation = rjt_data[2]; 363f1c3b0fcSJames Smart els_reply->rjt_data.vendor_unique = rjt_data[3]; 364f1c3b0fcSJames Smart } else 365f1c3b0fcSJames Smart rc = -EIO; 366f1c3b0fcSJames Smart } else 367f1c3b0fcSJames Smart rc = -EIO; 368f1c3b0fcSJames Smart 369f1c3b0fcSJames Smart if (iocb_status != IOCB_TIMEDOUT) 370f1c3b0fcSJames Smart lpfc_els_free_iocb(phba, cmdiocbq); 371f1c3b0fcSJames Smart 372f1c3b0fcSJames Smart lpfc_sli_release_iocbq(phba, rspiocbq); 373f1c3b0fcSJames Smart 374f1c3b0fcSJames Smart out: 375f1c3b0fcSJames Smart /* make error code available to userspace */ 376f1c3b0fcSJames Smart job->reply->result = rc; 377f1c3b0fcSJames Smart /* complete the job back to userspace */ 378f1c3b0fcSJames Smart job->job_done(job); 379f1c3b0fcSJames Smart 380f1c3b0fcSJames Smart return 0; 381f1c3b0fcSJames Smart } 382f1c3b0fcSJames Smart 383f1c3b0fcSJames Smart struct lpfc_ct_event { 384f1c3b0fcSJames Smart struct list_head node; 385f1c3b0fcSJames Smart int ref; 386f1c3b0fcSJames Smart wait_queue_head_t wq; 387f1c3b0fcSJames Smart 388f1c3b0fcSJames Smart /* Event type and waiter identifiers */ 389f1c3b0fcSJames Smart uint32_t type_mask; 390f1c3b0fcSJames Smart uint32_t req_id; 391f1c3b0fcSJames Smart uint32_t reg_id; 392f1c3b0fcSJames Smart 393f1c3b0fcSJames Smart /* next two flags are here for the auto-delete logic */ 394f1c3b0fcSJames Smart unsigned long wait_time_stamp; 395f1c3b0fcSJames Smart int waiting; 396f1c3b0fcSJames Smart 397f1c3b0fcSJames Smart /* seen and not seen events */ 398f1c3b0fcSJames Smart struct list_head events_to_get; 399f1c3b0fcSJames Smart struct list_head events_to_see; 400f1c3b0fcSJames Smart }; 401f1c3b0fcSJames Smart 402f1c3b0fcSJames Smart struct event_data { 403f1c3b0fcSJames Smart struct list_head node; 404f1c3b0fcSJames Smart uint32_t type; 405f1c3b0fcSJames Smart uint32_t immed_dat; 406f1c3b0fcSJames Smart void *data; 407f1c3b0fcSJames Smart uint32_t len; 408f1c3b0fcSJames Smart }; 409f1c3b0fcSJames Smart 410f1c3b0fcSJames Smart static struct lpfc_ct_event * 411f1c3b0fcSJames Smart lpfc_ct_event_new(int ev_reg_id, uint32_t ev_req_id) 412f1c3b0fcSJames Smart { 413f1c3b0fcSJames Smart struct lpfc_ct_event *evt = kzalloc(sizeof(*evt), GFP_KERNEL); 414f1c3b0fcSJames Smart if (!evt) 415f1c3b0fcSJames Smart return NULL; 416f1c3b0fcSJames Smart 417f1c3b0fcSJames Smart INIT_LIST_HEAD(&evt->events_to_get); 418f1c3b0fcSJames Smart INIT_LIST_HEAD(&evt->events_to_see); 419f1c3b0fcSJames Smart evt->req_id = ev_req_id; 420f1c3b0fcSJames Smart evt->reg_id = ev_reg_id; 421f1c3b0fcSJames Smart evt->wait_time_stamp = jiffies; 422f1c3b0fcSJames Smart init_waitqueue_head(&evt->wq); 423f1c3b0fcSJames Smart 424f1c3b0fcSJames Smart return evt; 425f1c3b0fcSJames Smart } 426f1c3b0fcSJames Smart 427f1c3b0fcSJames Smart static void 428f1c3b0fcSJames Smart lpfc_ct_event_free(struct lpfc_ct_event *evt) 429f1c3b0fcSJames Smart { 430f1c3b0fcSJames Smart struct event_data *ed; 431f1c3b0fcSJames Smart 432f1c3b0fcSJames Smart list_del(&evt->node); 433f1c3b0fcSJames Smart 434f1c3b0fcSJames Smart while (!list_empty(&evt->events_to_get)) { 435f1c3b0fcSJames Smart ed = list_entry(evt->events_to_get.next, typeof(*ed), node); 436f1c3b0fcSJames Smart list_del(&ed->node); 437f1c3b0fcSJames Smart kfree(ed->data); 438f1c3b0fcSJames Smart kfree(ed); 439f1c3b0fcSJames Smart } 440f1c3b0fcSJames Smart 441f1c3b0fcSJames Smart while (!list_empty(&evt->events_to_see)) { 442f1c3b0fcSJames Smart ed = list_entry(evt->events_to_see.next, typeof(*ed), node); 443f1c3b0fcSJames Smart list_del(&ed->node); 444f1c3b0fcSJames Smart kfree(ed->data); 445f1c3b0fcSJames Smart kfree(ed); 446f1c3b0fcSJames Smart } 447f1c3b0fcSJames Smart 448f1c3b0fcSJames Smart kfree(evt); 449f1c3b0fcSJames Smart } 450f1c3b0fcSJames Smart 451f1c3b0fcSJames Smart static inline void 452f1c3b0fcSJames Smart lpfc_ct_event_ref(struct lpfc_ct_event *evt) 453f1c3b0fcSJames Smart { 454f1c3b0fcSJames Smart evt->ref++; 455f1c3b0fcSJames Smart } 456f1c3b0fcSJames Smart 457f1c3b0fcSJames Smart static inline void 458f1c3b0fcSJames Smart lpfc_ct_event_unref(struct lpfc_ct_event *evt) 459f1c3b0fcSJames Smart { 460f1c3b0fcSJames Smart if (--evt->ref < 0) 461f1c3b0fcSJames Smart lpfc_ct_event_free(evt); 462f1c3b0fcSJames Smart } 463f1c3b0fcSJames Smart 464f1c3b0fcSJames Smart #define SLI_CT_ELX_LOOPBACK 0x10 465f1c3b0fcSJames Smart 466f1c3b0fcSJames Smart enum ELX_LOOPBACK_CMD { 467f1c3b0fcSJames Smart ELX_LOOPBACK_XRI_SETUP, 468f1c3b0fcSJames Smart ELX_LOOPBACK_DATA, 469f1c3b0fcSJames Smart }; 470f1c3b0fcSJames Smart 471f1c3b0fcSJames Smart /** 472f1c3b0fcSJames Smart * lpfc_bsg_ct_unsol_event - process an unsolicited CT command 473f1c3b0fcSJames Smart * @phba: 474f1c3b0fcSJames Smart * @pring: 475f1c3b0fcSJames Smart * @piocbq: 476f1c3b0fcSJames Smart * 477f1c3b0fcSJames Smart * This function is called when an unsolicited CT command is received. It 478f1c3b0fcSJames Smart * forwards the event to any processes registerd to receive CT events. 479f1c3b0fcSJames Smart */ 480*4fede78fSJames Smart int 481f1c3b0fcSJames Smart lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, 482f1c3b0fcSJames Smart struct lpfc_iocbq *piocbq) 483f1c3b0fcSJames Smart { 484f1c3b0fcSJames Smart uint32_t evt_req_id = 0; 485f1c3b0fcSJames Smart uint32_t cmd; 486f1c3b0fcSJames Smart uint32_t len; 487f1c3b0fcSJames Smart struct lpfc_dmabuf *dmabuf = NULL; 488f1c3b0fcSJames Smart struct lpfc_ct_event *evt; 489f1c3b0fcSJames Smart struct event_data *evt_dat = NULL; 490f1c3b0fcSJames Smart struct lpfc_iocbq *iocbq; 491f1c3b0fcSJames Smart size_t offset = 0; 492f1c3b0fcSJames Smart struct list_head head; 493f1c3b0fcSJames Smart struct ulp_bde64 *bde; 494f1c3b0fcSJames Smart dma_addr_t dma_addr; 495f1c3b0fcSJames Smart int i; 496f1c3b0fcSJames Smart struct lpfc_dmabuf *bdeBuf1 = piocbq->context2; 497f1c3b0fcSJames Smart struct lpfc_dmabuf *bdeBuf2 = piocbq->context3; 498f1c3b0fcSJames Smart struct lpfc_hbq_entry *hbqe; 499f1c3b0fcSJames Smart struct lpfc_sli_ct_request *ct_req; 500*4fede78fSJames Smart unsigned long flags; 501f1c3b0fcSJames Smart 502f1c3b0fcSJames Smart INIT_LIST_HEAD(&head); 503f1c3b0fcSJames Smart list_add_tail(&head, &piocbq->list); 504f1c3b0fcSJames Smart 505f1c3b0fcSJames Smart if (piocbq->iocb.ulpBdeCount == 0 || 506f1c3b0fcSJames Smart piocbq->iocb.un.cont64[0].tus.f.bdeSize == 0) 507f1c3b0fcSJames Smart goto error_ct_unsol_exit; 508f1c3b0fcSJames Smart 509f1c3b0fcSJames Smart if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) 510f1c3b0fcSJames Smart dmabuf = bdeBuf1; 511f1c3b0fcSJames Smart else { 512f1c3b0fcSJames Smart dma_addr = getPaddr(piocbq->iocb.un.cont64[0].addrHigh, 513f1c3b0fcSJames Smart piocbq->iocb.un.cont64[0].addrLow); 514f1c3b0fcSJames Smart dmabuf = lpfc_sli_ringpostbuf_get(phba, pring, dma_addr); 515f1c3b0fcSJames Smart } 516f1c3b0fcSJames Smart 517f1c3b0fcSJames Smart ct_req = (struct lpfc_sli_ct_request *)dmabuf->virt; 518f1c3b0fcSJames Smart evt_req_id = ct_req->FsType; 519f1c3b0fcSJames Smart cmd = ct_req->CommandResponse.bits.CmdRsp; 520f1c3b0fcSJames Smart len = ct_req->CommandResponse.bits.Size; 521f1c3b0fcSJames Smart if (!(phba->sli3_options & LPFC_SLI3_HBQ_ENABLED)) 522f1c3b0fcSJames Smart lpfc_sli_ringpostbuf_put(phba, pring, dmabuf); 523f1c3b0fcSJames Smart 524*4fede78fSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 525f1c3b0fcSJames Smart list_for_each_entry(evt, &phba->ct_ev_waiters, node) { 526f1c3b0fcSJames Smart if (evt->req_id != evt_req_id) 527f1c3b0fcSJames Smart continue; 528f1c3b0fcSJames Smart 529f1c3b0fcSJames Smart lpfc_ct_event_ref(evt); 530f1c3b0fcSJames Smart 531f1c3b0fcSJames Smart evt_dat = kzalloc(sizeof(*evt_dat), GFP_KERNEL); 532f1c3b0fcSJames Smart if (!evt_dat) { 533f1c3b0fcSJames Smart lpfc_ct_event_unref(evt); 534f1c3b0fcSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 535f1c3b0fcSJames Smart "2614 Memory allocation failed for " 536f1c3b0fcSJames Smart "CT event\n"); 537f1c3b0fcSJames Smart break; 538f1c3b0fcSJames Smart } 539f1c3b0fcSJames Smart 540*4fede78fSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 541f1c3b0fcSJames Smart 542f1c3b0fcSJames Smart if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) { 543f1c3b0fcSJames Smart /* take accumulated byte count from the last iocbq */ 544f1c3b0fcSJames Smart iocbq = list_entry(head.prev, typeof(*iocbq), list); 545f1c3b0fcSJames Smart evt_dat->len = iocbq->iocb.unsli3.rcvsli3.acc_len; 546f1c3b0fcSJames Smart } else { 547f1c3b0fcSJames Smart list_for_each_entry(iocbq, &head, list) { 548f1c3b0fcSJames Smart for (i = 0; i < iocbq->iocb.ulpBdeCount; i++) 549f1c3b0fcSJames Smart evt_dat->len += 550f1c3b0fcSJames Smart iocbq->iocb.un.cont64[i].tus.f.bdeSize; 551f1c3b0fcSJames Smart } 552f1c3b0fcSJames Smart } 553f1c3b0fcSJames Smart 554f1c3b0fcSJames Smart evt_dat->data = kzalloc(evt_dat->len, GFP_KERNEL); 555f1c3b0fcSJames Smart if (!evt_dat->data) { 556f1c3b0fcSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 557f1c3b0fcSJames Smart "2615 Memory allocation failed for " 558f1c3b0fcSJames Smart "CT event data, size %d\n", 559f1c3b0fcSJames Smart evt_dat->len); 560f1c3b0fcSJames Smart kfree(evt_dat); 561*4fede78fSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 562f1c3b0fcSJames Smart lpfc_ct_event_unref(evt); 563*4fede78fSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 564f1c3b0fcSJames Smart goto error_ct_unsol_exit; 565f1c3b0fcSJames Smart } 566f1c3b0fcSJames Smart 567f1c3b0fcSJames Smart list_for_each_entry(iocbq, &head, list) { 568f1c3b0fcSJames Smart if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) { 569f1c3b0fcSJames Smart bdeBuf1 = iocbq->context2; 570f1c3b0fcSJames Smart bdeBuf2 = iocbq->context3; 571f1c3b0fcSJames Smart } 572f1c3b0fcSJames Smart for (i = 0; i < iocbq->iocb.ulpBdeCount; i++) { 573f1c3b0fcSJames Smart int size = 0; 574f1c3b0fcSJames Smart if (phba->sli3_options & 575f1c3b0fcSJames Smart LPFC_SLI3_HBQ_ENABLED) { 576f1c3b0fcSJames Smart if (i == 0) { 577f1c3b0fcSJames Smart hbqe = (struct lpfc_hbq_entry *) 578f1c3b0fcSJames Smart &iocbq->iocb.un.ulpWord[0]; 579f1c3b0fcSJames Smart size = hbqe->bde.tus.f.bdeSize; 580f1c3b0fcSJames Smart dmabuf = bdeBuf1; 581f1c3b0fcSJames Smart } else if (i == 1) { 582f1c3b0fcSJames Smart hbqe = (struct lpfc_hbq_entry *) 583f1c3b0fcSJames Smart &iocbq->iocb.unsli3. 584f1c3b0fcSJames Smart sli3Words[4]; 585f1c3b0fcSJames Smart size = hbqe->bde.tus.f.bdeSize; 586f1c3b0fcSJames Smart dmabuf = bdeBuf2; 587f1c3b0fcSJames Smart } 588f1c3b0fcSJames Smart if ((offset + size) > evt_dat->len) 589f1c3b0fcSJames Smart size = evt_dat->len - offset; 590f1c3b0fcSJames Smart } else { 591f1c3b0fcSJames Smart size = iocbq->iocb.un.cont64[i]. 592f1c3b0fcSJames Smart tus.f.bdeSize; 593f1c3b0fcSJames Smart bde = &iocbq->iocb.un.cont64[i]; 594f1c3b0fcSJames Smart dma_addr = getPaddr(bde->addrHigh, 595f1c3b0fcSJames Smart bde->addrLow); 596f1c3b0fcSJames Smart dmabuf = lpfc_sli_ringpostbuf_get(phba, 597f1c3b0fcSJames Smart pring, dma_addr); 598f1c3b0fcSJames Smart } 599f1c3b0fcSJames Smart if (!dmabuf) { 600f1c3b0fcSJames Smart lpfc_printf_log(phba, KERN_ERR, 601f1c3b0fcSJames Smart LOG_LIBDFC, "2616 No dmabuf " 602f1c3b0fcSJames Smart "found for iocbq 0x%p\n", 603f1c3b0fcSJames Smart iocbq); 604f1c3b0fcSJames Smart kfree(evt_dat->data); 605f1c3b0fcSJames Smart kfree(evt_dat); 606*4fede78fSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, 607*4fede78fSJames Smart flags); 608f1c3b0fcSJames Smart lpfc_ct_event_unref(evt); 609*4fede78fSJames Smart spin_unlock_irqrestore( 610*4fede78fSJames Smart &phba->ct_ev_lock, flags); 611f1c3b0fcSJames Smart goto error_ct_unsol_exit; 612f1c3b0fcSJames Smart } 613f1c3b0fcSJames Smart memcpy((char *)(evt_dat->data) + offset, 614f1c3b0fcSJames Smart dmabuf->virt, size); 615f1c3b0fcSJames Smart offset += size; 616f1c3b0fcSJames Smart if (evt_req_id != SLI_CT_ELX_LOOPBACK && 617f1c3b0fcSJames Smart !(phba->sli3_options & 618f1c3b0fcSJames Smart LPFC_SLI3_HBQ_ENABLED)) { 619f1c3b0fcSJames Smart lpfc_sli_ringpostbuf_put(phba, pring, 620f1c3b0fcSJames Smart dmabuf); 621f1c3b0fcSJames Smart } else { 622f1c3b0fcSJames Smart switch (cmd) { 623f1c3b0fcSJames Smart case ELX_LOOPBACK_XRI_SETUP: 624f1c3b0fcSJames Smart if (!(phba->sli3_options & 625f1c3b0fcSJames Smart LPFC_SLI3_HBQ_ENABLED)) 626f1c3b0fcSJames Smart lpfc_post_buffer(phba, 627f1c3b0fcSJames Smart pring, 628f1c3b0fcSJames Smart 1); 629f1c3b0fcSJames Smart else 630f1c3b0fcSJames Smart lpfc_in_buf_free(phba, 631f1c3b0fcSJames Smart dmabuf); 632f1c3b0fcSJames Smart break; 633f1c3b0fcSJames Smart default: 634f1c3b0fcSJames Smart if (!(phba->sli3_options & 635f1c3b0fcSJames Smart LPFC_SLI3_HBQ_ENABLED)) 636f1c3b0fcSJames Smart lpfc_post_buffer(phba, 637f1c3b0fcSJames Smart pring, 638f1c3b0fcSJames Smart 1); 639f1c3b0fcSJames Smart break; 640f1c3b0fcSJames Smart } 641f1c3b0fcSJames Smart } 642f1c3b0fcSJames Smart } 643f1c3b0fcSJames Smart } 644f1c3b0fcSJames Smart 645*4fede78fSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 646f1c3b0fcSJames Smart if (phba->sli_rev == LPFC_SLI_REV4) { 647f1c3b0fcSJames Smart evt_dat->immed_dat = phba->ctx_idx; 648f1c3b0fcSJames Smart phba->ctx_idx = (phba->ctx_idx + 1) % 64; 649f1c3b0fcSJames Smart phba->ct_ctx[evt_dat->immed_dat].oxid = 650f1c3b0fcSJames Smart piocbq->iocb.ulpContext; 651f1c3b0fcSJames Smart phba->ct_ctx[evt_dat->immed_dat].SID = 652f1c3b0fcSJames Smart piocbq->iocb.un.rcvels.remoteID; 653f1c3b0fcSJames Smart } else 654f1c3b0fcSJames Smart evt_dat->immed_dat = piocbq->iocb.ulpContext; 655f1c3b0fcSJames Smart 656f1c3b0fcSJames Smart evt_dat->type = FC_REG_CT_EVENT; 657f1c3b0fcSJames Smart list_add(&evt_dat->node, &evt->events_to_see); 658f1c3b0fcSJames Smart wake_up_interruptible(&evt->wq); 659f1c3b0fcSJames Smart lpfc_ct_event_unref(evt); 660f1c3b0fcSJames Smart if (evt_req_id == SLI_CT_ELX_LOOPBACK) 661f1c3b0fcSJames Smart break; 662f1c3b0fcSJames Smart } 663*4fede78fSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 664f1c3b0fcSJames Smart 665f1c3b0fcSJames Smart error_ct_unsol_exit: 666f1c3b0fcSJames Smart if (!list_empty(&head)) 667f1c3b0fcSJames Smart list_del(&head); 668f1c3b0fcSJames Smart 669*4fede78fSJames Smart return 1; 670f1c3b0fcSJames Smart } 671f1c3b0fcSJames Smart 672f1c3b0fcSJames Smart /** 673f1c3b0fcSJames Smart * lpfc_bsg_set_event - process a SET_EVENT bsg vendor command 674f1c3b0fcSJames Smart * @job: SET_EVENT fc_bsg_job 675f1c3b0fcSJames Smart */ 676f1c3b0fcSJames Smart static int 677f1c3b0fcSJames Smart lpfc_bsg_set_event(struct fc_bsg_job *job) 678f1c3b0fcSJames Smart { 679f1c3b0fcSJames Smart struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; 680f1c3b0fcSJames Smart struct lpfc_hba *phba = vport->phba; 681f1c3b0fcSJames Smart struct set_ct_event *event_req; 682f1c3b0fcSJames Smart struct lpfc_ct_event *evt; 683*4fede78fSJames Smart unsigned long flags; 684f1c3b0fcSJames Smart int rc = 0; 685f1c3b0fcSJames Smart 686f1c3b0fcSJames Smart if (job->request_len < 687f1c3b0fcSJames Smart sizeof(struct fc_bsg_request) + sizeof(struct set_ct_event)) { 688f1c3b0fcSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 689f1c3b0fcSJames Smart "2612 Received SET_CT_EVENT below minimum " 690f1c3b0fcSJames Smart "size\n"); 691f1c3b0fcSJames Smart return -EINVAL; 692f1c3b0fcSJames Smart } 693f1c3b0fcSJames Smart 694f1c3b0fcSJames Smart event_req = (struct set_ct_event *) 695f1c3b0fcSJames Smart job->request->rqst_data.h_vendor.vendor_cmd; 696f1c3b0fcSJames Smart 697*4fede78fSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 698f1c3b0fcSJames Smart list_for_each_entry(evt, &phba->ct_ev_waiters, node) { 699f1c3b0fcSJames Smart if (evt->reg_id == event_req->ev_reg_id) { 700f1c3b0fcSJames Smart lpfc_ct_event_ref(evt); 701f1c3b0fcSJames Smart evt->wait_time_stamp = jiffies; 702f1c3b0fcSJames Smart break; 703f1c3b0fcSJames Smart } 704f1c3b0fcSJames Smart } 705*4fede78fSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 706f1c3b0fcSJames Smart 707f1c3b0fcSJames Smart if (&evt->node == &phba->ct_ev_waiters) { 708f1c3b0fcSJames Smart /* no event waiting struct yet - first call */ 709f1c3b0fcSJames Smart evt = lpfc_ct_event_new(event_req->ev_reg_id, 710f1c3b0fcSJames Smart event_req->ev_req_id); 711f1c3b0fcSJames Smart if (!evt) { 712f1c3b0fcSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 713f1c3b0fcSJames Smart "2617 Failed allocation of event " 714f1c3b0fcSJames Smart "waiter\n"); 715f1c3b0fcSJames Smart return -ENOMEM; 716f1c3b0fcSJames Smart } 717f1c3b0fcSJames Smart 718*4fede78fSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 719f1c3b0fcSJames Smart list_add(&evt->node, &phba->ct_ev_waiters); 720f1c3b0fcSJames Smart lpfc_ct_event_ref(evt); 721*4fede78fSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 722f1c3b0fcSJames Smart } 723f1c3b0fcSJames Smart 724f1c3b0fcSJames Smart evt->waiting = 1; 725f1c3b0fcSJames Smart if (wait_event_interruptible(evt->wq, 726f1c3b0fcSJames Smart !list_empty(&evt->events_to_see))) { 727*4fede78fSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 728f1c3b0fcSJames Smart lpfc_ct_event_unref(evt); /* release ref */ 729f1c3b0fcSJames Smart lpfc_ct_event_unref(evt); /* delete */ 730*4fede78fSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 731f1c3b0fcSJames Smart rc = -EINTR; 732f1c3b0fcSJames Smart goto set_event_out; 733f1c3b0fcSJames Smart } 734f1c3b0fcSJames Smart 735f1c3b0fcSJames Smart evt->wait_time_stamp = jiffies; 736f1c3b0fcSJames Smart evt->waiting = 0; 737f1c3b0fcSJames Smart 738*4fede78fSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 739f1c3b0fcSJames Smart list_move(evt->events_to_see.prev, &evt->events_to_get); 740f1c3b0fcSJames Smart lpfc_ct_event_unref(evt); /* release ref */ 741*4fede78fSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 742f1c3b0fcSJames Smart 743f1c3b0fcSJames Smart set_event_out: 744f1c3b0fcSJames Smart /* set_event carries no reply payload */ 745f1c3b0fcSJames Smart job->reply->reply_payload_rcv_len = 0; 746f1c3b0fcSJames Smart /* make error code available to userspace */ 747f1c3b0fcSJames Smart job->reply->result = rc; 748f1c3b0fcSJames Smart /* complete the job back to userspace */ 749f1c3b0fcSJames Smart job->job_done(job); 750f1c3b0fcSJames Smart 751f1c3b0fcSJames Smart return 0; 752f1c3b0fcSJames Smart } 753f1c3b0fcSJames Smart 754f1c3b0fcSJames Smart /** 755f1c3b0fcSJames Smart * lpfc_bsg_get_event - process a GET_EVENT bsg vendor command 756f1c3b0fcSJames Smart * @job: GET_EVENT fc_bsg_job 757f1c3b0fcSJames Smart */ 758f1c3b0fcSJames Smart static int 759f1c3b0fcSJames Smart lpfc_bsg_get_event(struct fc_bsg_job *job) 760f1c3b0fcSJames Smart { 761f1c3b0fcSJames Smart struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; 762f1c3b0fcSJames Smart struct lpfc_hba *phba = vport->phba; 763f1c3b0fcSJames Smart struct get_ct_event *event_req; 764f1c3b0fcSJames Smart struct get_ct_event_reply *event_reply; 765f1c3b0fcSJames Smart struct lpfc_ct_event *evt; 766f1c3b0fcSJames Smart struct event_data *evt_dat = NULL; 767*4fede78fSJames Smart unsigned long flags; 768f1c3b0fcSJames Smart int rc = 0; 769f1c3b0fcSJames Smart 770f1c3b0fcSJames Smart if (job->request_len < 771f1c3b0fcSJames Smart sizeof(struct fc_bsg_request) + sizeof(struct get_ct_event)) { 772f1c3b0fcSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 773f1c3b0fcSJames Smart "2613 Received GET_CT_EVENT request below " 774f1c3b0fcSJames Smart "minimum size\n"); 775f1c3b0fcSJames Smart return -EINVAL; 776f1c3b0fcSJames Smart } 777f1c3b0fcSJames Smart 778f1c3b0fcSJames Smart event_req = (struct get_ct_event *) 779f1c3b0fcSJames Smart job->request->rqst_data.h_vendor.vendor_cmd; 780f1c3b0fcSJames Smart 781f1c3b0fcSJames Smart event_reply = (struct get_ct_event_reply *) 782f1c3b0fcSJames Smart job->reply->reply_data.vendor_reply.vendor_rsp; 783f1c3b0fcSJames Smart 784*4fede78fSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 785f1c3b0fcSJames Smart list_for_each_entry(evt, &phba->ct_ev_waiters, node) { 786f1c3b0fcSJames Smart if (evt->reg_id == event_req->ev_reg_id) { 787f1c3b0fcSJames Smart if (list_empty(&evt->events_to_get)) 788f1c3b0fcSJames Smart break; 789f1c3b0fcSJames Smart lpfc_ct_event_ref(evt); 790f1c3b0fcSJames Smart evt->wait_time_stamp = jiffies; 791f1c3b0fcSJames Smart evt_dat = list_entry(evt->events_to_get.prev, 792f1c3b0fcSJames Smart struct event_data, node); 793f1c3b0fcSJames Smart list_del(&evt_dat->node); 794f1c3b0fcSJames Smart break; 795f1c3b0fcSJames Smart } 796f1c3b0fcSJames Smart } 797*4fede78fSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 798f1c3b0fcSJames Smart 799f1c3b0fcSJames Smart if (!evt_dat) { 800f1c3b0fcSJames Smart job->reply->reply_payload_rcv_len = 0; 801f1c3b0fcSJames Smart rc = -ENOENT; 802f1c3b0fcSJames Smart goto error_get_event_exit; 803f1c3b0fcSJames Smart } 804f1c3b0fcSJames Smart 805f1c3b0fcSJames Smart if (evt_dat->len > job->reply_payload.payload_len) { 806f1c3b0fcSJames Smart evt_dat->len = job->reply_payload.payload_len; 807f1c3b0fcSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 808f1c3b0fcSJames Smart "2618 Truncated event data at %d " 809f1c3b0fcSJames Smart "bytes\n", 810f1c3b0fcSJames Smart job->reply_payload.payload_len); 811f1c3b0fcSJames Smart } 812f1c3b0fcSJames Smart 813f1c3b0fcSJames Smart event_reply->immed_data = evt_dat->immed_dat; 814f1c3b0fcSJames Smart 815f1c3b0fcSJames Smart if (evt_dat->len > 0) 816f1c3b0fcSJames Smart job->reply->reply_payload_rcv_len = 817f1c3b0fcSJames Smart sg_copy_from_buffer(job->reply_payload.sg_list, 818f1c3b0fcSJames Smart job->reply_payload.sg_cnt, 819f1c3b0fcSJames Smart evt_dat->data, evt_dat->len); 820f1c3b0fcSJames Smart else 821f1c3b0fcSJames Smart job->reply->reply_payload_rcv_len = 0; 822f1c3b0fcSJames Smart rc = 0; 823f1c3b0fcSJames Smart 824f1c3b0fcSJames Smart if (evt_dat) 825f1c3b0fcSJames Smart kfree(evt_dat->data); 826f1c3b0fcSJames Smart kfree(evt_dat); 827*4fede78fSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 828f1c3b0fcSJames Smart lpfc_ct_event_unref(evt); 829*4fede78fSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 830f1c3b0fcSJames Smart 831f1c3b0fcSJames Smart error_get_event_exit: 832f1c3b0fcSJames Smart /* make error code available to userspace */ 833f1c3b0fcSJames Smart job->reply->result = rc; 834f1c3b0fcSJames Smart /* complete the job back to userspace */ 835f1c3b0fcSJames Smart job->job_done(job); 836f1c3b0fcSJames Smart 837f1c3b0fcSJames Smart return rc; 838f1c3b0fcSJames Smart } 839f1c3b0fcSJames Smart 840f1c3b0fcSJames Smart /** 841f1c3b0fcSJames Smart * lpfc_bsg_hst_vendor - process a vendor-specific fc_bsg_job 842f1c3b0fcSJames Smart * @job: fc_bsg_job to handle 843f1c3b0fcSJames Smart */ 844f1c3b0fcSJames Smart static int 845f1c3b0fcSJames Smart lpfc_bsg_hst_vendor(struct fc_bsg_job *job) 846f1c3b0fcSJames Smart { 847f1c3b0fcSJames Smart int command = job->request->rqst_data.h_vendor.vendor_cmd[0]; 848f1c3b0fcSJames Smart 849f1c3b0fcSJames Smart switch (command) { 850f1c3b0fcSJames Smart case LPFC_BSG_VENDOR_SET_CT_EVENT: 851f1c3b0fcSJames Smart return lpfc_bsg_set_event(job); 852f1c3b0fcSJames Smart break; 853f1c3b0fcSJames Smart 854f1c3b0fcSJames Smart case LPFC_BSG_VENDOR_GET_CT_EVENT: 855f1c3b0fcSJames Smart return lpfc_bsg_get_event(job); 856f1c3b0fcSJames Smart break; 857f1c3b0fcSJames Smart 858f1c3b0fcSJames Smart default: 859f1c3b0fcSJames Smart return -EINVAL; 860f1c3b0fcSJames Smart } 861f1c3b0fcSJames Smart } 862f1c3b0fcSJames Smart 863f1c3b0fcSJames Smart /** 864f1c3b0fcSJames Smart * lpfc_bsg_request - handle a bsg request from the FC transport 865f1c3b0fcSJames Smart * @job: fc_bsg_job to handle 866f1c3b0fcSJames Smart */ 867f1c3b0fcSJames Smart int 868f1c3b0fcSJames Smart lpfc_bsg_request(struct fc_bsg_job *job) 869f1c3b0fcSJames Smart { 870f1c3b0fcSJames Smart uint32_t msgcode; 871f1c3b0fcSJames Smart int rc = -EINVAL; 872f1c3b0fcSJames Smart 873f1c3b0fcSJames Smart msgcode = job->request->msgcode; 874f1c3b0fcSJames Smart 875f1c3b0fcSJames Smart switch (msgcode) { 876f1c3b0fcSJames Smart case FC_BSG_HST_VENDOR: 877f1c3b0fcSJames Smart rc = lpfc_bsg_hst_vendor(job); 878f1c3b0fcSJames Smart break; 879f1c3b0fcSJames Smart case FC_BSG_RPT_ELS: 880f1c3b0fcSJames Smart rc = lpfc_bsg_rport_els(job); 881f1c3b0fcSJames Smart break; 882f1c3b0fcSJames Smart case FC_BSG_RPT_CT: 883f1c3b0fcSJames Smart rc = lpfc_bsg_rport_ct(job); 884f1c3b0fcSJames Smart break; 885f1c3b0fcSJames Smart default: 886f1c3b0fcSJames Smart break; 887f1c3b0fcSJames Smart } 888f1c3b0fcSJames Smart 889f1c3b0fcSJames Smart return rc; 890f1c3b0fcSJames Smart } 891f1c3b0fcSJames Smart 892f1c3b0fcSJames Smart /** 893f1c3b0fcSJames Smart * lpfc_bsg_timeout - handle timeout of a bsg request from the FC transport 894f1c3b0fcSJames Smart * @job: fc_bsg_job that has timed out 895f1c3b0fcSJames Smart * 896f1c3b0fcSJames Smart * This function just aborts the job's IOCB. The aborted IOCB will return to 897f1c3b0fcSJames Smart * the waiting function which will handle passing the error back to userspace 898f1c3b0fcSJames Smart */ 899f1c3b0fcSJames Smart int 900f1c3b0fcSJames Smart lpfc_bsg_timeout(struct fc_bsg_job *job) 901f1c3b0fcSJames Smart { 902f1c3b0fcSJames Smart struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; 903f1c3b0fcSJames Smart struct lpfc_hba *phba = vport->phba; 904f1c3b0fcSJames Smart struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *)job->dd_data; 905f1c3b0fcSJames Smart struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING]; 906f1c3b0fcSJames Smart 907f1c3b0fcSJames Smart if (cmdiocb) 908f1c3b0fcSJames Smart lpfc_sli_issue_abort_iotag(phba, pring, cmdiocb); 909f1c3b0fcSJames Smart 910f1c3b0fcSJames Smart return 0; 911f1c3b0fcSJames Smart } 912