1f1c3b0fcSJames Smart /******************************************************************* 2f1c3b0fcSJames Smart * This file is part of the Emulex Linux Device Driver for * 3f1c3b0fcSJames Smart * Fibre Channel Host Bus Adapters. * 44fede78fSJames 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> 245a0e3ad6STejun Heo #include <linux/slab.h> 25277e76f1SJames Smart #include <linux/delay.h> 26f1c3b0fcSJames Smart 27f1c3b0fcSJames Smart #include <scsi/scsi.h> 28f1c3b0fcSJames Smart #include <scsi/scsi_host.h> 29f1c3b0fcSJames Smart #include <scsi/scsi_transport_fc.h> 30f1c3b0fcSJames Smart #include <scsi/scsi_bsg_fc.h> 316a9c52cfSJames Smart #include <scsi/fc/fc_fs.h> 32f1c3b0fcSJames Smart 33f1c3b0fcSJames Smart #include "lpfc_hw4.h" 34f1c3b0fcSJames Smart #include "lpfc_hw.h" 35f1c3b0fcSJames Smart #include "lpfc_sli.h" 36f1c3b0fcSJames Smart #include "lpfc_sli4.h" 37f1c3b0fcSJames Smart #include "lpfc_nl.h" 384fede78fSJames Smart #include "lpfc_bsg.h" 39f1c3b0fcSJames Smart #include "lpfc_disc.h" 40f1c3b0fcSJames Smart #include "lpfc_scsi.h" 41f1c3b0fcSJames Smart #include "lpfc.h" 42f1c3b0fcSJames Smart #include "lpfc_logmsg.h" 43f1c3b0fcSJames Smart #include "lpfc_crtn.h" 44f1c3b0fcSJames Smart #include "lpfc_vport.h" 45f1c3b0fcSJames Smart #include "lpfc_version.h" 46f1c3b0fcSJames Smart 474cc0e56eSJames Smart struct lpfc_bsg_event { 484cc0e56eSJames Smart struct list_head node; 494cc0e56eSJames Smart struct kref kref; 504cc0e56eSJames Smart wait_queue_head_t wq; 514cc0e56eSJames Smart 524cc0e56eSJames Smart /* Event type and waiter identifiers */ 534cc0e56eSJames Smart uint32_t type_mask; 544cc0e56eSJames Smart uint32_t req_id; 554cc0e56eSJames Smart uint32_t reg_id; 564cc0e56eSJames Smart 574cc0e56eSJames Smart /* next two flags are here for the auto-delete logic */ 584cc0e56eSJames Smart unsigned long wait_time_stamp; 594cc0e56eSJames Smart int waiting; 604cc0e56eSJames Smart 614cc0e56eSJames Smart /* seen and not seen events */ 624cc0e56eSJames Smart struct list_head events_to_get; 634cc0e56eSJames Smart struct list_head events_to_see; 644cc0e56eSJames Smart 654cc0e56eSJames Smart /* job waiting for this event to finish */ 664cc0e56eSJames Smart struct fc_bsg_job *set_job; 674cc0e56eSJames Smart }; 684cc0e56eSJames Smart 694cc0e56eSJames Smart struct lpfc_bsg_iocb { 704cc0e56eSJames Smart struct lpfc_iocbq *cmdiocbq; 714cc0e56eSJames Smart struct lpfc_iocbq *rspiocbq; 724cc0e56eSJames Smart struct lpfc_dmabuf *bmp; 734cc0e56eSJames Smart struct lpfc_nodelist *ndlp; 744cc0e56eSJames Smart 754cc0e56eSJames Smart /* job waiting for this iocb to finish */ 764cc0e56eSJames Smart struct fc_bsg_job *set_job; 774cc0e56eSJames Smart }; 784cc0e56eSJames Smart 793b5dd52aSJames Smart struct lpfc_bsg_mbox { 803b5dd52aSJames Smart LPFC_MBOXQ_t *pmboxq; 813b5dd52aSJames Smart MAILBOX_t *mb; 827a470277SJames Smart struct lpfc_dmabuf *rxbmp; /* for BIU diags */ 837a470277SJames Smart struct lpfc_dmabufext *dmp; /* for BIU diags */ 847a470277SJames Smart uint8_t *ext; /* extended mailbox data */ 857a470277SJames Smart uint32_t mbOffset; /* from app */ 867a470277SJames Smart uint32_t inExtWLen; /* from app */ 87c7495937SJames Smart uint32_t outExtWLen; /* from app */ 883b5dd52aSJames Smart 893b5dd52aSJames Smart /* job waiting for this mbox command to finish */ 903b5dd52aSJames Smart struct fc_bsg_job *set_job; 913b5dd52aSJames Smart }; 923b5dd52aSJames Smart 93e2aed29fSJames Smart #define MENLO_DID 0x0000FC0E 94e2aed29fSJames Smart 95e2aed29fSJames Smart struct lpfc_bsg_menlo { 96e2aed29fSJames Smart struct lpfc_iocbq *cmdiocbq; 97e2aed29fSJames Smart struct lpfc_iocbq *rspiocbq; 98e2aed29fSJames Smart struct lpfc_dmabuf *bmp; 99e2aed29fSJames Smart 100e2aed29fSJames Smart /* job waiting for this iocb to finish */ 101e2aed29fSJames Smart struct fc_bsg_job *set_job; 102e2aed29fSJames Smart }; 103e2aed29fSJames Smart 1044cc0e56eSJames Smart #define TYPE_EVT 1 1054cc0e56eSJames Smart #define TYPE_IOCB 2 1063b5dd52aSJames Smart #define TYPE_MBOX 3 107e2aed29fSJames Smart #define TYPE_MENLO 4 1084cc0e56eSJames Smart struct bsg_job_data { 1094cc0e56eSJames Smart uint32_t type; 1104cc0e56eSJames Smart union { 1114cc0e56eSJames Smart struct lpfc_bsg_event *evt; 1124cc0e56eSJames Smart struct lpfc_bsg_iocb iocb; 1133b5dd52aSJames Smart struct lpfc_bsg_mbox mbox; 114e2aed29fSJames Smart struct lpfc_bsg_menlo menlo; 1154cc0e56eSJames Smart } context_un; 1164cc0e56eSJames Smart }; 1174cc0e56eSJames Smart 1184cc0e56eSJames Smart struct event_data { 1194cc0e56eSJames Smart struct list_head node; 1204cc0e56eSJames Smart uint32_t type; 1214cc0e56eSJames Smart uint32_t immed_dat; 1224cc0e56eSJames Smart void *data; 1234cc0e56eSJames Smart uint32_t len; 1244cc0e56eSJames Smart }; 1254cc0e56eSJames Smart 1263b5dd52aSJames Smart #define BUF_SZ_4K 4096 1274cc0e56eSJames Smart #define SLI_CT_ELX_LOOPBACK 0x10 1284cc0e56eSJames Smart 1294cc0e56eSJames Smart enum ELX_LOOPBACK_CMD { 1304cc0e56eSJames Smart ELX_LOOPBACK_XRI_SETUP, 1314cc0e56eSJames Smart ELX_LOOPBACK_DATA, 1324cc0e56eSJames Smart }; 1334cc0e56eSJames Smart 1343b5dd52aSJames Smart #define ELX_LOOPBACK_HEADER_SZ \ 1353b5dd52aSJames Smart (size_t)(&((struct lpfc_sli_ct_request *)NULL)->un) 1363b5dd52aSJames Smart 1374cc0e56eSJames Smart struct lpfc_dmabufext { 1384cc0e56eSJames Smart struct lpfc_dmabuf dma; 1394cc0e56eSJames Smart uint32_t size; 1404cc0e56eSJames Smart uint32_t flag; 1414cc0e56eSJames Smart }; 1424cc0e56eSJames Smart 143f1c3b0fcSJames Smart /** 1444cc0e56eSJames Smart * lpfc_bsg_send_mgmt_cmd_cmp - lpfc_bsg_send_mgmt_cmd's completion handler 1454cc0e56eSJames Smart * @phba: Pointer to HBA context object. 1464cc0e56eSJames Smart * @cmdiocbq: Pointer to command iocb. 1474cc0e56eSJames Smart * @rspiocbq: Pointer to response iocb. 1484cc0e56eSJames Smart * 1494cc0e56eSJames Smart * This function is the completion handler for iocbs issued using 1504cc0e56eSJames Smart * lpfc_bsg_send_mgmt_cmd function. This function is called by the 1514cc0e56eSJames Smart * ring event handler function without any lock held. This function 1524cc0e56eSJames Smart * can be called from both worker thread context and interrupt 1534cc0e56eSJames Smart * context. This function also can be called from another thread which 1544cc0e56eSJames Smart * cleans up the SLI layer objects. 1554cc0e56eSJames Smart * This function copies the contents of the response iocb to the 1564cc0e56eSJames Smart * response iocb memory object provided by the caller of 1574cc0e56eSJames Smart * lpfc_sli_issue_iocb_wait and then wakes up the thread which 1584cc0e56eSJames Smart * sleeps for the iocb completion. 1594cc0e56eSJames Smart **/ 1604cc0e56eSJames Smart static void 1614cc0e56eSJames Smart lpfc_bsg_send_mgmt_cmd_cmp(struct lpfc_hba *phba, 1624cc0e56eSJames Smart struct lpfc_iocbq *cmdiocbq, 1634cc0e56eSJames Smart struct lpfc_iocbq *rspiocbq) 1644cc0e56eSJames Smart { 1654cc0e56eSJames Smart unsigned long iflags; 1664cc0e56eSJames Smart struct bsg_job_data *dd_data; 1674cc0e56eSJames Smart struct fc_bsg_job *job; 1684cc0e56eSJames Smart IOCB_t *rsp; 1694cc0e56eSJames Smart struct lpfc_dmabuf *bmp; 1704cc0e56eSJames Smart struct lpfc_nodelist *ndlp; 1714cc0e56eSJames Smart struct lpfc_bsg_iocb *iocb; 1724cc0e56eSJames Smart unsigned long flags; 1734cc0e56eSJames Smart int rc = 0; 1744cc0e56eSJames Smart 1754cc0e56eSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 1764cc0e56eSJames Smart dd_data = cmdiocbq->context1; 1774cc0e56eSJames Smart if (!dd_data) { 1784cc0e56eSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 1794cc0e56eSJames Smart return; 1804cc0e56eSJames Smart } 1814cc0e56eSJames Smart 1824cc0e56eSJames Smart iocb = &dd_data->context_un.iocb; 1834cc0e56eSJames Smart job = iocb->set_job; 1844cc0e56eSJames Smart job->dd_data = NULL; /* so timeout handler does not reply */ 1854cc0e56eSJames Smart 1864cc0e56eSJames Smart spin_lock_irqsave(&phba->hbalock, iflags); 1874cc0e56eSJames Smart cmdiocbq->iocb_flag |= LPFC_IO_WAKE; 1884cc0e56eSJames Smart if (cmdiocbq->context2 && rspiocbq) 1894cc0e56eSJames Smart memcpy(&((struct lpfc_iocbq *)cmdiocbq->context2)->iocb, 1904cc0e56eSJames Smart &rspiocbq->iocb, sizeof(IOCB_t)); 1914cc0e56eSJames Smart spin_unlock_irqrestore(&phba->hbalock, iflags); 1924cc0e56eSJames Smart 1934cc0e56eSJames Smart bmp = iocb->bmp; 1944cc0e56eSJames Smart rspiocbq = iocb->rspiocbq; 1954cc0e56eSJames Smart rsp = &rspiocbq->iocb; 1964cc0e56eSJames Smart ndlp = iocb->ndlp; 1974cc0e56eSJames Smart 1984cc0e56eSJames Smart pci_unmap_sg(phba->pcidev, job->request_payload.sg_list, 1994cc0e56eSJames Smart job->request_payload.sg_cnt, DMA_TO_DEVICE); 2004cc0e56eSJames Smart pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list, 2014cc0e56eSJames Smart job->reply_payload.sg_cnt, DMA_FROM_DEVICE); 2024cc0e56eSJames Smart 2034cc0e56eSJames Smart if (rsp->ulpStatus) { 2044cc0e56eSJames Smart if (rsp->ulpStatus == IOSTAT_LOCAL_REJECT) { 2054cc0e56eSJames Smart switch (rsp->un.ulpWord[4] & 0xff) { 2064cc0e56eSJames Smart case IOERR_SEQUENCE_TIMEOUT: 2074cc0e56eSJames Smart rc = -ETIMEDOUT; 2084cc0e56eSJames Smart break; 2094cc0e56eSJames Smart case IOERR_INVALID_RPI: 2104cc0e56eSJames Smart rc = -EFAULT; 2114cc0e56eSJames Smart break; 2124cc0e56eSJames Smart default: 2134cc0e56eSJames Smart rc = -EACCES; 2144cc0e56eSJames Smart break; 2154cc0e56eSJames Smart } 2164cc0e56eSJames Smart } else 2174cc0e56eSJames Smart rc = -EACCES; 2184cc0e56eSJames Smart } else 2194cc0e56eSJames Smart job->reply->reply_payload_rcv_len = 2204cc0e56eSJames Smart rsp->un.genreq64.bdl.bdeSize; 2214cc0e56eSJames Smart 2224cc0e56eSJames Smart lpfc_mbuf_free(phba, bmp->virt, bmp->phys); 2234cc0e56eSJames Smart lpfc_sli_release_iocbq(phba, rspiocbq); 2244cc0e56eSJames Smart lpfc_sli_release_iocbq(phba, cmdiocbq); 2254cc0e56eSJames Smart lpfc_nlp_put(ndlp); 2264cc0e56eSJames Smart kfree(bmp); 2274cc0e56eSJames Smart kfree(dd_data); 2284cc0e56eSJames Smart /* make error code available to userspace */ 2294cc0e56eSJames Smart job->reply->result = rc; 2304cc0e56eSJames Smart /* complete the job back to userspace */ 2314cc0e56eSJames Smart job->job_done(job); 2324cc0e56eSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 2334cc0e56eSJames Smart return; 2344cc0e56eSJames Smart } 2354cc0e56eSJames Smart 2364cc0e56eSJames Smart /** 2374cc0e56eSJames Smart * lpfc_bsg_send_mgmt_cmd - send a CT command from a bsg request 238f1c3b0fcSJames Smart * @job: fc_bsg_job to handle 2393b5dd52aSJames Smart **/ 240f1c3b0fcSJames Smart static int 2414cc0e56eSJames Smart lpfc_bsg_send_mgmt_cmd(struct fc_bsg_job *job) 242f1c3b0fcSJames Smart { 243f1c3b0fcSJames Smart struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; 244f1c3b0fcSJames Smart struct lpfc_hba *phba = vport->phba; 245f1c3b0fcSJames Smart struct lpfc_rport_data *rdata = job->rport->dd_data; 246f1c3b0fcSJames Smart struct lpfc_nodelist *ndlp = rdata->pnode; 247f1c3b0fcSJames Smart struct ulp_bde64 *bpl = NULL; 248f1c3b0fcSJames Smart uint32_t timeout; 249f1c3b0fcSJames Smart struct lpfc_iocbq *cmdiocbq = NULL; 250f1c3b0fcSJames Smart struct lpfc_iocbq *rspiocbq = NULL; 251f1c3b0fcSJames Smart IOCB_t *cmd; 252f1c3b0fcSJames Smart IOCB_t *rsp; 253f1c3b0fcSJames Smart struct lpfc_dmabuf *bmp = NULL; 254f1c3b0fcSJames Smart int request_nseg; 255f1c3b0fcSJames Smart int reply_nseg; 256f1c3b0fcSJames Smart struct scatterlist *sgel = NULL; 257f1c3b0fcSJames Smart int numbde; 258f1c3b0fcSJames Smart dma_addr_t busaddr; 2594cc0e56eSJames Smart struct bsg_job_data *dd_data; 2604cc0e56eSJames Smart uint32_t creg_val; 261f1c3b0fcSJames Smart int rc = 0; 262*d439d286SJames Smart int iocb_stat; 263f1c3b0fcSJames Smart 264f1c3b0fcSJames Smart /* in case no data is transferred */ 265f1c3b0fcSJames Smart job->reply->reply_payload_rcv_len = 0; 266f1c3b0fcSJames Smart 2674cc0e56eSJames Smart /* allocate our bsg tracking structure */ 2684cc0e56eSJames Smart dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL); 2694cc0e56eSJames Smart if (!dd_data) { 2704cc0e56eSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 2714cc0e56eSJames Smart "2733 Failed allocation of dd_data\n"); 2724cc0e56eSJames Smart rc = -ENOMEM; 2734cc0e56eSJames Smart goto no_dd_data; 2744cc0e56eSJames Smart } 2754cc0e56eSJames Smart 276f1c3b0fcSJames Smart if (!lpfc_nlp_get(ndlp)) { 2774cc0e56eSJames Smart rc = -ENODEV; 2784cc0e56eSJames Smart goto no_ndlp; 2794cc0e56eSJames Smart } 2804cc0e56eSJames Smart 2814cc0e56eSJames Smart bmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); 2824cc0e56eSJames Smart if (!bmp) { 2834cc0e56eSJames Smart rc = -ENOMEM; 2844cc0e56eSJames Smart goto free_ndlp; 285f1c3b0fcSJames Smart } 286f1c3b0fcSJames Smart 287f1c3b0fcSJames Smart if (ndlp->nlp_flag & NLP_ELS_SND_MASK) { 288f1c3b0fcSJames Smart rc = -ENODEV; 2894cc0e56eSJames Smart goto free_bmp; 290f1c3b0fcSJames Smart } 291f1c3b0fcSJames Smart 292f1c3b0fcSJames Smart cmdiocbq = lpfc_sli_get_iocbq(phba); 293f1c3b0fcSJames Smart if (!cmdiocbq) { 294f1c3b0fcSJames Smart rc = -ENOMEM; 2954cc0e56eSJames Smart goto free_bmp; 296f1c3b0fcSJames Smart } 297f1c3b0fcSJames Smart 2984cc0e56eSJames Smart cmd = &cmdiocbq->iocb; 299f1c3b0fcSJames Smart rspiocbq = lpfc_sli_get_iocbq(phba); 300f1c3b0fcSJames Smart if (!rspiocbq) { 301f1c3b0fcSJames Smart rc = -ENOMEM; 302f1c3b0fcSJames Smart goto free_cmdiocbq; 303f1c3b0fcSJames Smart } 304f1c3b0fcSJames Smart 305f1c3b0fcSJames Smart rsp = &rspiocbq->iocb; 306f1c3b0fcSJames Smart bmp->virt = lpfc_mbuf_alloc(phba, 0, &bmp->phys); 307f1c3b0fcSJames Smart if (!bmp->virt) { 308f1c3b0fcSJames Smart rc = -ENOMEM; 3094cc0e56eSJames Smart goto free_rspiocbq; 310f1c3b0fcSJames Smart } 311f1c3b0fcSJames Smart 312f1c3b0fcSJames Smart INIT_LIST_HEAD(&bmp->list); 313f1c3b0fcSJames Smart bpl = (struct ulp_bde64 *) bmp->virt; 314f1c3b0fcSJames Smart request_nseg = pci_map_sg(phba->pcidev, job->request_payload.sg_list, 315f1c3b0fcSJames Smart job->request_payload.sg_cnt, DMA_TO_DEVICE); 316f1c3b0fcSJames Smart for_each_sg(job->request_payload.sg_list, sgel, request_nseg, numbde) { 317f1c3b0fcSJames Smart busaddr = sg_dma_address(sgel); 318f1c3b0fcSJames Smart bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64; 319f1c3b0fcSJames Smart bpl->tus.f.bdeSize = sg_dma_len(sgel); 320f1c3b0fcSJames Smart bpl->tus.w = cpu_to_le32(bpl->tus.w); 321f1c3b0fcSJames Smart bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr)); 322f1c3b0fcSJames Smart bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr)); 323f1c3b0fcSJames Smart bpl++; 324f1c3b0fcSJames Smart } 325f1c3b0fcSJames Smart 326f1c3b0fcSJames Smart reply_nseg = pci_map_sg(phba->pcidev, job->reply_payload.sg_list, 327f1c3b0fcSJames Smart job->reply_payload.sg_cnt, DMA_FROM_DEVICE); 328f1c3b0fcSJames Smart for_each_sg(job->reply_payload.sg_list, sgel, reply_nseg, numbde) { 329f1c3b0fcSJames Smart busaddr = sg_dma_address(sgel); 330f1c3b0fcSJames Smart bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I; 331f1c3b0fcSJames Smart bpl->tus.f.bdeSize = sg_dma_len(sgel); 332f1c3b0fcSJames Smart bpl->tus.w = cpu_to_le32(bpl->tus.w); 333f1c3b0fcSJames Smart bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr)); 334f1c3b0fcSJames Smart bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr)); 335f1c3b0fcSJames Smart bpl++; 336f1c3b0fcSJames Smart } 337f1c3b0fcSJames Smart 338f1c3b0fcSJames Smart cmd->un.genreq64.bdl.ulpIoTag32 = 0; 339f1c3b0fcSJames Smart cmd->un.genreq64.bdl.addrHigh = putPaddrHigh(bmp->phys); 340f1c3b0fcSJames Smart cmd->un.genreq64.bdl.addrLow = putPaddrLow(bmp->phys); 341f1c3b0fcSJames Smart cmd->un.genreq64.bdl.bdeFlags = BUFF_TYPE_BLP_64; 342f1c3b0fcSJames Smart cmd->un.genreq64.bdl.bdeSize = 343f1c3b0fcSJames Smart (request_nseg + reply_nseg) * sizeof(struct ulp_bde64); 344f1c3b0fcSJames Smart cmd->ulpCommand = CMD_GEN_REQUEST64_CR; 345f1c3b0fcSJames Smart cmd->un.genreq64.w5.hcsw.Fctl = (SI | LA); 346f1c3b0fcSJames Smart cmd->un.genreq64.w5.hcsw.Dfctl = 0; 3476a9c52cfSJames Smart cmd->un.genreq64.w5.hcsw.Rctl = FC_RCTL_DD_UNSOL_CTL; 3486a9c52cfSJames Smart cmd->un.genreq64.w5.hcsw.Type = FC_TYPE_CT; 349f1c3b0fcSJames Smart cmd->ulpBdeCount = 1; 350f1c3b0fcSJames Smart cmd->ulpLe = 1; 351f1c3b0fcSJames Smart cmd->ulpClass = CLASS3; 352f1c3b0fcSJames Smart cmd->ulpContext = ndlp->nlp_rpi; 353f1c3b0fcSJames Smart cmd->ulpOwner = OWN_CHIP; 354f1c3b0fcSJames Smart cmdiocbq->vport = phba->pport; 3554cc0e56eSJames Smart cmdiocbq->context3 = bmp; 356f1c3b0fcSJames Smart cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC; 357f1c3b0fcSJames Smart timeout = phba->fc_ratov * 2; 3584cc0e56eSJames Smart cmd->ulpTimeout = timeout; 359f1c3b0fcSJames Smart 3604cc0e56eSJames Smart cmdiocbq->iocb_cmpl = lpfc_bsg_send_mgmt_cmd_cmp; 3614cc0e56eSJames Smart cmdiocbq->context1 = dd_data; 3624cc0e56eSJames Smart cmdiocbq->context2 = rspiocbq; 3634cc0e56eSJames Smart dd_data->type = TYPE_IOCB; 3644cc0e56eSJames Smart dd_data->context_un.iocb.cmdiocbq = cmdiocbq; 3654cc0e56eSJames Smart dd_data->context_un.iocb.rspiocbq = rspiocbq; 3664cc0e56eSJames Smart dd_data->context_un.iocb.set_job = job; 3674cc0e56eSJames Smart dd_data->context_un.iocb.bmp = bmp; 3684cc0e56eSJames Smart dd_data->context_un.iocb.ndlp = ndlp; 369f1c3b0fcSJames Smart 3704cc0e56eSJames Smart if (phba->cfg_poll & DISABLE_FCP_RING_INT) { 3714cc0e56eSJames Smart creg_val = readl(phba->HCregaddr); 3724cc0e56eSJames Smart creg_val |= (HC_R0INT_ENA << LPFC_FCP_RING); 3734cc0e56eSJames Smart writel(creg_val, phba->HCregaddr); 3744cc0e56eSJames Smart readl(phba->HCregaddr); /* flush */ 3754cc0e56eSJames Smart } 3764cc0e56eSJames Smart 377*d439d286SJames Smart iocb_stat = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq, 0); 378*d439d286SJames Smart if (iocb_stat == IOCB_SUCCESS) 3794cc0e56eSJames Smart return 0; /* done for now */ 380*d439d286SJames Smart else if (iocb_stat == IOCB_BUSY) 381*d439d286SJames Smart rc = -EAGAIN; 3822a9bf3d0SJames Smart else 383*d439d286SJames Smart rc = -EIO; 3842a9bf3d0SJames Smart 3854cc0e56eSJames Smart 3864cc0e56eSJames Smart /* iocb failed so cleanup */ 387f1c3b0fcSJames Smart pci_unmap_sg(phba->pcidev, job->request_payload.sg_list, 388f1c3b0fcSJames Smart job->request_payload.sg_cnt, DMA_TO_DEVICE); 389f1c3b0fcSJames Smart pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list, 390f1c3b0fcSJames Smart job->reply_payload.sg_cnt, DMA_FROM_DEVICE); 391f1c3b0fcSJames Smart 392f1c3b0fcSJames Smart lpfc_mbuf_free(phba, bmp->virt, bmp->phys); 3934cc0e56eSJames Smart 394f1c3b0fcSJames Smart free_rspiocbq: 395f1c3b0fcSJames Smart lpfc_sli_release_iocbq(phba, rspiocbq); 396f1c3b0fcSJames Smart free_cmdiocbq: 397f1c3b0fcSJames Smart lpfc_sli_release_iocbq(phba, cmdiocbq); 3984cc0e56eSJames Smart free_bmp: 3994cc0e56eSJames Smart kfree(bmp); 4004cc0e56eSJames Smart free_ndlp: 401f1c3b0fcSJames Smart lpfc_nlp_put(ndlp); 4024cc0e56eSJames Smart no_ndlp: 4034cc0e56eSJames Smart kfree(dd_data); 4044cc0e56eSJames Smart no_dd_data: 405f1c3b0fcSJames Smart /* make error code available to userspace */ 406f1c3b0fcSJames Smart job->reply->result = rc; 4074cc0e56eSJames Smart job->dd_data = NULL; 4084cc0e56eSJames Smart return rc; 4094cc0e56eSJames Smart } 4104cc0e56eSJames Smart 4114cc0e56eSJames Smart /** 4124cc0e56eSJames Smart * lpfc_bsg_rport_els_cmp - lpfc_bsg_rport_els's completion handler 4134cc0e56eSJames Smart * @phba: Pointer to HBA context object. 4144cc0e56eSJames Smart * @cmdiocbq: Pointer to command iocb. 4154cc0e56eSJames Smart * @rspiocbq: Pointer to response iocb. 4164cc0e56eSJames Smart * 4174cc0e56eSJames Smart * This function is the completion handler for iocbs issued using 4184cc0e56eSJames Smart * lpfc_bsg_rport_els_cmp function. This function is called by the 4194cc0e56eSJames Smart * ring event handler function without any lock held. This function 4204cc0e56eSJames Smart * can be called from both worker thread context and interrupt 4214cc0e56eSJames Smart * context. This function also can be called from other thread which 4224cc0e56eSJames Smart * cleans up the SLI layer objects. 4233b5dd52aSJames Smart * This function copies the contents of the response iocb to the 4244cc0e56eSJames Smart * response iocb memory object provided by the caller of 4254cc0e56eSJames Smart * lpfc_sli_issue_iocb_wait and then wakes up the thread which 4264cc0e56eSJames Smart * sleeps for the iocb completion. 4274cc0e56eSJames Smart **/ 4284cc0e56eSJames Smart static void 4294cc0e56eSJames Smart lpfc_bsg_rport_els_cmp(struct lpfc_hba *phba, 4304cc0e56eSJames Smart struct lpfc_iocbq *cmdiocbq, 4314cc0e56eSJames Smart struct lpfc_iocbq *rspiocbq) 4324cc0e56eSJames Smart { 4334cc0e56eSJames Smart struct bsg_job_data *dd_data; 4344cc0e56eSJames Smart struct fc_bsg_job *job; 4354cc0e56eSJames Smart IOCB_t *rsp; 4364cc0e56eSJames Smart struct lpfc_nodelist *ndlp; 4374cc0e56eSJames Smart struct lpfc_dmabuf *pbuflist = NULL; 4384cc0e56eSJames Smart struct fc_bsg_ctels_reply *els_reply; 4394cc0e56eSJames Smart uint8_t *rjt_data; 4404cc0e56eSJames Smart unsigned long flags; 4414cc0e56eSJames Smart int rc = 0; 4424cc0e56eSJames Smart 4434cc0e56eSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 4444cc0e56eSJames Smart dd_data = cmdiocbq->context1; 4454cc0e56eSJames Smart /* normal completion and timeout crossed paths, already done */ 4464cc0e56eSJames Smart if (!dd_data) { 44767221a42SJiri Slaby spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 4484cc0e56eSJames Smart return; 4494cc0e56eSJames Smart } 4504cc0e56eSJames Smart 4514cc0e56eSJames Smart cmdiocbq->iocb_flag |= LPFC_IO_WAKE; 4524cc0e56eSJames Smart if (cmdiocbq->context2 && rspiocbq) 4534cc0e56eSJames Smart memcpy(&((struct lpfc_iocbq *)cmdiocbq->context2)->iocb, 4544cc0e56eSJames Smart &rspiocbq->iocb, sizeof(IOCB_t)); 4554cc0e56eSJames Smart 4564cc0e56eSJames Smart job = dd_data->context_un.iocb.set_job; 4574cc0e56eSJames Smart cmdiocbq = dd_data->context_un.iocb.cmdiocbq; 4584cc0e56eSJames Smart rspiocbq = dd_data->context_un.iocb.rspiocbq; 4594cc0e56eSJames Smart rsp = &rspiocbq->iocb; 4604cc0e56eSJames Smart ndlp = dd_data->context_un.iocb.ndlp; 4614cc0e56eSJames Smart 4624cc0e56eSJames Smart pci_unmap_sg(phba->pcidev, job->request_payload.sg_list, 4634cc0e56eSJames Smart job->request_payload.sg_cnt, DMA_TO_DEVICE); 4644cc0e56eSJames Smart pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list, 4654cc0e56eSJames Smart job->reply_payload.sg_cnt, DMA_FROM_DEVICE); 4664cc0e56eSJames Smart 4674cc0e56eSJames Smart if (job->reply->result == -EAGAIN) 4684cc0e56eSJames Smart rc = -EAGAIN; 4694cc0e56eSJames Smart else if (rsp->ulpStatus == IOSTAT_SUCCESS) 4704cc0e56eSJames Smart job->reply->reply_payload_rcv_len = 4714cc0e56eSJames Smart rsp->un.elsreq64.bdl.bdeSize; 4724cc0e56eSJames Smart else if (rsp->ulpStatus == IOSTAT_LS_RJT) { 4734cc0e56eSJames Smart job->reply->reply_payload_rcv_len = 4744cc0e56eSJames Smart sizeof(struct fc_bsg_ctels_reply); 4754cc0e56eSJames Smart /* LS_RJT data returned in word 4 */ 4764cc0e56eSJames Smart rjt_data = (uint8_t *)&rsp->un.ulpWord[4]; 4774cc0e56eSJames Smart els_reply = &job->reply->reply_data.ctels_reply; 4784cc0e56eSJames Smart els_reply->status = FC_CTELS_STATUS_REJECT; 4794cc0e56eSJames Smart els_reply->rjt_data.action = rjt_data[3]; 4804cc0e56eSJames Smart els_reply->rjt_data.reason_code = rjt_data[2]; 4814cc0e56eSJames Smart els_reply->rjt_data.reason_explanation = rjt_data[1]; 4824cc0e56eSJames Smart els_reply->rjt_data.vendor_unique = rjt_data[0]; 4834cc0e56eSJames Smart } else 4844cc0e56eSJames Smart rc = -EIO; 4854cc0e56eSJames Smart 4864cc0e56eSJames Smart pbuflist = (struct lpfc_dmabuf *) cmdiocbq->context3; 4874cc0e56eSJames Smart lpfc_mbuf_free(phba, pbuflist->virt, pbuflist->phys); 4884cc0e56eSJames Smart lpfc_sli_release_iocbq(phba, rspiocbq); 4894cc0e56eSJames Smart lpfc_sli_release_iocbq(phba, cmdiocbq); 4904cc0e56eSJames Smart lpfc_nlp_put(ndlp); 4914cc0e56eSJames Smart kfree(dd_data); 4924cc0e56eSJames Smart /* make error code available to userspace */ 4934cc0e56eSJames Smart job->reply->result = rc; 4944cc0e56eSJames Smart job->dd_data = NULL; 495f1c3b0fcSJames Smart /* complete the job back to userspace */ 496f1c3b0fcSJames Smart job->job_done(job); 4974cc0e56eSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 4984cc0e56eSJames Smart return; 499f1c3b0fcSJames Smart } 500f1c3b0fcSJames Smart 501f1c3b0fcSJames Smart /** 502f1c3b0fcSJames Smart * lpfc_bsg_rport_els - send an ELS command from a bsg request 503f1c3b0fcSJames Smart * @job: fc_bsg_job to handle 5043b5dd52aSJames Smart **/ 505f1c3b0fcSJames Smart static int 506f1c3b0fcSJames Smart lpfc_bsg_rport_els(struct fc_bsg_job *job) 507f1c3b0fcSJames Smart { 508f1c3b0fcSJames Smart struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; 509f1c3b0fcSJames Smart struct lpfc_hba *phba = vport->phba; 510f1c3b0fcSJames Smart struct lpfc_rport_data *rdata = job->rport->dd_data; 511f1c3b0fcSJames Smart struct lpfc_nodelist *ndlp = rdata->pnode; 512f1c3b0fcSJames Smart uint32_t elscmd; 513f1c3b0fcSJames Smart uint32_t cmdsize; 514f1c3b0fcSJames Smart uint32_t rspsize; 515f1c3b0fcSJames Smart struct lpfc_iocbq *rspiocbq; 516f1c3b0fcSJames Smart struct lpfc_iocbq *cmdiocbq; 517f1c3b0fcSJames Smart IOCB_t *rsp; 518f1c3b0fcSJames Smart uint16_t rpi = 0; 519f1c3b0fcSJames Smart struct lpfc_dmabuf *pcmd; 520f1c3b0fcSJames Smart struct lpfc_dmabuf *prsp; 521f1c3b0fcSJames Smart struct lpfc_dmabuf *pbuflist = NULL; 522f1c3b0fcSJames Smart struct ulp_bde64 *bpl; 523f1c3b0fcSJames Smart int request_nseg; 524f1c3b0fcSJames Smart int reply_nseg; 525f1c3b0fcSJames Smart struct scatterlist *sgel = NULL; 526f1c3b0fcSJames Smart int numbde; 527f1c3b0fcSJames Smart dma_addr_t busaddr; 5284cc0e56eSJames Smart struct bsg_job_data *dd_data; 5294cc0e56eSJames Smart uint32_t creg_val; 530f1c3b0fcSJames Smart int rc = 0; 531f1c3b0fcSJames Smart 532f1c3b0fcSJames Smart /* in case no data is transferred */ 533f1c3b0fcSJames Smart job->reply->reply_payload_rcv_len = 0; 534f1c3b0fcSJames Smart 5354cc0e56eSJames Smart /* allocate our bsg tracking structure */ 5364cc0e56eSJames Smart dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL); 5374cc0e56eSJames Smart if (!dd_data) { 5384cc0e56eSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 5394cc0e56eSJames Smart "2735 Failed allocation of dd_data\n"); 5404cc0e56eSJames Smart rc = -ENOMEM; 5414cc0e56eSJames Smart goto no_dd_data; 5424cc0e56eSJames Smart } 5434cc0e56eSJames Smart 544f1c3b0fcSJames Smart if (!lpfc_nlp_get(ndlp)) { 545f1c3b0fcSJames Smart rc = -ENODEV; 5464cc0e56eSJames Smart goto free_dd_data; 547f1c3b0fcSJames Smart } 548f1c3b0fcSJames Smart 549f1c3b0fcSJames Smart elscmd = job->request->rqst_data.r_els.els_code; 550f1c3b0fcSJames Smart cmdsize = job->request_payload.payload_len; 551f1c3b0fcSJames Smart rspsize = job->reply_payload.payload_len; 552f1c3b0fcSJames Smart rspiocbq = lpfc_sli_get_iocbq(phba); 553f1c3b0fcSJames Smart if (!rspiocbq) { 554f1c3b0fcSJames Smart lpfc_nlp_put(ndlp); 555f1c3b0fcSJames Smart rc = -ENOMEM; 5564cc0e56eSJames Smart goto free_dd_data; 557f1c3b0fcSJames Smart } 558f1c3b0fcSJames Smart 559f1c3b0fcSJames Smart rsp = &rspiocbq->iocb; 560f1c3b0fcSJames Smart rpi = ndlp->nlp_rpi; 561f1c3b0fcSJames Smart 5624cc0e56eSJames Smart cmdiocbq = lpfc_prep_els_iocb(vport, 1, cmdsize, 0, ndlp, 563f1c3b0fcSJames Smart ndlp->nlp_DID, elscmd); 564f1c3b0fcSJames Smart if (!cmdiocbq) { 5654cc0e56eSJames Smart rc = -EIO; 5664cc0e56eSJames Smart goto free_rspiocbq; 567f1c3b0fcSJames Smart } 568f1c3b0fcSJames Smart 5694cc0e56eSJames Smart /* prep els iocb set context1 to the ndlp, context2 to the command 5704cc0e56eSJames Smart * dmabuf, context3 holds the data dmabuf 5714cc0e56eSJames Smart */ 572f1c3b0fcSJames Smart pcmd = (struct lpfc_dmabuf *) cmdiocbq->context2; 573f1c3b0fcSJames Smart prsp = (struct lpfc_dmabuf *) pcmd->list.next; 574f1c3b0fcSJames Smart lpfc_mbuf_free(phba, pcmd->virt, pcmd->phys); 575f1c3b0fcSJames Smart kfree(pcmd); 576f1c3b0fcSJames Smart lpfc_mbuf_free(phba, prsp->virt, prsp->phys); 577f1c3b0fcSJames Smart kfree(prsp); 578f1c3b0fcSJames Smart cmdiocbq->context2 = NULL; 579f1c3b0fcSJames Smart 580f1c3b0fcSJames Smart pbuflist = (struct lpfc_dmabuf *) cmdiocbq->context3; 581f1c3b0fcSJames Smart bpl = (struct ulp_bde64 *) pbuflist->virt; 582f1c3b0fcSJames Smart 583f1c3b0fcSJames Smart request_nseg = pci_map_sg(phba->pcidev, job->request_payload.sg_list, 584f1c3b0fcSJames Smart job->request_payload.sg_cnt, DMA_TO_DEVICE); 585f1c3b0fcSJames Smart for_each_sg(job->request_payload.sg_list, sgel, request_nseg, numbde) { 586f1c3b0fcSJames Smart busaddr = sg_dma_address(sgel); 587f1c3b0fcSJames Smart bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64; 588f1c3b0fcSJames Smart bpl->tus.f.bdeSize = sg_dma_len(sgel); 589f1c3b0fcSJames Smart bpl->tus.w = cpu_to_le32(bpl->tus.w); 590f1c3b0fcSJames Smart bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr)); 591f1c3b0fcSJames Smart bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr)); 592f1c3b0fcSJames Smart bpl++; 593f1c3b0fcSJames Smart } 594f1c3b0fcSJames Smart 595f1c3b0fcSJames Smart reply_nseg = pci_map_sg(phba->pcidev, job->reply_payload.sg_list, 596f1c3b0fcSJames Smart job->reply_payload.sg_cnt, DMA_FROM_DEVICE); 597f1c3b0fcSJames Smart for_each_sg(job->reply_payload.sg_list, sgel, reply_nseg, numbde) { 598f1c3b0fcSJames Smart busaddr = sg_dma_address(sgel); 599f1c3b0fcSJames Smart bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I; 600f1c3b0fcSJames Smart bpl->tus.f.bdeSize = sg_dma_len(sgel); 601f1c3b0fcSJames Smart bpl->tus.w = cpu_to_le32(bpl->tus.w); 602f1c3b0fcSJames Smart bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr)); 603f1c3b0fcSJames Smart bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr)); 604f1c3b0fcSJames Smart bpl++; 605f1c3b0fcSJames Smart } 606f1c3b0fcSJames Smart cmdiocbq->iocb.un.elsreq64.bdl.bdeSize = 607f1c3b0fcSJames Smart (request_nseg + reply_nseg) * sizeof(struct ulp_bde64); 608f1c3b0fcSJames Smart cmdiocbq->iocb.ulpContext = rpi; 609f1c3b0fcSJames Smart cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC; 610f1c3b0fcSJames Smart cmdiocbq->context1 = NULL; 611f1c3b0fcSJames Smart cmdiocbq->context2 = NULL; 612f1c3b0fcSJames Smart 6134cc0e56eSJames Smart cmdiocbq->iocb_cmpl = lpfc_bsg_rport_els_cmp; 6144cc0e56eSJames Smart cmdiocbq->context1 = dd_data; 6154cc0e56eSJames Smart cmdiocbq->context2 = rspiocbq; 6164cc0e56eSJames Smart dd_data->type = TYPE_IOCB; 6174cc0e56eSJames Smart dd_data->context_un.iocb.cmdiocbq = cmdiocbq; 6184cc0e56eSJames Smart dd_data->context_un.iocb.rspiocbq = rspiocbq; 6194cc0e56eSJames Smart dd_data->context_un.iocb.set_job = job; 6204cc0e56eSJames Smart dd_data->context_un.iocb.bmp = NULL;; 6214cc0e56eSJames Smart dd_data->context_un.iocb.ndlp = ndlp; 622f1c3b0fcSJames Smart 6234cc0e56eSJames Smart if (phba->cfg_poll & DISABLE_FCP_RING_INT) { 6244cc0e56eSJames Smart creg_val = readl(phba->HCregaddr); 6254cc0e56eSJames Smart creg_val |= (HC_R0INT_ENA << LPFC_FCP_RING); 6264cc0e56eSJames Smart writel(creg_val, phba->HCregaddr); 6274cc0e56eSJames Smart readl(phba->HCregaddr); /* flush */ 6284cc0e56eSJames Smart } 6294cc0e56eSJames Smart rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq, 0); 630f1c3b0fcSJames Smart lpfc_nlp_put(ndlp); 6314cc0e56eSJames Smart if (rc == IOCB_SUCCESS) 6324cc0e56eSJames Smart return 0; /* done for now */ 6332a9bf3d0SJames Smart else if (rc == IOCB_BUSY) 634*d439d286SJames Smart rc = -EAGAIN; 6352a9bf3d0SJames Smart else 636*d439d286SJames Smart rc = -EIO; 6374cc0e56eSJames Smart 638f1c3b0fcSJames Smart pci_unmap_sg(phba->pcidev, job->request_payload.sg_list, 639f1c3b0fcSJames Smart job->request_payload.sg_cnt, DMA_TO_DEVICE); 640f1c3b0fcSJames Smart pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list, 641f1c3b0fcSJames Smart job->reply_payload.sg_cnt, DMA_FROM_DEVICE); 642f1c3b0fcSJames Smart 6434cc0e56eSJames Smart lpfc_mbuf_free(phba, pbuflist->virt, pbuflist->phys); 644f1c3b0fcSJames Smart 6454cc0e56eSJames Smart lpfc_sli_release_iocbq(phba, cmdiocbq); 646f1c3b0fcSJames Smart 6474cc0e56eSJames Smart free_rspiocbq: 648f1c3b0fcSJames Smart lpfc_sli_release_iocbq(phba, rspiocbq); 649f1c3b0fcSJames Smart 6504cc0e56eSJames Smart free_dd_data: 6514cc0e56eSJames Smart kfree(dd_data); 6524cc0e56eSJames Smart 6534cc0e56eSJames Smart no_dd_data: 654f1c3b0fcSJames Smart /* make error code available to userspace */ 655f1c3b0fcSJames Smart job->reply->result = rc; 6564cc0e56eSJames Smart job->dd_data = NULL; 6574cc0e56eSJames Smart return rc; 658f1c3b0fcSJames Smart } 659f1c3b0fcSJames Smart 6603b5dd52aSJames Smart /** 6613b5dd52aSJames Smart * lpfc_bsg_event_free - frees an allocated event structure 6623b5dd52aSJames Smart * @kref: Pointer to a kref. 6633b5dd52aSJames Smart * 6643b5dd52aSJames Smart * Called from kref_put. Back cast the kref into an event structure address. 6653b5dd52aSJames Smart * Free any events to get, delete associated nodes, free any events to see, 6663b5dd52aSJames Smart * free any data then free the event itself. 6673b5dd52aSJames Smart **/ 668f1c3b0fcSJames Smart static void 6694cc0e56eSJames Smart lpfc_bsg_event_free(struct kref *kref) 670f1c3b0fcSJames Smart { 6714cc0e56eSJames Smart struct lpfc_bsg_event *evt = container_of(kref, struct lpfc_bsg_event, 6724cc0e56eSJames Smart kref); 673f1c3b0fcSJames Smart struct event_data *ed; 674f1c3b0fcSJames Smart 675f1c3b0fcSJames Smart list_del(&evt->node); 676f1c3b0fcSJames Smart 677f1c3b0fcSJames Smart while (!list_empty(&evt->events_to_get)) { 678f1c3b0fcSJames Smart ed = list_entry(evt->events_to_get.next, typeof(*ed), node); 679f1c3b0fcSJames Smart list_del(&ed->node); 680f1c3b0fcSJames Smart kfree(ed->data); 681f1c3b0fcSJames Smart kfree(ed); 682f1c3b0fcSJames Smart } 683f1c3b0fcSJames Smart 684f1c3b0fcSJames Smart while (!list_empty(&evt->events_to_see)) { 685f1c3b0fcSJames Smart ed = list_entry(evt->events_to_see.next, typeof(*ed), node); 686f1c3b0fcSJames Smart list_del(&ed->node); 687f1c3b0fcSJames Smart kfree(ed->data); 688f1c3b0fcSJames Smart kfree(ed); 689f1c3b0fcSJames Smart } 690f1c3b0fcSJames Smart 691f1c3b0fcSJames Smart kfree(evt); 692f1c3b0fcSJames Smart } 693f1c3b0fcSJames Smart 6943b5dd52aSJames Smart /** 6953b5dd52aSJames Smart * lpfc_bsg_event_ref - increments the kref for an event 6963b5dd52aSJames Smart * @evt: Pointer to an event structure. 6973b5dd52aSJames Smart **/ 698f1c3b0fcSJames Smart static inline void 6994cc0e56eSJames Smart lpfc_bsg_event_ref(struct lpfc_bsg_event *evt) 700f1c3b0fcSJames Smart { 7014cc0e56eSJames Smart kref_get(&evt->kref); 702f1c3b0fcSJames Smart } 703f1c3b0fcSJames Smart 7043b5dd52aSJames Smart /** 7053b5dd52aSJames Smart * lpfc_bsg_event_unref - Uses kref_put to free an event structure 7063b5dd52aSJames Smart * @evt: Pointer to an event structure. 7073b5dd52aSJames Smart **/ 708f1c3b0fcSJames Smart static inline void 7094cc0e56eSJames Smart lpfc_bsg_event_unref(struct lpfc_bsg_event *evt) 710f1c3b0fcSJames Smart { 7114cc0e56eSJames Smart kref_put(&evt->kref, lpfc_bsg_event_free); 712f1c3b0fcSJames Smart } 713f1c3b0fcSJames Smart 7143b5dd52aSJames Smart /** 7153b5dd52aSJames Smart * lpfc_bsg_event_new - allocate and initialize a event structure 7163b5dd52aSJames Smart * @ev_mask: Mask of events. 7173b5dd52aSJames Smart * @ev_reg_id: Event reg id. 7183b5dd52aSJames Smart * @ev_req_id: Event request id. 7193b5dd52aSJames Smart **/ 7204cc0e56eSJames Smart static struct lpfc_bsg_event * 7214cc0e56eSJames Smart lpfc_bsg_event_new(uint32_t ev_mask, int ev_reg_id, uint32_t ev_req_id) 7224cc0e56eSJames Smart { 7234cc0e56eSJames Smart struct lpfc_bsg_event *evt = kzalloc(sizeof(*evt), GFP_KERNEL); 724f1c3b0fcSJames Smart 7254cc0e56eSJames Smart if (!evt) 7264cc0e56eSJames Smart return NULL; 7274cc0e56eSJames Smart 7284cc0e56eSJames Smart INIT_LIST_HEAD(&evt->events_to_get); 7294cc0e56eSJames Smart INIT_LIST_HEAD(&evt->events_to_see); 7304cc0e56eSJames Smart evt->type_mask = ev_mask; 7314cc0e56eSJames Smart evt->req_id = ev_req_id; 7324cc0e56eSJames Smart evt->reg_id = ev_reg_id; 7334cc0e56eSJames Smart evt->wait_time_stamp = jiffies; 7344cc0e56eSJames Smart init_waitqueue_head(&evt->wq); 7354cc0e56eSJames Smart kref_init(&evt->kref); 7364cc0e56eSJames Smart return evt; 7374cc0e56eSJames Smart } 7384cc0e56eSJames Smart 7393b5dd52aSJames Smart /** 7403b5dd52aSJames Smart * diag_cmd_data_free - Frees an lpfc dma buffer extension 7413b5dd52aSJames Smart * @phba: Pointer to HBA context object. 7423b5dd52aSJames Smart * @mlist: Pointer to an lpfc dma buffer extension. 7433b5dd52aSJames Smart **/ 7444cc0e56eSJames Smart static int 7453b5dd52aSJames Smart diag_cmd_data_free(struct lpfc_hba *phba, struct lpfc_dmabufext *mlist) 7464cc0e56eSJames Smart { 7474cc0e56eSJames Smart struct lpfc_dmabufext *mlast; 7484cc0e56eSJames Smart struct pci_dev *pcidev; 7494cc0e56eSJames Smart struct list_head head, *curr, *next; 7504cc0e56eSJames Smart 7514cc0e56eSJames Smart if ((!mlist) || (!lpfc_is_link_up(phba) && 7524cc0e56eSJames Smart (phba->link_flag & LS_LOOPBACK_MODE))) { 7534cc0e56eSJames Smart return 0; 7544cc0e56eSJames Smart } 7554cc0e56eSJames Smart 7564cc0e56eSJames Smart pcidev = phba->pcidev; 7574cc0e56eSJames Smart list_add_tail(&head, &mlist->dma.list); 7584cc0e56eSJames Smart 7594cc0e56eSJames Smart list_for_each_safe(curr, next, &head) { 7604cc0e56eSJames Smart mlast = list_entry(curr, struct lpfc_dmabufext , dma.list); 7614cc0e56eSJames Smart if (mlast->dma.virt) 7624cc0e56eSJames Smart dma_free_coherent(&pcidev->dev, 7634cc0e56eSJames Smart mlast->size, 7644cc0e56eSJames Smart mlast->dma.virt, 7654cc0e56eSJames Smart mlast->dma.phys); 7664cc0e56eSJames Smart kfree(mlast); 7674cc0e56eSJames Smart } 7684cc0e56eSJames Smart return 0; 7694cc0e56eSJames Smart } 770f1c3b0fcSJames Smart 771f1c3b0fcSJames Smart /** 772f1c3b0fcSJames Smart * lpfc_bsg_ct_unsol_event - process an unsolicited CT command 773f1c3b0fcSJames Smart * @phba: 774f1c3b0fcSJames Smart * @pring: 775f1c3b0fcSJames Smart * @piocbq: 776f1c3b0fcSJames Smart * 777f1c3b0fcSJames Smart * This function is called when an unsolicited CT command is received. It 7784cc0e56eSJames Smart * forwards the event to any processes registered to receive CT events. 7793b5dd52aSJames Smart **/ 7804fede78fSJames Smart int 781f1c3b0fcSJames Smart lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, 782f1c3b0fcSJames Smart struct lpfc_iocbq *piocbq) 783f1c3b0fcSJames Smart { 784f1c3b0fcSJames Smart uint32_t evt_req_id = 0; 785f1c3b0fcSJames Smart uint32_t cmd; 786f1c3b0fcSJames Smart uint32_t len; 787f1c3b0fcSJames Smart struct lpfc_dmabuf *dmabuf = NULL; 7884cc0e56eSJames Smart struct lpfc_bsg_event *evt; 789f1c3b0fcSJames Smart struct event_data *evt_dat = NULL; 790f1c3b0fcSJames Smart struct lpfc_iocbq *iocbq; 791f1c3b0fcSJames Smart size_t offset = 0; 792f1c3b0fcSJames Smart struct list_head head; 793f1c3b0fcSJames Smart struct ulp_bde64 *bde; 794f1c3b0fcSJames Smart dma_addr_t dma_addr; 795f1c3b0fcSJames Smart int i; 796f1c3b0fcSJames Smart struct lpfc_dmabuf *bdeBuf1 = piocbq->context2; 797f1c3b0fcSJames Smart struct lpfc_dmabuf *bdeBuf2 = piocbq->context3; 798f1c3b0fcSJames Smart struct lpfc_hbq_entry *hbqe; 799f1c3b0fcSJames Smart struct lpfc_sli_ct_request *ct_req; 8004cc0e56eSJames Smart struct fc_bsg_job *job = NULL; 8014fede78fSJames Smart unsigned long flags; 8024cc0e56eSJames Smart int size = 0; 803f1c3b0fcSJames Smart 804f1c3b0fcSJames Smart INIT_LIST_HEAD(&head); 805f1c3b0fcSJames Smart list_add_tail(&head, &piocbq->list); 806f1c3b0fcSJames Smart 807f1c3b0fcSJames Smart if (piocbq->iocb.ulpBdeCount == 0 || 808f1c3b0fcSJames Smart piocbq->iocb.un.cont64[0].tus.f.bdeSize == 0) 809f1c3b0fcSJames Smart goto error_ct_unsol_exit; 810f1c3b0fcSJames Smart 8114cc0e56eSJames Smart if (phba->link_state == LPFC_HBA_ERROR || 8124cc0e56eSJames Smart (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE))) 8134cc0e56eSJames Smart goto error_ct_unsol_exit; 8144cc0e56eSJames Smart 815f1c3b0fcSJames Smart if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) 816f1c3b0fcSJames Smart dmabuf = bdeBuf1; 817f1c3b0fcSJames Smart else { 818f1c3b0fcSJames Smart dma_addr = getPaddr(piocbq->iocb.un.cont64[0].addrHigh, 819f1c3b0fcSJames Smart piocbq->iocb.un.cont64[0].addrLow); 820f1c3b0fcSJames Smart dmabuf = lpfc_sli_ringpostbuf_get(phba, pring, dma_addr); 821f1c3b0fcSJames Smart } 8224cc0e56eSJames Smart if (dmabuf == NULL) 8234cc0e56eSJames Smart goto error_ct_unsol_exit; 824f1c3b0fcSJames Smart ct_req = (struct lpfc_sli_ct_request *)dmabuf->virt; 825f1c3b0fcSJames Smart evt_req_id = ct_req->FsType; 826f1c3b0fcSJames Smart cmd = ct_req->CommandResponse.bits.CmdRsp; 827f1c3b0fcSJames Smart len = ct_req->CommandResponse.bits.Size; 828f1c3b0fcSJames Smart if (!(phba->sli3_options & LPFC_SLI3_HBQ_ENABLED)) 829f1c3b0fcSJames Smart lpfc_sli_ringpostbuf_put(phba, pring, dmabuf); 830f1c3b0fcSJames Smart 8314fede78fSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 832f1c3b0fcSJames Smart list_for_each_entry(evt, &phba->ct_ev_waiters, node) { 8334cc0e56eSJames Smart if (!(evt->type_mask & FC_REG_CT_EVENT) || 8344cc0e56eSJames Smart evt->req_id != evt_req_id) 835f1c3b0fcSJames Smart continue; 836f1c3b0fcSJames Smart 8374cc0e56eSJames Smart lpfc_bsg_event_ref(evt); 8384cc0e56eSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 839f1c3b0fcSJames Smart evt_dat = kzalloc(sizeof(*evt_dat), GFP_KERNEL); 8404cc0e56eSJames Smart if (evt_dat == NULL) { 8414cc0e56eSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 8424cc0e56eSJames Smart lpfc_bsg_event_unref(evt); 843f1c3b0fcSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 844f1c3b0fcSJames Smart "2614 Memory allocation failed for " 845f1c3b0fcSJames Smart "CT event\n"); 846f1c3b0fcSJames Smart break; 847f1c3b0fcSJames Smart } 848f1c3b0fcSJames Smart 849f1c3b0fcSJames Smart if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) { 850f1c3b0fcSJames Smart /* take accumulated byte count from the last iocbq */ 851f1c3b0fcSJames Smart iocbq = list_entry(head.prev, typeof(*iocbq), list); 852f1c3b0fcSJames Smart evt_dat->len = iocbq->iocb.unsli3.rcvsli3.acc_len; 853f1c3b0fcSJames Smart } else { 854f1c3b0fcSJames Smart list_for_each_entry(iocbq, &head, list) { 855f1c3b0fcSJames Smart for (i = 0; i < iocbq->iocb.ulpBdeCount; i++) 856f1c3b0fcSJames Smart evt_dat->len += 857f1c3b0fcSJames Smart iocbq->iocb.un.cont64[i].tus.f.bdeSize; 858f1c3b0fcSJames Smart } 859f1c3b0fcSJames Smart } 860f1c3b0fcSJames Smart 861f1c3b0fcSJames Smart evt_dat->data = kzalloc(evt_dat->len, GFP_KERNEL); 8624cc0e56eSJames Smart if (evt_dat->data == NULL) { 863f1c3b0fcSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 864f1c3b0fcSJames Smart "2615 Memory allocation failed for " 865f1c3b0fcSJames Smart "CT event data, size %d\n", 866f1c3b0fcSJames Smart evt_dat->len); 867f1c3b0fcSJames Smart kfree(evt_dat); 8684fede78fSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 8694cc0e56eSJames Smart lpfc_bsg_event_unref(evt); 8704fede78fSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 871f1c3b0fcSJames Smart goto error_ct_unsol_exit; 872f1c3b0fcSJames Smart } 873f1c3b0fcSJames Smart 874f1c3b0fcSJames Smart list_for_each_entry(iocbq, &head, list) { 8754cc0e56eSJames Smart size = 0; 876f1c3b0fcSJames Smart if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) { 877f1c3b0fcSJames Smart bdeBuf1 = iocbq->context2; 878f1c3b0fcSJames Smart bdeBuf2 = iocbq->context3; 879f1c3b0fcSJames Smart } 880f1c3b0fcSJames Smart for (i = 0; i < iocbq->iocb.ulpBdeCount; i++) { 881f1c3b0fcSJames Smart if (phba->sli3_options & 882f1c3b0fcSJames Smart LPFC_SLI3_HBQ_ENABLED) { 883f1c3b0fcSJames Smart if (i == 0) { 884f1c3b0fcSJames Smart hbqe = (struct lpfc_hbq_entry *) 885f1c3b0fcSJames Smart &iocbq->iocb.un.ulpWord[0]; 886f1c3b0fcSJames Smart size = hbqe->bde.tus.f.bdeSize; 887f1c3b0fcSJames Smart dmabuf = bdeBuf1; 888f1c3b0fcSJames Smart } else if (i == 1) { 889f1c3b0fcSJames Smart hbqe = (struct lpfc_hbq_entry *) 890f1c3b0fcSJames Smart &iocbq->iocb.unsli3. 891f1c3b0fcSJames Smart sli3Words[4]; 892f1c3b0fcSJames Smart size = hbqe->bde.tus.f.bdeSize; 893f1c3b0fcSJames Smart dmabuf = bdeBuf2; 894f1c3b0fcSJames Smart } 895f1c3b0fcSJames Smart if ((offset + size) > evt_dat->len) 896f1c3b0fcSJames Smart size = evt_dat->len - offset; 897f1c3b0fcSJames Smart } else { 898f1c3b0fcSJames Smart size = iocbq->iocb.un.cont64[i]. 899f1c3b0fcSJames Smart tus.f.bdeSize; 900f1c3b0fcSJames Smart bde = &iocbq->iocb.un.cont64[i]; 901f1c3b0fcSJames Smart dma_addr = getPaddr(bde->addrHigh, 902f1c3b0fcSJames Smart bde->addrLow); 903f1c3b0fcSJames Smart dmabuf = lpfc_sli_ringpostbuf_get(phba, 904f1c3b0fcSJames Smart pring, dma_addr); 905f1c3b0fcSJames Smart } 906f1c3b0fcSJames Smart if (!dmabuf) { 907f1c3b0fcSJames Smart lpfc_printf_log(phba, KERN_ERR, 908f1c3b0fcSJames Smart LOG_LIBDFC, "2616 No dmabuf " 909f1c3b0fcSJames Smart "found for iocbq 0x%p\n", 910f1c3b0fcSJames Smart iocbq); 911f1c3b0fcSJames Smart kfree(evt_dat->data); 912f1c3b0fcSJames Smart kfree(evt_dat); 9134fede78fSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, 9144fede78fSJames Smart flags); 9154cc0e56eSJames Smart lpfc_bsg_event_unref(evt); 9164fede78fSJames Smart spin_unlock_irqrestore( 9174fede78fSJames Smart &phba->ct_ev_lock, flags); 918f1c3b0fcSJames Smart goto error_ct_unsol_exit; 919f1c3b0fcSJames Smart } 920f1c3b0fcSJames Smart memcpy((char *)(evt_dat->data) + offset, 921f1c3b0fcSJames Smart dmabuf->virt, size); 922f1c3b0fcSJames Smart offset += size; 923f1c3b0fcSJames Smart if (evt_req_id != SLI_CT_ELX_LOOPBACK && 924f1c3b0fcSJames Smart !(phba->sli3_options & 925f1c3b0fcSJames Smart LPFC_SLI3_HBQ_ENABLED)) { 926f1c3b0fcSJames Smart lpfc_sli_ringpostbuf_put(phba, pring, 927f1c3b0fcSJames Smart dmabuf); 928f1c3b0fcSJames Smart } else { 929f1c3b0fcSJames Smart switch (cmd) { 9304cc0e56eSJames Smart case ELX_LOOPBACK_DATA: 9313b5dd52aSJames Smart diag_cmd_data_free(phba, 9324cc0e56eSJames Smart (struct lpfc_dmabufext *) 9334cc0e56eSJames Smart dmabuf); 9344cc0e56eSJames Smart break; 935f1c3b0fcSJames Smart case ELX_LOOPBACK_XRI_SETUP: 9364cc0e56eSJames Smart if ((phba->sli_rev == 9374cc0e56eSJames Smart LPFC_SLI_REV2) || 9384cc0e56eSJames Smart (phba->sli3_options & 9394cc0e56eSJames Smart LPFC_SLI3_HBQ_ENABLED 9404cc0e56eSJames Smart )) { 9414cc0e56eSJames Smart lpfc_in_buf_free(phba, 9424cc0e56eSJames Smart dmabuf); 9434cc0e56eSJames Smart } else { 944f1c3b0fcSJames Smart lpfc_post_buffer(phba, 945f1c3b0fcSJames Smart pring, 946f1c3b0fcSJames Smart 1); 9474cc0e56eSJames Smart } 948f1c3b0fcSJames Smart break; 949f1c3b0fcSJames Smart default: 950f1c3b0fcSJames Smart if (!(phba->sli3_options & 951f1c3b0fcSJames Smart LPFC_SLI3_HBQ_ENABLED)) 952f1c3b0fcSJames Smart lpfc_post_buffer(phba, 953f1c3b0fcSJames Smart pring, 954f1c3b0fcSJames Smart 1); 955f1c3b0fcSJames Smart break; 956f1c3b0fcSJames Smart } 957f1c3b0fcSJames Smart } 958f1c3b0fcSJames Smart } 959f1c3b0fcSJames Smart } 960f1c3b0fcSJames Smart 9614fede78fSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 962f1c3b0fcSJames Smart if (phba->sli_rev == LPFC_SLI_REV4) { 963f1c3b0fcSJames Smart evt_dat->immed_dat = phba->ctx_idx; 964f1c3b0fcSJames Smart phba->ctx_idx = (phba->ctx_idx + 1) % 64; 965589a52d6SJames Smart /* Provide warning for over-run of the ct_ctx array */ 966589a52d6SJames Smart if (phba->ct_ctx[evt_dat->immed_dat].flags & 967589a52d6SJames Smart UNSOL_VALID) 968589a52d6SJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_ELS, 969589a52d6SJames Smart "2717 CT context array entry " 970589a52d6SJames Smart "[%d] over-run: oxid:x%x, " 971589a52d6SJames Smart "sid:x%x\n", phba->ctx_idx, 972589a52d6SJames Smart phba->ct_ctx[ 973589a52d6SJames Smart evt_dat->immed_dat].oxid, 974589a52d6SJames Smart phba->ct_ctx[ 975589a52d6SJames Smart evt_dat->immed_dat].SID); 976f1c3b0fcSJames Smart phba->ct_ctx[evt_dat->immed_dat].oxid = 977f1c3b0fcSJames Smart piocbq->iocb.ulpContext; 978f1c3b0fcSJames Smart phba->ct_ctx[evt_dat->immed_dat].SID = 979f1c3b0fcSJames Smart piocbq->iocb.un.rcvels.remoteID; 980589a52d6SJames Smart phba->ct_ctx[evt_dat->immed_dat].flags = UNSOL_VALID; 981f1c3b0fcSJames Smart } else 982f1c3b0fcSJames Smart evt_dat->immed_dat = piocbq->iocb.ulpContext; 983f1c3b0fcSJames Smart 984f1c3b0fcSJames Smart evt_dat->type = FC_REG_CT_EVENT; 985f1c3b0fcSJames Smart list_add(&evt_dat->node, &evt->events_to_see); 9864cc0e56eSJames Smart if (evt_req_id == SLI_CT_ELX_LOOPBACK) { 987f1c3b0fcSJames Smart wake_up_interruptible(&evt->wq); 9884cc0e56eSJames Smart lpfc_bsg_event_unref(evt); 989f1c3b0fcSJames Smart break; 990f1c3b0fcSJames Smart } 9914cc0e56eSJames Smart 9924cc0e56eSJames Smart list_move(evt->events_to_see.prev, &evt->events_to_get); 9934cc0e56eSJames Smart lpfc_bsg_event_unref(evt); 9944cc0e56eSJames Smart 9954cc0e56eSJames Smart job = evt->set_job; 9964cc0e56eSJames Smart evt->set_job = NULL; 9974cc0e56eSJames Smart if (job) { 9984cc0e56eSJames Smart job->reply->reply_payload_rcv_len = size; 9994cc0e56eSJames Smart /* make error code available to userspace */ 10004cc0e56eSJames Smart job->reply->result = 0; 10014cc0e56eSJames Smart job->dd_data = NULL; 10024cc0e56eSJames Smart /* complete the job back to userspace */ 10034cc0e56eSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 10044cc0e56eSJames Smart job->job_done(job); 10054cc0e56eSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 10064cc0e56eSJames Smart } 10074cc0e56eSJames Smart } 10084fede78fSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 1009f1c3b0fcSJames Smart 1010f1c3b0fcSJames Smart error_ct_unsol_exit: 1011f1c3b0fcSJames Smart if (!list_empty(&head)) 1012f1c3b0fcSJames Smart list_del(&head); 10134cc0e56eSJames Smart if (evt_req_id == SLI_CT_ELX_LOOPBACK) 10144cc0e56eSJames Smart return 0; 10154fede78fSJames Smart return 1; 1016f1c3b0fcSJames Smart } 1017f1c3b0fcSJames Smart 1018f1c3b0fcSJames Smart /** 10194cc0e56eSJames Smart * lpfc_bsg_hba_set_event - process a SET_EVENT bsg vendor command 1020f1c3b0fcSJames Smart * @job: SET_EVENT fc_bsg_job 10213b5dd52aSJames Smart **/ 1022f1c3b0fcSJames Smart static int 10234cc0e56eSJames Smart lpfc_bsg_hba_set_event(struct fc_bsg_job *job) 1024f1c3b0fcSJames Smart { 1025f1c3b0fcSJames Smart struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; 1026f1c3b0fcSJames Smart struct lpfc_hba *phba = vport->phba; 1027f1c3b0fcSJames Smart struct set_ct_event *event_req; 10284cc0e56eSJames Smart struct lpfc_bsg_event *evt; 1029f1c3b0fcSJames Smart int rc = 0; 10304cc0e56eSJames Smart struct bsg_job_data *dd_data = NULL; 10314cc0e56eSJames Smart uint32_t ev_mask; 10324cc0e56eSJames Smart unsigned long flags; 1033f1c3b0fcSJames Smart 1034f1c3b0fcSJames Smart if (job->request_len < 1035f1c3b0fcSJames Smart sizeof(struct fc_bsg_request) + sizeof(struct set_ct_event)) { 1036f1c3b0fcSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 1037f1c3b0fcSJames Smart "2612 Received SET_CT_EVENT below minimum " 1038f1c3b0fcSJames Smart "size\n"); 10394cc0e56eSJames Smart rc = -EINVAL; 10404cc0e56eSJames Smart goto job_error; 10414cc0e56eSJames Smart } 10424cc0e56eSJames Smart 10434cc0e56eSJames Smart dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL); 10444cc0e56eSJames Smart if (dd_data == NULL) { 10454cc0e56eSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 10464cc0e56eSJames Smart "2734 Failed allocation of dd_data\n"); 10474cc0e56eSJames Smart rc = -ENOMEM; 10484cc0e56eSJames Smart goto job_error; 1049f1c3b0fcSJames Smart } 1050f1c3b0fcSJames Smart 1051f1c3b0fcSJames Smart event_req = (struct set_ct_event *) 1052f1c3b0fcSJames Smart job->request->rqst_data.h_vendor.vendor_cmd; 10534cc0e56eSJames Smart ev_mask = ((uint32_t)(unsigned long)event_req->type_mask & 10544cc0e56eSJames Smart FC_REG_EVENT_MASK); 10554fede78fSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 1056f1c3b0fcSJames Smart list_for_each_entry(evt, &phba->ct_ev_waiters, node) { 1057f1c3b0fcSJames Smart if (evt->reg_id == event_req->ev_reg_id) { 10584cc0e56eSJames Smart lpfc_bsg_event_ref(evt); 1059f1c3b0fcSJames Smart evt->wait_time_stamp = jiffies; 1060f1c3b0fcSJames Smart break; 1061f1c3b0fcSJames Smart } 1062f1c3b0fcSJames Smart } 10634fede78fSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 1064f1c3b0fcSJames Smart 1065f1c3b0fcSJames Smart if (&evt->node == &phba->ct_ev_waiters) { 1066f1c3b0fcSJames Smart /* no event waiting struct yet - first call */ 10674cc0e56eSJames Smart evt = lpfc_bsg_event_new(ev_mask, event_req->ev_reg_id, 1068f1c3b0fcSJames Smart event_req->ev_req_id); 1069f1c3b0fcSJames Smart if (!evt) { 1070f1c3b0fcSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 1071f1c3b0fcSJames Smart "2617 Failed allocation of event " 1072f1c3b0fcSJames Smart "waiter\n"); 10734cc0e56eSJames Smart rc = -ENOMEM; 10744cc0e56eSJames Smart goto job_error; 1075f1c3b0fcSJames Smart } 1076f1c3b0fcSJames Smart 10774fede78fSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 1078f1c3b0fcSJames Smart list_add(&evt->node, &phba->ct_ev_waiters); 10794cc0e56eSJames Smart lpfc_bsg_event_ref(evt); 1080f1c3b0fcSJames Smart evt->wait_time_stamp = jiffies; 10814cc0e56eSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 10824cc0e56eSJames Smart } 1083f1c3b0fcSJames Smart 10844fede78fSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 10854cc0e56eSJames Smart evt->waiting = 1; 10864cc0e56eSJames Smart dd_data->type = TYPE_EVT; 10874cc0e56eSJames Smart dd_data->context_un.evt = evt; 10884cc0e56eSJames Smart evt->set_job = job; /* for unsolicited command */ 10894cc0e56eSJames Smart job->dd_data = dd_data; /* for fc transport timeout callback*/ 10904fede78fSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 10914cc0e56eSJames Smart return 0; /* call job done later */ 1092f1c3b0fcSJames Smart 10934cc0e56eSJames Smart job_error: 10944cc0e56eSJames Smart if (dd_data != NULL) 10954cc0e56eSJames Smart kfree(dd_data); 1096f1c3b0fcSJames Smart 10974cc0e56eSJames Smart job->dd_data = NULL; 10984cc0e56eSJames Smart return rc; 1099f1c3b0fcSJames Smart } 1100f1c3b0fcSJames Smart 1101f1c3b0fcSJames Smart /** 11024cc0e56eSJames Smart * lpfc_bsg_hba_get_event - process a GET_EVENT bsg vendor command 1103f1c3b0fcSJames Smart * @job: GET_EVENT fc_bsg_job 11043b5dd52aSJames Smart **/ 1105f1c3b0fcSJames Smart static int 11064cc0e56eSJames Smart lpfc_bsg_hba_get_event(struct fc_bsg_job *job) 1107f1c3b0fcSJames Smart { 1108f1c3b0fcSJames Smart struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; 1109f1c3b0fcSJames Smart struct lpfc_hba *phba = vport->phba; 1110f1c3b0fcSJames Smart struct get_ct_event *event_req; 1111f1c3b0fcSJames Smart struct get_ct_event_reply *event_reply; 11124cc0e56eSJames Smart struct lpfc_bsg_event *evt; 1113f1c3b0fcSJames Smart struct event_data *evt_dat = NULL; 11144fede78fSJames Smart unsigned long flags; 11154cc0e56eSJames Smart uint32_t rc = 0; 1116f1c3b0fcSJames Smart 1117f1c3b0fcSJames Smart if (job->request_len < 1118f1c3b0fcSJames Smart sizeof(struct fc_bsg_request) + sizeof(struct get_ct_event)) { 1119f1c3b0fcSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 1120f1c3b0fcSJames Smart "2613 Received GET_CT_EVENT request below " 1121f1c3b0fcSJames Smart "minimum size\n"); 11224cc0e56eSJames Smart rc = -EINVAL; 11234cc0e56eSJames Smart goto job_error; 1124f1c3b0fcSJames Smart } 1125f1c3b0fcSJames Smart 1126f1c3b0fcSJames Smart event_req = (struct get_ct_event *) 1127f1c3b0fcSJames Smart job->request->rqst_data.h_vendor.vendor_cmd; 1128f1c3b0fcSJames Smart 1129f1c3b0fcSJames Smart event_reply = (struct get_ct_event_reply *) 1130f1c3b0fcSJames Smart job->reply->reply_data.vendor_reply.vendor_rsp; 11314fede78fSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 1132f1c3b0fcSJames Smart list_for_each_entry(evt, &phba->ct_ev_waiters, node) { 1133f1c3b0fcSJames Smart if (evt->reg_id == event_req->ev_reg_id) { 1134f1c3b0fcSJames Smart if (list_empty(&evt->events_to_get)) 1135f1c3b0fcSJames Smart break; 11364cc0e56eSJames Smart lpfc_bsg_event_ref(evt); 1137f1c3b0fcSJames Smart evt->wait_time_stamp = jiffies; 1138f1c3b0fcSJames Smart evt_dat = list_entry(evt->events_to_get.prev, 1139f1c3b0fcSJames Smart struct event_data, node); 1140f1c3b0fcSJames Smart list_del(&evt_dat->node); 1141f1c3b0fcSJames Smart break; 1142f1c3b0fcSJames Smart } 1143f1c3b0fcSJames Smart } 11444fede78fSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 1145f1c3b0fcSJames Smart 11464cc0e56eSJames Smart /* The app may continue to ask for event data until it gets 11474cc0e56eSJames Smart * an error indicating that there isn't anymore 11484cc0e56eSJames Smart */ 11494cc0e56eSJames Smart if (evt_dat == NULL) { 1150f1c3b0fcSJames Smart job->reply->reply_payload_rcv_len = 0; 1151f1c3b0fcSJames Smart rc = -ENOENT; 11524cc0e56eSJames Smart goto job_error; 1153f1c3b0fcSJames Smart } 1154f1c3b0fcSJames Smart 11554cc0e56eSJames Smart if (evt_dat->len > job->request_payload.payload_len) { 11564cc0e56eSJames Smart evt_dat->len = job->request_payload.payload_len; 1157f1c3b0fcSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 1158f1c3b0fcSJames Smart "2618 Truncated event data at %d " 1159f1c3b0fcSJames Smart "bytes\n", 11604cc0e56eSJames Smart job->request_payload.payload_len); 1161f1c3b0fcSJames Smart } 1162f1c3b0fcSJames Smart 11634cc0e56eSJames Smart event_reply->type = evt_dat->type; 1164f1c3b0fcSJames Smart event_reply->immed_data = evt_dat->immed_dat; 1165f1c3b0fcSJames Smart if (evt_dat->len > 0) 1166f1c3b0fcSJames Smart job->reply->reply_payload_rcv_len = 11674cc0e56eSJames Smart sg_copy_from_buffer(job->request_payload.sg_list, 11684cc0e56eSJames Smart job->request_payload.sg_cnt, 1169f1c3b0fcSJames Smart evt_dat->data, evt_dat->len); 1170f1c3b0fcSJames Smart else 1171f1c3b0fcSJames Smart job->reply->reply_payload_rcv_len = 0; 1172f1c3b0fcSJames Smart 11734cc0e56eSJames Smart if (evt_dat) { 1174f1c3b0fcSJames Smart kfree(evt_dat->data); 1175f1c3b0fcSJames Smart kfree(evt_dat); 11764cc0e56eSJames Smart } 11774cc0e56eSJames Smart 11784fede78fSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 11794cc0e56eSJames Smart lpfc_bsg_event_unref(evt); 11804fede78fSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 11814cc0e56eSJames Smart job->dd_data = NULL; 11824cc0e56eSJames Smart job->reply->result = 0; 1183f1c3b0fcSJames Smart job->job_done(job); 11844cc0e56eSJames Smart return 0; 1185f1c3b0fcSJames Smart 11864cc0e56eSJames Smart job_error: 11874cc0e56eSJames Smart job->dd_data = NULL; 11884cc0e56eSJames Smart job->reply->result = rc; 1189f1c3b0fcSJames Smart return rc; 1190f1c3b0fcSJames Smart } 1191f1c3b0fcSJames Smart 1192f1c3b0fcSJames Smart /** 11933b5dd52aSJames Smart * lpfc_issue_ct_rsp_cmp - lpfc_issue_ct_rsp's completion handler 11943b5dd52aSJames Smart * @phba: Pointer to HBA context object. 11953b5dd52aSJames Smart * @cmdiocbq: Pointer to command iocb. 11963b5dd52aSJames Smart * @rspiocbq: Pointer to response iocb. 11973b5dd52aSJames Smart * 11983b5dd52aSJames Smart * This function is the completion handler for iocbs issued using 11993b5dd52aSJames Smart * lpfc_issue_ct_rsp_cmp function. This function is called by the 12003b5dd52aSJames Smart * ring event handler function without any lock held. This function 12013b5dd52aSJames Smart * can be called from both worker thread context and interrupt 12023b5dd52aSJames Smart * context. This function also can be called from other thread which 12033b5dd52aSJames Smart * cleans up the SLI layer objects. 12043b5dd52aSJames Smart * This function copy the contents of the response iocb to the 12053b5dd52aSJames Smart * response iocb memory object provided by the caller of 12063b5dd52aSJames Smart * lpfc_sli_issue_iocb_wait and then wakes up the thread which 12073b5dd52aSJames Smart * sleeps for the iocb completion. 12083b5dd52aSJames Smart **/ 12093b5dd52aSJames Smart static void 12103b5dd52aSJames Smart lpfc_issue_ct_rsp_cmp(struct lpfc_hba *phba, 12113b5dd52aSJames Smart struct lpfc_iocbq *cmdiocbq, 12123b5dd52aSJames Smart struct lpfc_iocbq *rspiocbq) 12133b5dd52aSJames Smart { 12143b5dd52aSJames Smart struct bsg_job_data *dd_data; 12153b5dd52aSJames Smart struct fc_bsg_job *job; 12163b5dd52aSJames Smart IOCB_t *rsp; 12173b5dd52aSJames Smart struct lpfc_dmabuf *bmp; 12183b5dd52aSJames Smart struct lpfc_nodelist *ndlp; 12193b5dd52aSJames Smart unsigned long flags; 12203b5dd52aSJames Smart int rc = 0; 12213b5dd52aSJames Smart 12223b5dd52aSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 12233b5dd52aSJames Smart dd_data = cmdiocbq->context1; 12243b5dd52aSJames Smart /* normal completion and timeout crossed paths, already done */ 12253b5dd52aSJames Smart if (!dd_data) { 122667221a42SJiri Slaby spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 12273b5dd52aSJames Smart return; 12283b5dd52aSJames Smart } 12293b5dd52aSJames Smart 12303b5dd52aSJames Smart job = dd_data->context_un.iocb.set_job; 12313b5dd52aSJames Smart bmp = dd_data->context_un.iocb.bmp; 12323b5dd52aSJames Smart rsp = &rspiocbq->iocb; 12333b5dd52aSJames Smart ndlp = dd_data->context_un.iocb.ndlp; 12343b5dd52aSJames Smart 12353b5dd52aSJames Smart pci_unmap_sg(phba->pcidev, job->request_payload.sg_list, 12363b5dd52aSJames Smart job->request_payload.sg_cnt, DMA_TO_DEVICE); 12373b5dd52aSJames Smart 12383b5dd52aSJames Smart if (rsp->ulpStatus) { 12393b5dd52aSJames Smart if (rsp->ulpStatus == IOSTAT_LOCAL_REJECT) { 12403b5dd52aSJames Smart switch (rsp->un.ulpWord[4] & 0xff) { 12413b5dd52aSJames Smart case IOERR_SEQUENCE_TIMEOUT: 12423b5dd52aSJames Smart rc = -ETIMEDOUT; 12433b5dd52aSJames Smart break; 12443b5dd52aSJames Smart case IOERR_INVALID_RPI: 12453b5dd52aSJames Smart rc = -EFAULT; 12463b5dd52aSJames Smart break; 12473b5dd52aSJames Smart default: 12483b5dd52aSJames Smart rc = -EACCES; 12493b5dd52aSJames Smart break; 12503b5dd52aSJames Smart } 12513b5dd52aSJames Smart } else 12523b5dd52aSJames Smart rc = -EACCES; 12533b5dd52aSJames Smart } else 12543b5dd52aSJames Smart job->reply->reply_payload_rcv_len = 12553b5dd52aSJames Smart rsp->un.genreq64.bdl.bdeSize; 12563b5dd52aSJames Smart 12573b5dd52aSJames Smart lpfc_mbuf_free(phba, bmp->virt, bmp->phys); 12583b5dd52aSJames Smart lpfc_sli_release_iocbq(phba, cmdiocbq); 12593b5dd52aSJames Smart lpfc_nlp_put(ndlp); 12603b5dd52aSJames Smart kfree(bmp); 12613b5dd52aSJames Smart kfree(dd_data); 12623b5dd52aSJames Smart /* make error code available to userspace */ 12633b5dd52aSJames Smart job->reply->result = rc; 12643b5dd52aSJames Smart job->dd_data = NULL; 12653b5dd52aSJames Smart /* complete the job back to userspace */ 12663b5dd52aSJames Smart job->job_done(job); 12673b5dd52aSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 12683b5dd52aSJames Smart return; 12693b5dd52aSJames Smart } 12703b5dd52aSJames Smart 12713b5dd52aSJames Smart /** 12723b5dd52aSJames Smart * lpfc_issue_ct_rsp - issue a ct response 12733b5dd52aSJames Smart * @phba: Pointer to HBA context object. 12743b5dd52aSJames Smart * @job: Pointer to the job object. 12753b5dd52aSJames Smart * @tag: tag index value into the ports context exchange array. 12763b5dd52aSJames Smart * @bmp: Pointer to a dma buffer descriptor. 12773b5dd52aSJames Smart * @num_entry: Number of enties in the bde. 12783b5dd52aSJames Smart **/ 12793b5dd52aSJames Smart static int 12803b5dd52aSJames Smart lpfc_issue_ct_rsp(struct lpfc_hba *phba, struct fc_bsg_job *job, uint32_t tag, 12813b5dd52aSJames Smart struct lpfc_dmabuf *bmp, int num_entry) 12823b5dd52aSJames Smart { 12833b5dd52aSJames Smart IOCB_t *icmd; 12843b5dd52aSJames Smart struct lpfc_iocbq *ctiocb = NULL; 12853b5dd52aSJames Smart int rc = 0; 12863b5dd52aSJames Smart struct lpfc_nodelist *ndlp = NULL; 12873b5dd52aSJames Smart struct bsg_job_data *dd_data; 12883b5dd52aSJames Smart uint32_t creg_val; 12893b5dd52aSJames Smart 12903b5dd52aSJames Smart /* allocate our bsg tracking structure */ 12913b5dd52aSJames Smart dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL); 12923b5dd52aSJames Smart if (!dd_data) { 12933b5dd52aSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 12943b5dd52aSJames Smart "2736 Failed allocation of dd_data\n"); 12953b5dd52aSJames Smart rc = -ENOMEM; 12963b5dd52aSJames Smart goto no_dd_data; 12973b5dd52aSJames Smart } 12983b5dd52aSJames Smart 12993b5dd52aSJames Smart /* Allocate buffer for command iocb */ 13003b5dd52aSJames Smart ctiocb = lpfc_sli_get_iocbq(phba); 13013b5dd52aSJames Smart if (!ctiocb) { 1302*d439d286SJames Smart rc = -ENOMEM; 13033b5dd52aSJames Smart goto no_ctiocb; 13043b5dd52aSJames Smart } 13053b5dd52aSJames Smart 13063b5dd52aSJames Smart icmd = &ctiocb->iocb; 13073b5dd52aSJames Smart icmd->un.xseq64.bdl.ulpIoTag32 = 0; 13083b5dd52aSJames Smart icmd->un.xseq64.bdl.addrHigh = putPaddrHigh(bmp->phys); 13093b5dd52aSJames Smart icmd->un.xseq64.bdl.addrLow = putPaddrLow(bmp->phys); 13103b5dd52aSJames Smart icmd->un.xseq64.bdl.bdeFlags = BUFF_TYPE_BLP_64; 13113b5dd52aSJames Smart icmd->un.xseq64.bdl.bdeSize = (num_entry * sizeof(struct ulp_bde64)); 13123b5dd52aSJames Smart icmd->un.xseq64.w5.hcsw.Fctl = (LS | LA); 13133b5dd52aSJames Smart icmd->un.xseq64.w5.hcsw.Dfctl = 0; 13143b5dd52aSJames Smart icmd->un.xseq64.w5.hcsw.Rctl = FC_RCTL_DD_SOL_CTL; 13153b5dd52aSJames Smart icmd->un.xseq64.w5.hcsw.Type = FC_TYPE_CT; 13163b5dd52aSJames Smart 13173b5dd52aSJames Smart /* Fill in rest of iocb */ 13183b5dd52aSJames Smart icmd->ulpCommand = CMD_XMIT_SEQUENCE64_CX; 13193b5dd52aSJames Smart icmd->ulpBdeCount = 1; 13203b5dd52aSJames Smart icmd->ulpLe = 1; 13213b5dd52aSJames Smart icmd->ulpClass = CLASS3; 13223b5dd52aSJames Smart if (phba->sli_rev == LPFC_SLI_REV4) { 13233b5dd52aSJames Smart /* Do not issue unsol response if oxid not marked as valid */ 13243b5dd52aSJames Smart if (!(phba->ct_ctx[tag].flags & UNSOL_VALID)) { 13253b5dd52aSJames Smart rc = IOCB_ERROR; 13263b5dd52aSJames Smart goto issue_ct_rsp_exit; 13273b5dd52aSJames Smart } 13283b5dd52aSJames Smart icmd->ulpContext = phba->ct_ctx[tag].oxid; 13293b5dd52aSJames Smart ndlp = lpfc_findnode_did(phba->pport, phba->ct_ctx[tag].SID); 13303b5dd52aSJames Smart if (!ndlp) { 13313b5dd52aSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_ELS, 13323b5dd52aSJames Smart "2721 ndlp null for oxid %x SID %x\n", 13333b5dd52aSJames Smart icmd->ulpContext, 13343b5dd52aSJames Smart phba->ct_ctx[tag].SID); 13353b5dd52aSJames Smart rc = IOCB_ERROR; 13363b5dd52aSJames Smart goto issue_ct_rsp_exit; 13373b5dd52aSJames Smart } 1338589a52d6SJames Smart 1339589a52d6SJames Smart /* Check if the ndlp is active */ 1340589a52d6SJames Smart if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) { 1341589a52d6SJames Smart rc = -IOCB_ERROR; 1342589a52d6SJames Smart goto issue_ct_rsp_exit; 1343589a52d6SJames Smart } 1344589a52d6SJames Smart 1345589a52d6SJames Smart /* get a refernece count so the ndlp doesn't go away while 1346589a52d6SJames Smart * we respond 1347589a52d6SJames Smart */ 1348589a52d6SJames Smart if (!lpfc_nlp_get(ndlp)) { 1349589a52d6SJames Smart rc = -IOCB_ERROR; 1350589a52d6SJames Smart goto issue_ct_rsp_exit; 1351589a52d6SJames Smart } 1352589a52d6SJames Smart 13533b5dd52aSJames Smart icmd->un.ulpWord[3] = ndlp->nlp_rpi; 13543b5dd52aSJames Smart /* The exchange is done, mark the entry as invalid */ 13553b5dd52aSJames Smart phba->ct_ctx[tag].flags &= ~UNSOL_VALID; 13563b5dd52aSJames Smart } else 13573b5dd52aSJames Smart icmd->ulpContext = (ushort) tag; 13583b5dd52aSJames Smart 13593b5dd52aSJames Smart icmd->ulpTimeout = phba->fc_ratov * 2; 13603b5dd52aSJames Smart 13613b5dd52aSJames Smart /* Xmit CT response on exchange <xid> */ 13623b5dd52aSJames Smart lpfc_printf_log(phba, KERN_INFO, LOG_ELS, 13633b5dd52aSJames Smart "2722 Xmit CT response on exchange x%x Data: x%x x%x\n", 13643b5dd52aSJames Smart icmd->ulpContext, icmd->ulpIoTag, phba->link_state); 13653b5dd52aSJames Smart 13663b5dd52aSJames Smart ctiocb->iocb_cmpl = NULL; 13673b5dd52aSJames Smart ctiocb->iocb_flag |= LPFC_IO_LIBDFC; 13683b5dd52aSJames Smart ctiocb->vport = phba->pport; 13693b5dd52aSJames Smart ctiocb->context3 = bmp; 13703b5dd52aSJames Smart 13713b5dd52aSJames Smart ctiocb->iocb_cmpl = lpfc_issue_ct_rsp_cmp; 13723b5dd52aSJames Smart ctiocb->context1 = dd_data; 13733b5dd52aSJames Smart ctiocb->context2 = NULL; 13743b5dd52aSJames Smart dd_data->type = TYPE_IOCB; 13753b5dd52aSJames Smart dd_data->context_un.iocb.cmdiocbq = ctiocb; 13763b5dd52aSJames Smart dd_data->context_un.iocb.rspiocbq = NULL; 13773b5dd52aSJames Smart dd_data->context_un.iocb.set_job = job; 13783b5dd52aSJames Smart dd_data->context_un.iocb.bmp = bmp; 13793b5dd52aSJames Smart dd_data->context_un.iocb.ndlp = ndlp; 13803b5dd52aSJames Smart 13813b5dd52aSJames Smart if (phba->cfg_poll & DISABLE_FCP_RING_INT) { 13823b5dd52aSJames Smart creg_val = readl(phba->HCregaddr); 13833b5dd52aSJames Smart creg_val |= (HC_R0INT_ENA << LPFC_FCP_RING); 13843b5dd52aSJames Smart writel(creg_val, phba->HCregaddr); 13853b5dd52aSJames Smart readl(phba->HCregaddr); /* flush */ 13863b5dd52aSJames Smart } 13873b5dd52aSJames Smart 13883b5dd52aSJames Smart rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, ctiocb, 0); 13893b5dd52aSJames Smart 13903b5dd52aSJames Smart if (rc == IOCB_SUCCESS) 13913b5dd52aSJames Smart return 0; /* done for now */ 13923b5dd52aSJames Smart 13933b5dd52aSJames Smart issue_ct_rsp_exit: 13943b5dd52aSJames Smart lpfc_sli_release_iocbq(phba, ctiocb); 13953b5dd52aSJames Smart no_ctiocb: 13963b5dd52aSJames Smart kfree(dd_data); 13973b5dd52aSJames Smart no_dd_data: 13983b5dd52aSJames Smart return rc; 13993b5dd52aSJames Smart } 14003b5dd52aSJames Smart 14013b5dd52aSJames Smart /** 14023b5dd52aSJames Smart * lpfc_bsg_send_mgmt_rsp - process a SEND_MGMT_RESP bsg vendor command 14033b5dd52aSJames Smart * @job: SEND_MGMT_RESP fc_bsg_job 14043b5dd52aSJames Smart **/ 14053b5dd52aSJames Smart static int 14063b5dd52aSJames Smart lpfc_bsg_send_mgmt_rsp(struct fc_bsg_job *job) 14073b5dd52aSJames Smart { 14083b5dd52aSJames Smart struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; 14093b5dd52aSJames Smart struct lpfc_hba *phba = vport->phba; 14103b5dd52aSJames Smart struct send_mgmt_resp *mgmt_resp = (struct send_mgmt_resp *) 14113b5dd52aSJames Smart job->request->rqst_data.h_vendor.vendor_cmd; 14123b5dd52aSJames Smart struct ulp_bde64 *bpl; 14133b5dd52aSJames Smart struct lpfc_dmabuf *bmp = NULL; 14143b5dd52aSJames Smart struct scatterlist *sgel = NULL; 14153b5dd52aSJames Smart int request_nseg; 14163b5dd52aSJames Smart int numbde; 14173b5dd52aSJames Smart dma_addr_t busaddr; 14183b5dd52aSJames Smart uint32_t tag = mgmt_resp->tag; 14193b5dd52aSJames Smart unsigned long reqbfrcnt = 14203b5dd52aSJames Smart (unsigned long)job->request_payload.payload_len; 14213b5dd52aSJames Smart int rc = 0; 14223b5dd52aSJames Smart 14233b5dd52aSJames Smart /* in case no data is transferred */ 14243b5dd52aSJames Smart job->reply->reply_payload_rcv_len = 0; 14253b5dd52aSJames Smart 14263b5dd52aSJames Smart if (!reqbfrcnt || (reqbfrcnt > (80 * BUF_SZ_4K))) { 14273b5dd52aSJames Smart rc = -ERANGE; 14283b5dd52aSJames Smart goto send_mgmt_rsp_exit; 14293b5dd52aSJames Smart } 14303b5dd52aSJames Smart 14313b5dd52aSJames Smart bmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); 14323b5dd52aSJames Smart if (!bmp) { 14333b5dd52aSJames Smart rc = -ENOMEM; 14343b5dd52aSJames Smart goto send_mgmt_rsp_exit; 14353b5dd52aSJames Smart } 14363b5dd52aSJames Smart 14373b5dd52aSJames Smart bmp->virt = lpfc_mbuf_alloc(phba, 0, &bmp->phys); 14383b5dd52aSJames Smart if (!bmp->virt) { 14393b5dd52aSJames Smart rc = -ENOMEM; 14403b5dd52aSJames Smart goto send_mgmt_rsp_free_bmp; 14413b5dd52aSJames Smart } 14423b5dd52aSJames Smart 14433b5dd52aSJames Smart INIT_LIST_HEAD(&bmp->list); 14443b5dd52aSJames Smart bpl = (struct ulp_bde64 *) bmp->virt; 14453b5dd52aSJames Smart request_nseg = pci_map_sg(phba->pcidev, job->request_payload.sg_list, 14463b5dd52aSJames Smart job->request_payload.sg_cnt, DMA_TO_DEVICE); 14473b5dd52aSJames Smart for_each_sg(job->request_payload.sg_list, sgel, request_nseg, numbde) { 14483b5dd52aSJames Smart busaddr = sg_dma_address(sgel); 14493b5dd52aSJames Smart bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64; 14503b5dd52aSJames Smart bpl->tus.f.bdeSize = sg_dma_len(sgel); 14513b5dd52aSJames Smart bpl->tus.w = cpu_to_le32(bpl->tus.w); 14523b5dd52aSJames Smart bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr)); 14533b5dd52aSJames Smart bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr)); 14543b5dd52aSJames Smart bpl++; 14553b5dd52aSJames Smart } 14563b5dd52aSJames Smart 14573b5dd52aSJames Smart rc = lpfc_issue_ct_rsp(phba, job, tag, bmp, request_nseg); 14583b5dd52aSJames Smart 14593b5dd52aSJames Smart if (rc == IOCB_SUCCESS) 14603b5dd52aSJames Smart return 0; /* done for now */ 14613b5dd52aSJames Smart 14623b5dd52aSJames Smart /* TBD need to handle a timeout */ 14633b5dd52aSJames Smart pci_unmap_sg(phba->pcidev, job->request_payload.sg_list, 14643b5dd52aSJames Smart job->request_payload.sg_cnt, DMA_TO_DEVICE); 14653b5dd52aSJames Smart rc = -EACCES; 14663b5dd52aSJames Smart lpfc_mbuf_free(phba, bmp->virt, bmp->phys); 14673b5dd52aSJames Smart 14683b5dd52aSJames Smart send_mgmt_rsp_free_bmp: 14693b5dd52aSJames Smart kfree(bmp); 14703b5dd52aSJames Smart send_mgmt_rsp_exit: 14713b5dd52aSJames Smart /* make error code available to userspace */ 14723b5dd52aSJames Smart job->reply->result = rc; 14733b5dd52aSJames Smart job->dd_data = NULL; 14743b5dd52aSJames Smart return rc; 14753b5dd52aSJames Smart } 14763b5dd52aSJames Smart 14773b5dd52aSJames Smart /** 14783b5dd52aSJames Smart * lpfc_bsg_diag_mode - process a LPFC_BSG_VENDOR_DIAG_MODE bsg vendor command 14793b5dd52aSJames Smart * @job: LPFC_BSG_VENDOR_DIAG_MODE 14803b5dd52aSJames Smart * 14813b5dd52aSJames Smart * This function is responsible for placing a port into diagnostic loopback 14823b5dd52aSJames Smart * mode in order to perform a diagnostic loopback test. 14833b5dd52aSJames Smart * All new scsi requests are blocked, a small delay is used to allow the 14843b5dd52aSJames Smart * scsi requests to complete then the link is brought down. If the link is 14853b5dd52aSJames Smart * is placed in loopback mode then scsi requests are again allowed 14863b5dd52aSJames Smart * so the scsi mid-layer doesn't give up on the port. 14873b5dd52aSJames Smart * All of this is done in-line. 14883b5dd52aSJames Smart */ 14893b5dd52aSJames Smart static int 14903b5dd52aSJames Smart lpfc_bsg_diag_mode(struct fc_bsg_job *job) 14913b5dd52aSJames Smart { 14923b5dd52aSJames Smart struct Scsi_Host *shost = job->shost; 14933b5dd52aSJames Smart struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; 14943b5dd52aSJames Smart struct lpfc_hba *phba = vport->phba; 14953b5dd52aSJames Smart struct diag_mode_set *loopback_mode; 14963b5dd52aSJames Smart struct lpfc_sli *psli = &phba->sli; 14973b5dd52aSJames Smart struct lpfc_sli_ring *pring = &psli->ring[LPFC_FCP_RING]; 14983b5dd52aSJames Smart uint32_t link_flags; 14993b5dd52aSJames Smart uint32_t timeout; 15003b5dd52aSJames Smart struct lpfc_vport **vports; 15013b5dd52aSJames Smart LPFC_MBOXQ_t *pmboxq; 15023b5dd52aSJames Smart int mbxstatus; 15033b5dd52aSJames Smart int i = 0; 15043b5dd52aSJames Smart int rc = 0; 15053b5dd52aSJames Smart 15063b5dd52aSJames Smart /* no data to return just the return code */ 15073b5dd52aSJames Smart job->reply->reply_payload_rcv_len = 0; 15083b5dd52aSJames Smart 15093b5dd52aSJames Smart if (job->request_len < 15103b5dd52aSJames Smart sizeof(struct fc_bsg_request) + sizeof(struct diag_mode_set)) { 15113b5dd52aSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 15123b5dd52aSJames Smart "2738 Received DIAG MODE request below minimum " 15133b5dd52aSJames Smart "size\n"); 15143b5dd52aSJames Smart rc = -EINVAL; 15153b5dd52aSJames Smart goto job_error; 15163b5dd52aSJames Smart } 15173b5dd52aSJames Smart 15183b5dd52aSJames Smart loopback_mode = (struct diag_mode_set *) 15193b5dd52aSJames Smart job->request->rqst_data.h_vendor.vendor_cmd; 15203b5dd52aSJames Smart link_flags = loopback_mode->type; 15213b5dd52aSJames Smart timeout = loopback_mode->timeout; 15223b5dd52aSJames Smart 15233b5dd52aSJames Smart if ((phba->link_state == LPFC_HBA_ERROR) || 15243b5dd52aSJames Smart (psli->sli_flag & LPFC_BLOCK_MGMT_IO) || 15253b5dd52aSJames Smart (!(psli->sli_flag & LPFC_SLI_ACTIVE))) { 15263b5dd52aSJames Smart rc = -EACCES; 15273b5dd52aSJames Smart goto job_error; 15283b5dd52aSJames Smart } 15293b5dd52aSJames Smart 15303b5dd52aSJames Smart pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); 15313b5dd52aSJames Smart if (!pmboxq) { 15323b5dd52aSJames Smart rc = -ENOMEM; 15333b5dd52aSJames Smart goto job_error; 15343b5dd52aSJames Smart } 15353b5dd52aSJames Smart 15363b5dd52aSJames Smart vports = lpfc_create_vport_work_array(phba); 15373b5dd52aSJames Smart if (vports) { 15383b5dd52aSJames Smart for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) { 15393b5dd52aSJames Smart shost = lpfc_shost_from_vport(vports[i]); 15403b5dd52aSJames Smart scsi_block_requests(shost); 15413b5dd52aSJames Smart } 15423b5dd52aSJames Smart 15433b5dd52aSJames Smart lpfc_destroy_vport_work_array(phba, vports); 15443b5dd52aSJames Smart } else { 15453b5dd52aSJames Smart shost = lpfc_shost_from_vport(phba->pport); 15463b5dd52aSJames Smart scsi_block_requests(shost); 15473b5dd52aSJames Smart } 15483b5dd52aSJames Smart 15493b5dd52aSJames Smart while (pring->txcmplq_cnt) { 15503b5dd52aSJames Smart if (i++ > 500) /* wait up to 5 seconds */ 15513b5dd52aSJames Smart break; 15523b5dd52aSJames Smart 15533b5dd52aSJames Smart msleep(10); 15543b5dd52aSJames Smart } 15553b5dd52aSJames Smart 15563b5dd52aSJames Smart memset((void *)pmboxq, 0, sizeof(LPFC_MBOXQ_t)); 15573b5dd52aSJames Smart pmboxq->u.mb.mbxCommand = MBX_DOWN_LINK; 15583b5dd52aSJames Smart pmboxq->u.mb.mbxOwner = OWN_HOST; 15593b5dd52aSJames Smart 15603b5dd52aSJames Smart mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq, LPFC_MBOX_TMO); 15613b5dd52aSJames Smart 15623b5dd52aSJames Smart if ((mbxstatus == MBX_SUCCESS) && (pmboxq->u.mb.mbxStatus == 0)) { 15633b5dd52aSJames Smart /* wait for link down before proceeding */ 15643b5dd52aSJames Smart i = 0; 15653b5dd52aSJames Smart while (phba->link_state != LPFC_LINK_DOWN) { 15663b5dd52aSJames Smart if (i++ > timeout) { 15673b5dd52aSJames Smart rc = -ETIMEDOUT; 15683b5dd52aSJames Smart goto loopback_mode_exit; 15693b5dd52aSJames Smart } 15703b5dd52aSJames Smart 15713b5dd52aSJames Smart msleep(10); 15723b5dd52aSJames Smart } 15733b5dd52aSJames Smart 15743b5dd52aSJames Smart memset((void *)pmboxq, 0, sizeof(LPFC_MBOXQ_t)); 15753b5dd52aSJames Smart if (link_flags == INTERNAL_LOOP_BACK) 15763b5dd52aSJames Smart pmboxq->u.mb.un.varInitLnk.link_flags = FLAGS_LOCAL_LB; 15773b5dd52aSJames Smart else 15783b5dd52aSJames Smart pmboxq->u.mb.un.varInitLnk.link_flags = 15793b5dd52aSJames Smart FLAGS_TOPOLOGY_MODE_LOOP; 15803b5dd52aSJames Smart 15813b5dd52aSJames Smart pmboxq->u.mb.mbxCommand = MBX_INIT_LINK; 15823b5dd52aSJames Smart pmboxq->u.mb.mbxOwner = OWN_HOST; 15833b5dd52aSJames Smart 15843b5dd52aSJames Smart mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq, 15853b5dd52aSJames Smart LPFC_MBOX_TMO); 15863b5dd52aSJames Smart 15873b5dd52aSJames Smart if ((mbxstatus != MBX_SUCCESS) || (pmboxq->u.mb.mbxStatus)) 15883b5dd52aSJames Smart rc = -ENODEV; 15893b5dd52aSJames Smart else { 15903b5dd52aSJames Smart phba->link_flag |= LS_LOOPBACK_MODE; 15913b5dd52aSJames Smart /* wait for the link attention interrupt */ 15923b5dd52aSJames Smart msleep(100); 15933b5dd52aSJames Smart 15943b5dd52aSJames Smart i = 0; 15953b5dd52aSJames Smart while (phba->link_state != LPFC_HBA_READY) { 15963b5dd52aSJames Smart if (i++ > timeout) { 15973b5dd52aSJames Smart rc = -ETIMEDOUT; 15983b5dd52aSJames Smart break; 15993b5dd52aSJames Smart } 16003b5dd52aSJames Smart 16013b5dd52aSJames Smart msleep(10); 16023b5dd52aSJames Smart } 16033b5dd52aSJames Smart } 16043b5dd52aSJames Smart 16053b5dd52aSJames Smart } else 16063b5dd52aSJames Smart rc = -ENODEV; 16073b5dd52aSJames Smart 16083b5dd52aSJames Smart loopback_mode_exit: 16093b5dd52aSJames Smart vports = lpfc_create_vport_work_array(phba); 16103b5dd52aSJames Smart if (vports) { 16113b5dd52aSJames Smart for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) { 16123b5dd52aSJames Smart shost = lpfc_shost_from_vport(vports[i]); 16133b5dd52aSJames Smart scsi_unblock_requests(shost); 16143b5dd52aSJames Smart } 16153b5dd52aSJames Smart lpfc_destroy_vport_work_array(phba, vports); 16163b5dd52aSJames Smart } else { 16173b5dd52aSJames Smart shost = lpfc_shost_from_vport(phba->pport); 16183b5dd52aSJames Smart scsi_unblock_requests(shost); 16193b5dd52aSJames Smart } 16203b5dd52aSJames Smart 16213b5dd52aSJames Smart /* 16223b5dd52aSJames Smart * Let SLI layer release mboxq if mbox command completed after timeout. 16233b5dd52aSJames Smart */ 16243b5dd52aSJames Smart if (mbxstatus != MBX_TIMEOUT) 16253b5dd52aSJames Smart mempool_free(pmboxq, phba->mbox_mem_pool); 16263b5dd52aSJames Smart 16273b5dd52aSJames Smart job_error: 16283b5dd52aSJames Smart /* make error code available to userspace */ 16293b5dd52aSJames Smart job->reply->result = rc; 16303b5dd52aSJames Smart /* complete the job back to userspace if no error */ 16313b5dd52aSJames Smart if (rc == 0) 16323b5dd52aSJames Smart job->job_done(job); 16333b5dd52aSJames Smart return rc; 16343b5dd52aSJames Smart } 16353b5dd52aSJames Smart 16363b5dd52aSJames Smart /** 16373b5dd52aSJames Smart * lpfcdiag_loop_self_reg - obtains a remote port login id 16383b5dd52aSJames Smart * @phba: Pointer to HBA context object 16393b5dd52aSJames Smart * @rpi: Pointer to a remote port login id 16403b5dd52aSJames Smart * 16413b5dd52aSJames Smart * This function obtains a remote port login id so the diag loopback test 16423b5dd52aSJames Smart * can send and receive its own unsolicited CT command. 16433b5dd52aSJames Smart **/ 16443b5dd52aSJames Smart static int lpfcdiag_loop_self_reg(struct lpfc_hba *phba, uint16_t * rpi) 16453b5dd52aSJames Smart { 16463b5dd52aSJames Smart LPFC_MBOXQ_t *mbox; 16473b5dd52aSJames Smart struct lpfc_dmabuf *dmabuff; 16483b5dd52aSJames Smart int status; 16493b5dd52aSJames Smart 16503b5dd52aSJames Smart mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); 16513b5dd52aSJames Smart if (!mbox) 1652*d439d286SJames Smart return -ENOMEM; 16533b5dd52aSJames Smart 16543b5dd52aSJames Smart status = lpfc_reg_rpi(phba, 0, phba->pport->fc_myDID, 16553b5dd52aSJames Smart (uint8_t *)&phba->pport->fc_sparam, mbox, 0); 16563b5dd52aSJames Smart if (status) { 16573b5dd52aSJames Smart mempool_free(mbox, phba->mbox_mem_pool); 1658*d439d286SJames Smart return -ENOMEM; 16593b5dd52aSJames Smart } 16603b5dd52aSJames Smart 16613b5dd52aSJames Smart dmabuff = (struct lpfc_dmabuf *) mbox->context1; 16623b5dd52aSJames Smart mbox->context1 = NULL; 1663*d439d286SJames Smart mbox->context2 = NULL; 16643b5dd52aSJames Smart status = lpfc_sli_issue_mbox_wait(phba, mbox, LPFC_MBOX_TMO); 16653b5dd52aSJames Smart 16663b5dd52aSJames Smart if ((status != MBX_SUCCESS) || (mbox->u.mb.mbxStatus)) { 16673b5dd52aSJames Smart lpfc_mbuf_free(phba, dmabuff->virt, dmabuff->phys); 16683b5dd52aSJames Smart kfree(dmabuff); 16693b5dd52aSJames Smart if (status != MBX_TIMEOUT) 16703b5dd52aSJames Smart mempool_free(mbox, phba->mbox_mem_pool); 1671*d439d286SJames Smart return -ENODEV; 16723b5dd52aSJames Smart } 16733b5dd52aSJames Smart 16743b5dd52aSJames Smart *rpi = mbox->u.mb.un.varWords[0]; 16753b5dd52aSJames Smart 16763b5dd52aSJames Smart lpfc_mbuf_free(phba, dmabuff->virt, dmabuff->phys); 16773b5dd52aSJames Smart kfree(dmabuff); 16783b5dd52aSJames Smart mempool_free(mbox, phba->mbox_mem_pool); 16793b5dd52aSJames Smart return 0; 16803b5dd52aSJames Smart } 16813b5dd52aSJames Smart 16823b5dd52aSJames Smart /** 16833b5dd52aSJames Smart * lpfcdiag_loop_self_unreg - unregs from the rpi 16843b5dd52aSJames Smart * @phba: Pointer to HBA context object 16853b5dd52aSJames Smart * @rpi: Remote port login id 16863b5dd52aSJames Smart * 16873b5dd52aSJames Smart * This function unregisters the rpi obtained in lpfcdiag_loop_self_reg 16883b5dd52aSJames Smart **/ 16893b5dd52aSJames Smart static int lpfcdiag_loop_self_unreg(struct lpfc_hba *phba, uint16_t rpi) 16903b5dd52aSJames Smart { 16913b5dd52aSJames Smart LPFC_MBOXQ_t *mbox; 16923b5dd52aSJames Smart int status; 16933b5dd52aSJames Smart 16943b5dd52aSJames Smart /* Allocate mboxq structure */ 16953b5dd52aSJames Smart mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); 16963b5dd52aSJames Smart if (mbox == NULL) 1697*d439d286SJames Smart return -ENOMEM; 16983b5dd52aSJames Smart 16993b5dd52aSJames Smart lpfc_unreg_login(phba, 0, rpi, mbox); 17003b5dd52aSJames Smart status = lpfc_sli_issue_mbox_wait(phba, mbox, LPFC_MBOX_TMO); 17013b5dd52aSJames Smart 17023b5dd52aSJames Smart if ((status != MBX_SUCCESS) || (mbox->u.mb.mbxStatus)) { 17033b5dd52aSJames Smart if (status != MBX_TIMEOUT) 17043b5dd52aSJames Smart mempool_free(mbox, phba->mbox_mem_pool); 1705*d439d286SJames Smart return -EIO; 17063b5dd52aSJames Smart } 17073b5dd52aSJames Smart 17083b5dd52aSJames Smart mempool_free(mbox, phba->mbox_mem_pool); 17093b5dd52aSJames Smart return 0; 17103b5dd52aSJames Smart } 17113b5dd52aSJames Smart 17123b5dd52aSJames Smart /** 17133b5dd52aSJames Smart * lpfcdiag_loop_get_xri - obtains the transmit and receive ids 17143b5dd52aSJames Smart * @phba: Pointer to HBA context object 17153b5dd52aSJames Smart * @rpi: Remote port login id 17163b5dd52aSJames Smart * @txxri: Pointer to transmit exchange id 17173b5dd52aSJames Smart * @rxxri: Pointer to response exchabge id 17183b5dd52aSJames Smart * 17193b5dd52aSJames Smart * This function obtains the transmit and receive ids required to send 17203b5dd52aSJames Smart * an unsolicited ct command with a payload. A special lpfc FsType and CmdRsp 17213b5dd52aSJames Smart * flags are used to the unsolicted response handler is able to process 17223b5dd52aSJames Smart * the ct command sent on the same port. 17233b5dd52aSJames Smart **/ 17243b5dd52aSJames Smart static int lpfcdiag_loop_get_xri(struct lpfc_hba *phba, uint16_t rpi, 17253b5dd52aSJames Smart uint16_t *txxri, uint16_t * rxxri) 17263b5dd52aSJames Smart { 17273b5dd52aSJames Smart struct lpfc_bsg_event *evt; 17283b5dd52aSJames Smart struct lpfc_iocbq *cmdiocbq, *rspiocbq; 17293b5dd52aSJames Smart IOCB_t *cmd, *rsp; 17303b5dd52aSJames Smart struct lpfc_dmabuf *dmabuf; 17313b5dd52aSJames Smart struct ulp_bde64 *bpl = NULL; 17323b5dd52aSJames Smart struct lpfc_sli_ct_request *ctreq = NULL; 17333b5dd52aSJames Smart int ret_val = 0; 1734*d439d286SJames Smart int time_left; 1735*d439d286SJames Smart int iocb_stat; 17363b5dd52aSJames Smart unsigned long flags; 17373b5dd52aSJames Smart 17383b5dd52aSJames Smart *txxri = 0; 17393b5dd52aSJames Smart *rxxri = 0; 17403b5dd52aSJames Smart evt = lpfc_bsg_event_new(FC_REG_CT_EVENT, current->pid, 17413b5dd52aSJames Smart SLI_CT_ELX_LOOPBACK); 17423b5dd52aSJames Smart if (!evt) 1743*d439d286SJames Smart return -ENOMEM; 17443b5dd52aSJames Smart 17453b5dd52aSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 17463b5dd52aSJames Smart list_add(&evt->node, &phba->ct_ev_waiters); 17473b5dd52aSJames Smart lpfc_bsg_event_ref(evt); 17483b5dd52aSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 17493b5dd52aSJames Smart 17503b5dd52aSJames Smart cmdiocbq = lpfc_sli_get_iocbq(phba); 17513b5dd52aSJames Smart rspiocbq = lpfc_sli_get_iocbq(phba); 17523b5dd52aSJames Smart 17533b5dd52aSJames Smart dmabuf = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); 17543b5dd52aSJames Smart if (dmabuf) { 17553b5dd52aSJames Smart dmabuf->virt = lpfc_mbuf_alloc(phba, 0, &dmabuf->phys); 1756c7495937SJames Smart if (dmabuf->virt) { 17573b5dd52aSJames Smart INIT_LIST_HEAD(&dmabuf->list); 17583b5dd52aSJames Smart bpl = (struct ulp_bde64 *) dmabuf->virt; 17593b5dd52aSJames Smart memset(bpl, 0, sizeof(*bpl)); 17603b5dd52aSJames Smart ctreq = (struct lpfc_sli_ct_request *)(bpl + 1); 17613b5dd52aSJames Smart bpl->addrHigh = 1762c7495937SJames Smart le32_to_cpu(putPaddrHigh(dmabuf->phys + 1763c7495937SJames Smart sizeof(*bpl))); 17643b5dd52aSJames Smart bpl->addrLow = 1765c7495937SJames Smart le32_to_cpu(putPaddrLow(dmabuf->phys + 1766c7495937SJames Smart sizeof(*bpl))); 17673b5dd52aSJames Smart bpl->tus.f.bdeFlags = 0; 17683b5dd52aSJames Smart bpl->tus.f.bdeSize = ELX_LOOPBACK_HEADER_SZ; 17693b5dd52aSJames Smart bpl->tus.w = le32_to_cpu(bpl->tus.w); 17703b5dd52aSJames Smart } 1771c7495937SJames Smart } 17723b5dd52aSJames Smart 17733b5dd52aSJames Smart if (cmdiocbq == NULL || rspiocbq == NULL || 1774c7495937SJames Smart dmabuf == NULL || bpl == NULL || ctreq == NULL || 1775c7495937SJames Smart dmabuf->virt == NULL) { 1776*d439d286SJames Smart ret_val = -ENOMEM; 17773b5dd52aSJames Smart goto err_get_xri_exit; 17783b5dd52aSJames Smart } 17793b5dd52aSJames Smart 17803b5dd52aSJames Smart cmd = &cmdiocbq->iocb; 17813b5dd52aSJames Smart rsp = &rspiocbq->iocb; 17823b5dd52aSJames Smart 17833b5dd52aSJames Smart memset(ctreq, 0, ELX_LOOPBACK_HEADER_SZ); 17843b5dd52aSJames Smart 17853b5dd52aSJames Smart ctreq->RevisionId.bits.Revision = SLI_CT_REVISION; 17863b5dd52aSJames Smart ctreq->RevisionId.bits.InId = 0; 17873b5dd52aSJames Smart ctreq->FsType = SLI_CT_ELX_LOOPBACK; 17883b5dd52aSJames Smart ctreq->FsSubType = 0; 17893b5dd52aSJames Smart ctreq->CommandResponse.bits.CmdRsp = ELX_LOOPBACK_XRI_SETUP; 17903b5dd52aSJames Smart ctreq->CommandResponse.bits.Size = 0; 17913b5dd52aSJames Smart 17923b5dd52aSJames Smart 17933b5dd52aSJames Smart cmd->un.xseq64.bdl.addrHigh = putPaddrHigh(dmabuf->phys); 17943b5dd52aSJames Smart cmd->un.xseq64.bdl.addrLow = putPaddrLow(dmabuf->phys); 17953b5dd52aSJames Smart cmd->un.xseq64.bdl.bdeFlags = BUFF_TYPE_BLP_64; 17963b5dd52aSJames Smart cmd->un.xseq64.bdl.bdeSize = sizeof(*bpl); 17973b5dd52aSJames Smart 17983b5dd52aSJames Smart cmd->un.xseq64.w5.hcsw.Fctl = LA; 17993b5dd52aSJames Smart cmd->un.xseq64.w5.hcsw.Dfctl = 0; 18003b5dd52aSJames Smart cmd->un.xseq64.w5.hcsw.Rctl = FC_RCTL_DD_UNSOL_CTL; 18013b5dd52aSJames Smart cmd->un.xseq64.w5.hcsw.Type = FC_TYPE_CT; 18023b5dd52aSJames Smart 18033b5dd52aSJames Smart cmd->ulpCommand = CMD_XMIT_SEQUENCE64_CR; 18043b5dd52aSJames Smart cmd->ulpBdeCount = 1; 18053b5dd52aSJames Smart cmd->ulpLe = 1; 18063b5dd52aSJames Smart cmd->ulpClass = CLASS3; 18073b5dd52aSJames Smart cmd->ulpContext = rpi; 18083b5dd52aSJames Smart 18093b5dd52aSJames Smart cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC; 18103b5dd52aSJames Smart cmdiocbq->vport = phba->pport; 18113b5dd52aSJames Smart 1812*d439d286SJames Smart iocb_stat = lpfc_sli_issue_iocb_wait(phba, LPFC_ELS_RING, cmdiocbq, 18133b5dd52aSJames Smart rspiocbq, 18143b5dd52aSJames Smart (phba->fc_ratov * 2) 18153b5dd52aSJames Smart + LPFC_DRVR_TIMEOUT); 1816*d439d286SJames Smart if (iocb_stat) { 1817*d439d286SJames Smart ret_val = -EIO; 18183b5dd52aSJames Smart goto err_get_xri_exit; 1819*d439d286SJames Smart } 18203b5dd52aSJames Smart *txxri = rsp->ulpContext; 18213b5dd52aSJames Smart 18223b5dd52aSJames Smart evt->waiting = 1; 18233b5dd52aSJames Smart evt->wait_time_stamp = jiffies; 1824*d439d286SJames Smart time_left = wait_event_interruptible_timeout( 18253b5dd52aSJames Smart evt->wq, !list_empty(&evt->events_to_see), 18263b5dd52aSJames Smart ((phba->fc_ratov * 2) + LPFC_DRVR_TIMEOUT) * HZ); 18273b5dd52aSJames Smart if (list_empty(&evt->events_to_see)) 1828*d439d286SJames Smart ret_val = (time_left) ? -EINTR : -ETIMEDOUT; 18293b5dd52aSJames Smart else { 18303b5dd52aSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 18313b5dd52aSJames Smart list_move(evt->events_to_see.prev, &evt->events_to_get); 18323b5dd52aSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 18333b5dd52aSJames Smart *rxxri = (list_entry(evt->events_to_get.prev, 18343b5dd52aSJames Smart typeof(struct event_data), 18353b5dd52aSJames Smart node))->immed_dat; 18363b5dd52aSJames Smart } 18373b5dd52aSJames Smart evt->waiting = 0; 18383b5dd52aSJames Smart 18393b5dd52aSJames Smart err_get_xri_exit: 18403b5dd52aSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 18413b5dd52aSJames Smart lpfc_bsg_event_unref(evt); /* release ref */ 18423b5dd52aSJames Smart lpfc_bsg_event_unref(evt); /* delete */ 18433b5dd52aSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 18443b5dd52aSJames Smart 18453b5dd52aSJames Smart if (dmabuf) { 18463b5dd52aSJames Smart if (dmabuf->virt) 18473b5dd52aSJames Smart lpfc_mbuf_free(phba, dmabuf->virt, dmabuf->phys); 18483b5dd52aSJames Smart kfree(dmabuf); 18493b5dd52aSJames Smart } 18503b5dd52aSJames Smart 1851*d439d286SJames Smart if (cmdiocbq && (iocb_stat != IOCB_TIMEDOUT)) 18523b5dd52aSJames Smart lpfc_sli_release_iocbq(phba, cmdiocbq); 18533b5dd52aSJames Smart if (rspiocbq) 18543b5dd52aSJames Smart lpfc_sli_release_iocbq(phba, rspiocbq); 18553b5dd52aSJames Smart return ret_val; 18563b5dd52aSJames Smart } 18573b5dd52aSJames Smart 18583b5dd52aSJames Smart /** 18593b5dd52aSJames Smart * diag_cmd_data_alloc - fills in a bde struct with dma buffers 18603b5dd52aSJames Smart * @phba: Pointer to HBA context object 18613b5dd52aSJames Smart * @bpl: Pointer to 64 bit bde structure 18623b5dd52aSJames Smart * @size: Number of bytes to process 18633b5dd52aSJames Smart * @nocopydata: Flag to copy user data into the allocated buffer 18643b5dd52aSJames Smart * 18653b5dd52aSJames Smart * This function allocates page size buffers and populates an lpfc_dmabufext. 18663b5dd52aSJames Smart * If allowed the user data pointed to with indataptr is copied into the kernel 18673b5dd52aSJames Smart * memory. The chained list of page size buffers is returned. 18683b5dd52aSJames Smart **/ 18693b5dd52aSJames Smart static struct lpfc_dmabufext * 18703b5dd52aSJames Smart diag_cmd_data_alloc(struct lpfc_hba *phba, 18713b5dd52aSJames Smart struct ulp_bde64 *bpl, uint32_t size, 18723b5dd52aSJames Smart int nocopydata) 18733b5dd52aSJames Smart { 18743b5dd52aSJames Smart struct lpfc_dmabufext *mlist = NULL; 18753b5dd52aSJames Smart struct lpfc_dmabufext *dmp; 18763b5dd52aSJames Smart int cnt, offset = 0, i = 0; 18773b5dd52aSJames Smart struct pci_dev *pcidev; 18783b5dd52aSJames Smart 18793b5dd52aSJames Smart pcidev = phba->pcidev; 18803b5dd52aSJames Smart 18813b5dd52aSJames Smart while (size) { 18823b5dd52aSJames Smart /* We get chunks of 4K */ 18833b5dd52aSJames Smart if (size > BUF_SZ_4K) 18843b5dd52aSJames Smart cnt = BUF_SZ_4K; 18853b5dd52aSJames Smart else 18863b5dd52aSJames Smart cnt = size; 18873b5dd52aSJames Smart 18883b5dd52aSJames Smart /* allocate struct lpfc_dmabufext buffer header */ 18893b5dd52aSJames Smart dmp = kmalloc(sizeof(struct lpfc_dmabufext), GFP_KERNEL); 18903b5dd52aSJames Smart if (!dmp) 18913b5dd52aSJames Smart goto out; 18923b5dd52aSJames Smart 18933b5dd52aSJames Smart INIT_LIST_HEAD(&dmp->dma.list); 18943b5dd52aSJames Smart 18953b5dd52aSJames Smart /* Queue it to a linked list */ 18963b5dd52aSJames Smart if (mlist) 18973b5dd52aSJames Smart list_add_tail(&dmp->dma.list, &mlist->dma.list); 18983b5dd52aSJames Smart else 18993b5dd52aSJames Smart mlist = dmp; 19003b5dd52aSJames Smart 19013b5dd52aSJames Smart /* allocate buffer */ 19023b5dd52aSJames Smart dmp->dma.virt = dma_alloc_coherent(&pcidev->dev, 19033b5dd52aSJames Smart cnt, 19043b5dd52aSJames Smart &(dmp->dma.phys), 19053b5dd52aSJames Smart GFP_KERNEL); 19063b5dd52aSJames Smart 19073b5dd52aSJames Smart if (!dmp->dma.virt) 19083b5dd52aSJames Smart goto out; 19093b5dd52aSJames Smart 19103b5dd52aSJames Smart dmp->size = cnt; 19113b5dd52aSJames Smart 19123b5dd52aSJames Smart if (nocopydata) { 19133b5dd52aSJames Smart bpl->tus.f.bdeFlags = 0; 19143b5dd52aSJames Smart pci_dma_sync_single_for_device(phba->pcidev, 19153b5dd52aSJames Smart dmp->dma.phys, LPFC_BPL_SIZE, PCI_DMA_TODEVICE); 19163b5dd52aSJames Smart 19173b5dd52aSJames Smart } else { 19183b5dd52aSJames Smart memset((uint8_t *)dmp->dma.virt, 0, cnt); 19193b5dd52aSJames Smart bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I; 19203b5dd52aSJames Smart } 19213b5dd52aSJames Smart 19223b5dd52aSJames Smart /* build buffer ptr list for IOCB */ 19233b5dd52aSJames Smart bpl->addrLow = le32_to_cpu(putPaddrLow(dmp->dma.phys)); 19243b5dd52aSJames Smart bpl->addrHigh = le32_to_cpu(putPaddrHigh(dmp->dma.phys)); 19253b5dd52aSJames Smart bpl->tus.f.bdeSize = (ushort) cnt; 19263b5dd52aSJames Smart bpl->tus.w = le32_to_cpu(bpl->tus.w); 19273b5dd52aSJames Smart bpl++; 19283b5dd52aSJames Smart 19293b5dd52aSJames Smart i++; 19303b5dd52aSJames Smart offset += cnt; 19313b5dd52aSJames Smart size -= cnt; 19323b5dd52aSJames Smart } 19333b5dd52aSJames Smart 19343b5dd52aSJames Smart mlist->flag = i; 19353b5dd52aSJames Smart return mlist; 19363b5dd52aSJames Smart out: 19373b5dd52aSJames Smart diag_cmd_data_free(phba, mlist); 19383b5dd52aSJames Smart return NULL; 19393b5dd52aSJames Smart } 19403b5dd52aSJames Smart 19413b5dd52aSJames Smart /** 19423b5dd52aSJames Smart * lpfcdiag_loop_post_rxbufs - post the receive buffers for an unsol CT cmd 19433b5dd52aSJames Smart * @phba: Pointer to HBA context object 19443b5dd52aSJames Smart * @rxxri: Receive exchange id 19453b5dd52aSJames Smart * @len: Number of data bytes 19463b5dd52aSJames Smart * 19473b5dd52aSJames Smart * This function allocates and posts a data buffer of sufficient size to recieve 19483b5dd52aSJames Smart * an unsolicted CT command. 19493b5dd52aSJames Smart **/ 19503b5dd52aSJames Smart static int lpfcdiag_loop_post_rxbufs(struct lpfc_hba *phba, uint16_t rxxri, 19513b5dd52aSJames Smart size_t len) 19523b5dd52aSJames Smart { 19533b5dd52aSJames Smart struct lpfc_sli *psli = &phba->sli; 19543b5dd52aSJames Smart struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING]; 19553b5dd52aSJames Smart struct lpfc_iocbq *cmdiocbq; 19563b5dd52aSJames Smart IOCB_t *cmd = NULL; 19573b5dd52aSJames Smart struct list_head head, *curr, *next; 19583b5dd52aSJames Smart struct lpfc_dmabuf *rxbmp; 19593b5dd52aSJames Smart struct lpfc_dmabuf *dmp; 19603b5dd52aSJames Smart struct lpfc_dmabuf *mp[2] = {NULL, NULL}; 19613b5dd52aSJames Smart struct ulp_bde64 *rxbpl = NULL; 19623b5dd52aSJames Smart uint32_t num_bde; 19633b5dd52aSJames Smart struct lpfc_dmabufext *rxbuffer = NULL; 19643b5dd52aSJames Smart int ret_val = 0; 1965*d439d286SJames Smart int iocb_stat; 19663b5dd52aSJames Smart int i = 0; 19673b5dd52aSJames Smart 19683b5dd52aSJames Smart cmdiocbq = lpfc_sli_get_iocbq(phba); 19693b5dd52aSJames Smart rxbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); 19703b5dd52aSJames Smart if (rxbmp != NULL) { 19713b5dd52aSJames Smart rxbmp->virt = lpfc_mbuf_alloc(phba, 0, &rxbmp->phys); 1972c7495937SJames Smart if (rxbmp->virt) { 19733b5dd52aSJames Smart INIT_LIST_HEAD(&rxbmp->list); 19743b5dd52aSJames Smart rxbpl = (struct ulp_bde64 *) rxbmp->virt; 19753b5dd52aSJames Smart rxbuffer = diag_cmd_data_alloc(phba, rxbpl, len, 0); 19763b5dd52aSJames Smart } 1977c7495937SJames Smart } 19783b5dd52aSJames Smart 19793b5dd52aSJames Smart if (!cmdiocbq || !rxbmp || !rxbpl || !rxbuffer) { 1980*d439d286SJames Smart ret_val = -ENOMEM; 19813b5dd52aSJames Smart goto err_post_rxbufs_exit; 19823b5dd52aSJames Smart } 19833b5dd52aSJames Smart 19843b5dd52aSJames Smart /* Queue buffers for the receive exchange */ 19853b5dd52aSJames Smart num_bde = (uint32_t)rxbuffer->flag; 19863b5dd52aSJames Smart dmp = &rxbuffer->dma; 19873b5dd52aSJames Smart 19883b5dd52aSJames Smart cmd = &cmdiocbq->iocb; 19893b5dd52aSJames Smart i = 0; 19903b5dd52aSJames Smart 19913b5dd52aSJames Smart INIT_LIST_HEAD(&head); 19923b5dd52aSJames Smart list_add_tail(&head, &dmp->list); 19933b5dd52aSJames Smart list_for_each_safe(curr, next, &head) { 19943b5dd52aSJames Smart mp[i] = list_entry(curr, struct lpfc_dmabuf, list); 19953b5dd52aSJames Smart list_del(curr); 19963b5dd52aSJames Smart 19973b5dd52aSJames Smart if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) { 19983b5dd52aSJames Smart mp[i]->buffer_tag = lpfc_sli_get_buffer_tag(phba); 19993b5dd52aSJames Smart cmd->un.quexri64cx.buff.bde.addrHigh = 20003b5dd52aSJames Smart putPaddrHigh(mp[i]->phys); 20013b5dd52aSJames Smart cmd->un.quexri64cx.buff.bde.addrLow = 20023b5dd52aSJames Smart putPaddrLow(mp[i]->phys); 20033b5dd52aSJames Smart cmd->un.quexri64cx.buff.bde.tus.f.bdeSize = 20043b5dd52aSJames Smart ((struct lpfc_dmabufext *)mp[i])->size; 20053b5dd52aSJames Smart cmd->un.quexri64cx.buff.buffer_tag = mp[i]->buffer_tag; 20063b5dd52aSJames Smart cmd->ulpCommand = CMD_QUE_XRI64_CX; 20073b5dd52aSJames Smart cmd->ulpPU = 0; 20083b5dd52aSJames Smart cmd->ulpLe = 1; 20093b5dd52aSJames Smart cmd->ulpBdeCount = 1; 20103b5dd52aSJames Smart cmd->unsli3.que_xri64cx_ext_words.ebde_count = 0; 20113b5dd52aSJames Smart 20123b5dd52aSJames Smart } else { 20133b5dd52aSJames Smart cmd->un.cont64[i].addrHigh = putPaddrHigh(mp[i]->phys); 20143b5dd52aSJames Smart cmd->un.cont64[i].addrLow = putPaddrLow(mp[i]->phys); 20153b5dd52aSJames Smart cmd->un.cont64[i].tus.f.bdeSize = 20163b5dd52aSJames Smart ((struct lpfc_dmabufext *)mp[i])->size; 20173b5dd52aSJames Smart cmd->ulpBdeCount = ++i; 20183b5dd52aSJames Smart 20193b5dd52aSJames Smart if ((--num_bde > 0) && (i < 2)) 20203b5dd52aSJames Smart continue; 20213b5dd52aSJames Smart 20223b5dd52aSJames Smart cmd->ulpCommand = CMD_QUE_XRI_BUF64_CX; 20233b5dd52aSJames Smart cmd->ulpLe = 1; 20243b5dd52aSJames Smart } 20253b5dd52aSJames Smart 20263b5dd52aSJames Smart cmd->ulpClass = CLASS3; 20273b5dd52aSJames Smart cmd->ulpContext = rxxri; 20283b5dd52aSJames Smart 2029*d439d286SJames Smart iocb_stat = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq, 2030*d439d286SJames Smart 0); 2031*d439d286SJames Smart if (iocb_stat == IOCB_ERROR) { 20323b5dd52aSJames Smart diag_cmd_data_free(phba, 20333b5dd52aSJames Smart (struct lpfc_dmabufext *)mp[0]); 20343b5dd52aSJames Smart if (mp[1]) 20353b5dd52aSJames Smart diag_cmd_data_free(phba, 20363b5dd52aSJames Smart (struct lpfc_dmabufext *)mp[1]); 20373b5dd52aSJames Smart dmp = list_entry(next, struct lpfc_dmabuf, list); 2038*d439d286SJames Smart ret_val = -EIO; 20393b5dd52aSJames Smart goto err_post_rxbufs_exit; 20403b5dd52aSJames Smart } 20413b5dd52aSJames Smart 20423b5dd52aSJames Smart lpfc_sli_ringpostbuf_put(phba, pring, mp[0]); 20433b5dd52aSJames Smart if (mp[1]) { 20443b5dd52aSJames Smart lpfc_sli_ringpostbuf_put(phba, pring, mp[1]); 20453b5dd52aSJames Smart mp[1] = NULL; 20463b5dd52aSJames Smart } 20473b5dd52aSJames Smart 20483b5dd52aSJames Smart /* The iocb was freed by lpfc_sli_issue_iocb */ 20493b5dd52aSJames Smart cmdiocbq = lpfc_sli_get_iocbq(phba); 20503b5dd52aSJames Smart if (!cmdiocbq) { 20513b5dd52aSJames Smart dmp = list_entry(next, struct lpfc_dmabuf, list); 2052*d439d286SJames Smart ret_val = -EIO; 20533b5dd52aSJames Smart goto err_post_rxbufs_exit; 20543b5dd52aSJames Smart } 20553b5dd52aSJames Smart 20563b5dd52aSJames Smart cmd = &cmdiocbq->iocb; 20573b5dd52aSJames Smart i = 0; 20583b5dd52aSJames Smart } 20593b5dd52aSJames Smart list_del(&head); 20603b5dd52aSJames Smart 20613b5dd52aSJames Smart err_post_rxbufs_exit: 20623b5dd52aSJames Smart 20633b5dd52aSJames Smart if (rxbmp) { 20643b5dd52aSJames Smart if (rxbmp->virt) 20653b5dd52aSJames Smart lpfc_mbuf_free(phba, rxbmp->virt, rxbmp->phys); 20663b5dd52aSJames Smart kfree(rxbmp); 20673b5dd52aSJames Smart } 20683b5dd52aSJames Smart 20693b5dd52aSJames Smart if (cmdiocbq) 20703b5dd52aSJames Smart lpfc_sli_release_iocbq(phba, cmdiocbq); 20713b5dd52aSJames Smart return ret_val; 20723b5dd52aSJames Smart } 20733b5dd52aSJames Smart 20743b5dd52aSJames Smart /** 20753b5dd52aSJames Smart * lpfc_bsg_diag_test - with a port in loopback issues a Ct cmd to itself 20763b5dd52aSJames Smart * @job: LPFC_BSG_VENDOR_DIAG_TEST fc_bsg_job 20773b5dd52aSJames Smart * 20783b5dd52aSJames Smart * This function receives a user data buffer to be transmitted and received on 20793b5dd52aSJames Smart * the same port, the link must be up and in loopback mode prior 20803b5dd52aSJames Smart * to being called. 20813b5dd52aSJames Smart * 1. A kernel buffer is allocated to copy the user data into. 20823b5dd52aSJames Smart * 2. The port registers with "itself". 20833b5dd52aSJames Smart * 3. The transmit and receive exchange ids are obtained. 20843b5dd52aSJames Smart * 4. The receive exchange id is posted. 20853b5dd52aSJames Smart * 5. A new els loopback event is created. 20863b5dd52aSJames Smart * 6. The command and response iocbs are allocated. 20873b5dd52aSJames Smart * 7. The cmd iocb FsType is set to elx loopback and the CmdRsp to looppback. 20883b5dd52aSJames Smart * 20893b5dd52aSJames Smart * This function is meant to be called n times while the port is in loopback 20903b5dd52aSJames Smart * so it is the apps responsibility to issue a reset to take the port out 20913b5dd52aSJames Smart * of loopback mode. 20923b5dd52aSJames Smart **/ 20933b5dd52aSJames Smart static int 20943b5dd52aSJames Smart lpfc_bsg_diag_test(struct fc_bsg_job *job) 20953b5dd52aSJames Smart { 20963b5dd52aSJames Smart struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; 20973b5dd52aSJames Smart struct lpfc_hba *phba = vport->phba; 20983b5dd52aSJames Smart struct diag_mode_test *diag_mode; 20993b5dd52aSJames Smart struct lpfc_bsg_event *evt; 21003b5dd52aSJames Smart struct event_data *evdat; 21013b5dd52aSJames Smart struct lpfc_sli *psli = &phba->sli; 21023b5dd52aSJames Smart uint32_t size; 21033b5dd52aSJames Smart uint32_t full_size; 21043b5dd52aSJames Smart size_t segment_len = 0, segment_offset = 0, current_offset = 0; 21053b5dd52aSJames Smart uint16_t rpi; 21063b5dd52aSJames Smart struct lpfc_iocbq *cmdiocbq, *rspiocbq; 21073b5dd52aSJames Smart IOCB_t *cmd, *rsp; 21083b5dd52aSJames Smart struct lpfc_sli_ct_request *ctreq; 21093b5dd52aSJames Smart struct lpfc_dmabuf *txbmp; 21103b5dd52aSJames Smart struct ulp_bde64 *txbpl = NULL; 21113b5dd52aSJames Smart struct lpfc_dmabufext *txbuffer = NULL; 21123b5dd52aSJames Smart struct list_head head; 21133b5dd52aSJames Smart struct lpfc_dmabuf *curr; 21143b5dd52aSJames Smart uint16_t txxri, rxxri; 21153b5dd52aSJames Smart uint32_t num_bde; 21163b5dd52aSJames Smart uint8_t *ptr = NULL, *rx_databuf = NULL; 21173b5dd52aSJames Smart int rc = 0; 2118*d439d286SJames Smart int time_left; 2119*d439d286SJames Smart int iocb_stat; 21203b5dd52aSJames Smart unsigned long flags; 21213b5dd52aSJames Smart void *dataout = NULL; 21223b5dd52aSJames Smart uint32_t total_mem; 21233b5dd52aSJames Smart 21243b5dd52aSJames Smart /* in case no data is returned return just the return code */ 21253b5dd52aSJames Smart job->reply->reply_payload_rcv_len = 0; 21263b5dd52aSJames Smart 21273b5dd52aSJames Smart if (job->request_len < 21283b5dd52aSJames Smart sizeof(struct fc_bsg_request) + sizeof(struct diag_mode_test)) { 21293b5dd52aSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 21303b5dd52aSJames Smart "2739 Received DIAG TEST request below minimum " 21313b5dd52aSJames Smart "size\n"); 21323b5dd52aSJames Smart rc = -EINVAL; 21333b5dd52aSJames Smart goto loopback_test_exit; 21343b5dd52aSJames Smart } 21353b5dd52aSJames Smart 21363b5dd52aSJames Smart if (job->request_payload.payload_len != 21373b5dd52aSJames Smart job->reply_payload.payload_len) { 21383b5dd52aSJames Smart rc = -EINVAL; 21393b5dd52aSJames Smart goto loopback_test_exit; 21403b5dd52aSJames Smart } 21413b5dd52aSJames Smart 21423b5dd52aSJames Smart diag_mode = (struct diag_mode_test *) 21433b5dd52aSJames Smart job->request->rqst_data.h_vendor.vendor_cmd; 21443b5dd52aSJames Smart 21453b5dd52aSJames Smart if ((phba->link_state == LPFC_HBA_ERROR) || 21463b5dd52aSJames Smart (psli->sli_flag & LPFC_BLOCK_MGMT_IO) || 21473b5dd52aSJames Smart (!(psli->sli_flag & LPFC_SLI_ACTIVE))) { 21483b5dd52aSJames Smart rc = -EACCES; 21493b5dd52aSJames Smart goto loopback_test_exit; 21503b5dd52aSJames Smart } 21513b5dd52aSJames Smart 21523b5dd52aSJames Smart if (!lpfc_is_link_up(phba) || !(phba->link_flag & LS_LOOPBACK_MODE)) { 21533b5dd52aSJames Smart rc = -EACCES; 21543b5dd52aSJames Smart goto loopback_test_exit; 21553b5dd52aSJames Smart } 21563b5dd52aSJames Smart 21573b5dd52aSJames Smart size = job->request_payload.payload_len; 21583b5dd52aSJames Smart full_size = size + ELX_LOOPBACK_HEADER_SZ; /* plus the header */ 21593b5dd52aSJames Smart 21603b5dd52aSJames Smart if ((size == 0) || (size > 80 * BUF_SZ_4K)) { 21613b5dd52aSJames Smart rc = -ERANGE; 21623b5dd52aSJames Smart goto loopback_test_exit; 21633b5dd52aSJames Smart } 21643b5dd52aSJames Smart 21653b5dd52aSJames Smart if (size >= BUF_SZ_4K) { 21663b5dd52aSJames Smart /* 21673b5dd52aSJames Smart * Allocate memory for ioctl data. If buffer is bigger than 64k, 21683b5dd52aSJames Smart * then we allocate 64k and re-use that buffer over and over to 21693b5dd52aSJames Smart * xfer the whole block. This is because Linux kernel has a 21703b5dd52aSJames Smart * problem allocating more than 120k of kernel space memory. Saw 21713b5dd52aSJames Smart * problem with GET_FCPTARGETMAPPING... 21723b5dd52aSJames Smart */ 21733b5dd52aSJames Smart if (size <= (64 * 1024)) 21743b5dd52aSJames Smart total_mem = size; 21753b5dd52aSJames Smart else 21763b5dd52aSJames Smart total_mem = 64 * 1024; 21773b5dd52aSJames Smart } else 21783b5dd52aSJames Smart /* Allocate memory for ioctl data */ 21793b5dd52aSJames Smart total_mem = BUF_SZ_4K; 21803b5dd52aSJames Smart 21813b5dd52aSJames Smart dataout = kmalloc(total_mem, GFP_KERNEL); 21823b5dd52aSJames Smart if (dataout == NULL) { 21833b5dd52aSJames Smart rc = -ENOMEM; 21843b5dd52aSJames Smart goto loopback_test_exit; 21853b5dd52aSJames Smart } 21863b5dd52aSJames Smart 21873b5dd52aSJames Smart ptr = dataout; 21883b5dd52aSJames Smart ptr += ELX_LOOPBACK_HEADER_SZ; 21893b5dd52aSJames Smart sg_copy_to_buffer(job->request_payload.sg_list, 21903b5dd52aSJames Smart job->request_payload.sg_cnt, 21913b5dd52aSJames Smart ptr, size); 21923b5dd52aSJames Smart 21933b5dd52aSJames Smart rc = lpfcdiag_loop_self_reg(phba, &rpi); 2194*d439d286SJames Smart if (rc) 21953b5dd52aSJames Smart goto loopback_test_exit; 21963b5dd52aSJames Smart 21973b5dd52aSJames Smart rc = lpfcdiag_loop_get_xri(phba, rpi, &txxri, &rxxri); 21983b5dd52aSJames Smart if (rc) { 21993b5dd52aSJames Smart lpfcdiag_loop_self_unreg(phba, rpi); 22003b5dd52aSJames Smart goto loopback_test_exit; 22013b5dd52aSJames Smart } 22023b5dd52aSJames Smart 22033b5dd52aSJames Smart rc = lpfcdiag_loop_post_rxbufs(phba, rxxri, full_size); 22043b5dd52aSJames Smart if (rc) { 22053b5dd52aSJames Smart lpfcdiag_loop_self_unreg(phba, rpi); 22063b5dd52aSJames Smart goto loopback_test_exit; 22073b5dd52aSJames Smart } 22083b5dd52aSJames Smart 22093b5dd52aSJames Smart evt = lpfc_bsg_event_new(FC_REG_CT_EVENT, current->pid, 22103b5dd52aSJames Smart SLI_CT_ELX_LOOPBACK); 22113b5dd52aSJames Smart if (!evt) { 22123b5dd52aSJames Smart lpfcdiag_loop_self_unreg(phba, rpi); 22133b5dd52aSJames Smart rc = -ENOMEM; 22143b5dd52aSJames Smart goto loopback_test_exit; 22153b5dd52aSJames Smart } 22163b5dd52aSJames Smart 22173b5dd52aSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 22183b5dd52aSJames Smart list_add(&evt->node, &phba->ct_ev_waiters); 22193b5dd52aSJames Smart lpfc_bsg_event_ref(evt); 22203b5dd52aSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 22213b5dd52aSJames Smart 22223b5dd52aSJames Smart cmdiocbq = lpfc_sli_get_iocbq(phba); 22233b5dd52aSJames Smart rspiocbq = lpfc_sli_get_iocbq(phba); 22243b5dd52aSJames Smart txbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); 22253b5dd52aSJames Smart 22263b5dd52aSJames Smart if (txbmp) { 22273b5dd52aSJames Smart txbmp->virt = lpfc_mbuf_alloc(phba, 0, &txbmp->phys); 2228c7495937SJames Smart if (txbmp->virt) { 22293b5dd52aSJames Smart INIT_LIST_HEAD(&txbmp->list); 22303b5dd52aSJames Smart txbpl = (struct ulp_bde64 *) txbmp->virt; 22313b5dd52aSJames Smart txbuffer = diag_cmd_data_alloc(phba, 22323b5dd52aSJames Smart txbpl, full_size, 0); 22333b5dd52aSJames Smart } 2234c7495937SJames Smart } 22353b5dd52aSJames Smart 2236c7495937SJames Smart if (!cmdiocbq || !rspiocbq || !txbmp || !txbpl || !txbuffer || 2237c7495937SJames Smart !txbmp->virt) { 22383b5dd52aSJames Smart rc = -ENOMEM; 22393b5dd52aSJames Smart goto err_loopback_test_exit; 22403b5dd52aSJames Smart } 22413b5dd52aSJames Smart 22423b5dd52aSJames Smart cmd = &cmdiocbq->iocb; 22433b5dd52aSJames Smart rsp = &rspiocbq->iocb; 22443b5dd52aSJames Smart 22453b5dd52aSJames Smart INIT_LIST_HEAD(&head); 22463b5dd52aSJames Smart list_add_tail(&head, &txbuffer->dma.list); 22473b5dd52aSJames Smart list_for_each_entry(curr, &head, list) { 22483b5dd52aSJames Smart segment_len = ((struct lpfc_dmabufext *)curr)->size; 22493b5dd52aSJames Smart if (current_offset == 0) { 22503b5dd52aSJames Smart ctreq = curr->virt; 22513b5dd52aSJames Smart memset(ctreq, 0, ELX_LOOPBACK_HEADER_SZ); 22523b5dd52aSJames Smart ctreq->RevisionId.bits.Revision = SLI_CT_REVISION; 22533b5dd52aSJames Smart ctreq->RevisionId.bits.InId = 0; 22543b5dd52aSJames Smart ctreq->FsType = SLI_CT_ELX_LOOPBACK; 22553b5dd52aSJames Smart ctreq->FsSubType = 0; 22563b5dd52aSJames Smart ctreq->CommandResponse.bits.CmdRsp = ELX_LOOPBACK_DATA; 22573b5dd52aSJames Smart ctreq->CommandResponse.bits.Size = size; 22583b5dd52aSJames Smart segment_offset = ELX_LOOPBACK_HEADER_SZ; 22593b5dd52aSJames Smart } else 22603b5dd52aSJames Smart segment_offset = 0; 22613b5dd52aSJames Smart 22623b5dd52aSJames Smart BUG_ON(segment_offset >= segment_len); 22633b5dd52aSJames Smart memcpy(curr->virt + segment_offset, 22643b5dd52aSJames Smart ptr + current_offset, 22653b5dd52aSJames Smart segment_len - segment_offset); 22663b5dd52aSJames Smart 22673b5dd52aSJames Smart current_offset += segment_len - segment_offset; 22683b5dd52aSJames Smart BUG_ON(current_offset > size); 22693b5dd52aSJames Smart } 22703b5dd52aSJames Smart list_del(&head); 22713b5dd52aSJames Smart 22723b5dd52aSJames Smart /* Build the XMIT_SEQUENCE iocb */ 22733b5dd52aSJames Smart 22743b5dd52aSJames Smart num_bde = (uint32_t)txbuffer->flag; 22753b5dd52aSJames Smart 22763b5dd52aSJames Smart cmd->un.xseq64.bdl.addrHigh = putPaddrHigh(txbmp->phys); 22773b5dd52aSJames Smart cmd->un.xseq64.bdl.addrLow = putPaddrLow(txbmp->phys); 22783b5dd52aSJames Smart cmd->un.xseq64.bdl.bdeFlags = BUFF_TYPE_BLP_64; 22793b5dd52aSJames Smart cmd->un.xseq64.bdl.bdeSize = (num_bde * sizeof(struct ulp_bde64)); 22803b5dd52aSJames Smart 22813b5dd52aSJames Smart cmd->un.xseq64.w5.hcsw.Fctl = (LS | LA); 22823b5dd52aSJames Smart cmd->un.xseq64.w5.hcsw.Dfctl = 0; 22833b5dd52aSJames Smart cmd->un.xseq64.w5.hcsw.Rctl = FC_RCTL_DD_UNSOL_CTL; 22843b5dd52aSJames Smart cmd->un.xseq64.w5.hcsw.Type = FC_TYPE_CT; 22853b5dd52aSJames Smart 22863b5dd52aSJames Smart cmd->ulpCommand = CMD_XMIT_SEQUENCE64_CX; 22873b5dd52aSJames Smart cmd->ulpBdeCount = 1; 22883b5dd52aSJames Smart cmd->ulpLe = 1; 22893b5dd52aSJames Smart cmd->ulpClass = CLASS3; 22903b5dd52aSJames Smart cmd->ulpContext = txxri; 22913b5dd52aSJames Smart 22923b5dd52aSJames Smart cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC; 22933b5dd52aSJames Smart cmdiocbq->vport = phba->pport; 22943b5dd52aSJames Smart 2295*d439d286SJames Smart iocb_stat = lpfc_sli_issue_iocb_wait(phba, LPFC_ELS_RING, cmdiocbq, 2296*d439d286SJames Smart rspiocbq, (phba->fc_ratov * 2) + 2297*d439d286SJames Smart LPFC_DRVR_TIMEOUT); 22983b5dd52aSJames Smart 2299*d439d286SJames Smart if ((iocb_stat != IOCB_SUCCESS) || (rsp->ulpStatus != IOCB_SUCCESS)) { 23003b5dd52aSJames Smart rc = -EIO; 23013b5dd52aSJames Smart goto err_loopback_test_exit; 23023b5dd52aSJames Smart } 23033b5dd52aSJames Smart 23043b5dd52aSJames Smart evt->waiting = 1; 2305*d439d286SJames Smart time_left = wait_event_interruptible_timeout( 23063b5dd52aSJames Smart evt->wq, !list_empty(&evt->events_to_see), 23073b5dd52aSJames Smart ((phba->fc_ratov * 2) + LPFC_DRVR_TIMEOUT) * HZ); 23083b5dd52aSJames Smart evt->waiting = 0; 23093b5dd52aSJames Smart if (list_empty(&evt->events_to_see)) 2310*d439d286SJames Smart rc = (time_left) ? -EINTR : -ETIMEDOUT; 23113b5dd52aSJames Smart else { 23123b5dd52aSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 23133b5dd52aSJames Smart list_move(evt->events_to_see.prev, &evt->events_to_get); 23143b5dd52aSJames Smart evdat = list_entry(evt->events_to_get.prev, 23153b5dd52aSJames Smart typeof(*evdat), node); 23163b5dd52aSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 23173b5dd52aSJames Smart rx_databuf = evdat->data; 23183b5dd52aSJames Smart if (evdat->len != full_size) { 23193b5dd52aSJames Smart lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC, 23203b5dd52aSJames Smart "1603 Loopback test did not receive expected " 23213b5dd52aSJames Smart "data length. actual length 0x%x expected " 23223b5dd52aSJames Smart "length 0x%x\n", 23233b5dd52aSJames Smart evdat->len, full_size); 23243b5dd52aSJames Smart rc = -EIO; 23253b5dd52aSJames Smart } else if (rx_databuf == NULL) 23263b5dd52aSJames Smart rc = -EIO; 23273b5dd52aSJames Smart else { 23283b5dd52aSJames Smart rc = IOCB_SUCCESS; 23293b5dd52aSJames Smart /* skip over elx loopback header */ 23303b5dd52aSJames Smart rx_databuf += ELX_LOOPBACK_HEADER_SZ; 23313b5dd52aSJames Smart job->reply->reply_payload_rcv_len = 23323b5dd52aSJames Smart sg_copy_from_buffer(job->reply_payload.sg_list, 23333b5dd52aSJames Smart job->reply_payload.sg_cnt, 23343b5dd52aSJames Smart rx_databuf, size); 23353b5dd52aSJames Smart job->reply->reply_payload_rcv_len = size; 23363b5dd52aSJames Smart } 23373b5dd52aSJames Smart } 23383b5dd52aSJames Smart 23393b5dd52aSJames Smart err_loopback_test_exit: 23403b5dd52aSJames Smart lpfcdiag_loop_self_unreg(phba, rpi); 23413b5dd52aSJames Smart 23423b5dd52aSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 23433b5dd52aSJames Smart lpfc_bsg_event_unref(evt); /* release ref */ 23443b5dd52aSJames Smart lpfc_bsg_event_unref(evt); /* delete */ 23453b5dd52aSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 23463b5dd52aSJames Smart 23473b5dd52aSJames Smart if (cmdiocbq != NULL) 23483b5dd52aSJames Smart lpfc_sli_release_iocbq(phba, cmdiocbq); 23493b5dd52aSJames Smart 23503b5dd52aSJames Smart if (rspiocbq != NULL) 23513b5dd52aSJames Smart lpfc_sli_release_iocbq(phba, rspiocbq); 23523b5dd52aSJames Smart 23533b5dd52aSJames Smart if (txbmp != NULL) { 23543b5dd52aSJames Smart if (txbpl != NULL) { 23553b5dd52aSJames Smart if (txbuffer != NULL) 23563b5dd52aSJames Smart diag_cmd_data_free(phba, txbuffer); 23573b5dd52aSJames Smart lpfc_mbuf_free(phba, txbmp->virt, txbmp->phys); 23583b5dd52aSJames Smart } 23593b5dd52aSJames Smart kfree(txbmp); 23603b5dd52aSJames Smart } 23613b5dd52aSJames Smart 23623b5dd52aSJames Smart loopback_test_exit: 23633b5dd52aSJames Smart kfree(dataout); 23643b5dd52aSJames Smart /* make error code available to userspace */ 23653b5dd52aSJames Smart job->reply->result = rc; 23663b5dd52aSJames Smart job->dd_data = NULL; 23673b5dd52aSJames Smart /* complete the job back to userspace if no error */ 23683b5dd52aSJames Smart if (rc == 0) 23693b5dd52aSJames Smart job->job_done(job); 23703b5dd52aSJames Smart return rc; 23713b5dd52aSJames Smart } 23723b5dd52aSJames Smart 23733b5dd52aSJames Smart /** 23743b5dd52aSJames Smart * lpfc_bsg_get_dfc_rev - process a GET_DFC_REV bsg vendor command 23753b5dd52aSJames Smart * @job: GET_DFC_REV fc_bsg_job 23763b5dd52aSJames Smart **/ 23773b5dd52aSJames Smart static int 23783b5dd52aSJames Smart lpfc_bsg_get_dfc_rev(struct fc_bsg_job *job) 23793b5dd52aSJames Smart { 23803b5dd52aSJames Smart struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; 23813b5dd52aSJames Smart struct lpfc_hba *phba = vport->phba; 23823b5dd52aSJames Smart struct get_mgmt_rev *event_req; 23833b5dd52aSJames Smart struct get_mgmt_rev_reply *event_reply; 23843b5dd52aSJames Smart int rc = 0; 23853b5dd52aSJames Smart 23863b5dd52aSJames Smart if (job->request_len < 23873b5dd52aSJames Smart sizeof(struct fc_bsg_request) + sizeof(struct get_mgmt_rev)) { 23883b5dd52aSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 23893b5dd52aSJames Smart "2740 Received GET_DFC_REV request below " 23903b5dd52aSJames Smart "minimum size\n"); 23913b5dd52aSJames Smart rc = -EINVAL; 23923b5dd52aSJames Smart goto job_error; 23933b5dd52aSJames Smart } 23943b5dd52aSJames Smart 23953b5dd52aSJames Smart event_req = (struct get_mgmt_rev *) 23963b5dd52aSJames Smart job->request->rqst_data.h_vendor.vendor_cmd; 23973b5dd52aSJames Smart 23983b5dd52aSJames Smart event_reply = (struct get_mgmt_rev_reply *) 23993b5dd52aSJames Smart job->reply->reply_data.vendor_reply.vendor_rsp; 24003b5dd52aSJames Smart 24013b5dd52aSJames Smart if (job->reply_len < 24023b5dd52aSJames Smart sizeof(struct fc_bsg_request) + sizeof(struct get_mgmt_rev_reply)) { 24033b5dd52aSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 24043b5dd52aSJames Smart "2741 Received GET_DFC_REV reply below " 24053b5dd52aSJames Smart "minimum size\n"); 24063b5dd52aSJames Smart rc = -EINVAL; 24073b5dd52aSJames Smart goto job_error; 24083b5dd52aSJames Smart } 24093b5dd52aSJames Smart 24103b5dd52aSJames Smart event_reply->info.a_Major = MANAGEMENT_MAJOR_REV; 24113b5dd52aSJames Smart event_reply->info.a_Minor = MANAGEMENT_MINOR_REV; 24123b5dd52aSJames Smart job_error: 24133b5dd52aSJames Smart job->reply->result = rc; 24143b5dd52aSJames Smart if (rc == 0) 24153b5dd52aSJames Smart job->job_done(job); 24163b5dd52aSJames Smart return rc; 24173b5dd52aSJames Smart } 24183b5dd52aSJames Smart 24193b5dd52aSJames Smart /** 24203b5dd52aSJames Smart * lpfc_bsg_wake_mbox_wait - lpfc_bsg_issue_mbox mbox completion handler 24213b5dd52aSJames Smart * @phba: Pointer to HBA context object. 24223b5dd52aSJames Smart * @pmboxq: Pointer to mailbox command. 24233b5dd52aSJames Smart * 24243b5dd52aSJames Smart * This is completion handler function for mailbox commands issued from 24253b5dd52aSJames Smart * lpfc_bsg_issue_mbox function. This function is called by the 24263b5dd52aSJames Smart * mailbox event handler function with no lock held. This function 24273b5dd52aSJames Smart * will wake up thread waiting on the wait queue pointed by context1 24283b5dd52aSJames Smart * of the mailbox. 24293b5dd52aSJames Smart **/ 24303b5dd52aSJames Smart void 24313b5dd52aSJames Smart lpfc_bsg_wake_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) 24323b5dd52aSJames Smart { 24333b5dd52aSJames Smart struct bsg_job_data *dd_data; 24343b5dd52aSJames Smart struct fc_bsg_job *job; 24353b5dd52aSJames Smart uint32_t size; 24363b5dd52aSJames Smart unsigned long flags; 24377a470277SJames Smart uint8_t *to; 24387a470277SJames Smart uint8_t *from; 24393b5dd52aSJames Smart 24403b5dd52aSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 24413b5dd52aSJames Smart dd_data = pmboxq->context1; 24427a470277SJames Smart /* job already timed out? */ 24433b5dd52aSJames Smart if (!dd_data) { 24443b5dd52aSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 24453b5dd52aSJames Smart return; 24463b5dd52aSJames Smart } 24473b5dd52aSJames Smart 24487a470277SJames Smart /* build the outgoing buffer to do an sg copy 24497a470277SJames Smart * the format is the response mailbox followed by any extended 24507a470277SJames Smart * mailbox data 24517a470277SJames Smart */ 24527a470277SJames Smart from = (uint8_t *)&pmboxq->u.mb; 24537a470277SJames Smart to = (uint8_t *)dd_data->context_un.mbox.mb; 24547a470277SJames Smart memcpy(to, from, sizeof(MAILBOX_t)); 2455c7495937SJames Smart if (pmboxq->u.mb.mbxStatus == MBX_SUCCESS) { 24567a470277SJames Smart /* copy the extended data if any, count is in words */ 2457c7495937SJames Smart if (dd_data->context_un.mbox.outExtWLen) { 24587a470277SJames Smart from = (uint8_t *)dd_data->context_un.mbox.ext; 24597a470277SJames Smart to += sizeof(MAILBOX_t); 2460c7495937SJames Smart size = dd_data->context_un.mbox.outExtWLen * 2461c7495937SJames Smart sizeof(uint32_t); 2462c7495937SJames Smart memcpy(to, from, size); 2463c7495937SJames Smart } else if (pmboxq->u.mb.mbxCommand == MBX_RUN_BIU_DIAG64) { 2464c7495937SJames Smart from = (uint8_t *)dd_data->context_un.mbox. 2465c7495937SJames Smart dmp->dma.virt; 2466c7495937SJames Smart to += sizeof(MAILBOX_t); 2467c7495937SJames Smart size = dd_data->context_un.mbox.dmp->size; 2468c7495937SJames Smart memcpy(to, from, size); 2469c7495937SJames Smart } else if ((phba->sli_rev == LPFC_SLI_REV4) && 2470c7495937SJames Smart (pmboxq->u.mb.mbxCommand == MBX_DUMP_MEMORY)) { 2471c7495937SJames Smart from = (uint8_t *)dd_data->context_un.mbox.dmp->dma. 2472c7495937SJames Smart virt; 2473c7495937SJames Smart to += sizeof(MAILBOX_t); 2474c7495937SJames Smart size = pmboxq->u.mb.un.varWords[5]; 2475c7495937SJames Smart memcpy(to, from, size); 2476c7495937SJames Smart } else if (pmboxq->u.mb.mbxCommand == MBX_READ_EVENT_LOG) { 2477c7495937SJames Smart from = (uint8_t *)dd_data->context_un. 2478c7495937SJames Smart mbox.dmp->dma.virt; 2479c7495937SJames Smart to += sizeof(MAILBOX_t); 2480c7495937SJames Smart size = dd_data->context_un.mbox.dmp->size; 2481c7495937SJames Smart memcpy(to, from, size); 2482c7495937SJames Smart } 24837a470277SJames Smart } 24847a470277SJames Smart 24857a470277SJames Smart from = (uint8_t *)dd_data->context_un.mbox.mb; 24863b5dd52aSJames Smart job = dd_data->context_un.mbox.set_job; 24877a470277SJames Smart size = job->reply_payload.payload_len; 24883b5dd52aSJames Smart job->reply->reply_payload_rcv_len = 24893b5dd52aSJames Smart sg_copy_from_buffer(job->reply_payload.sg_list, 24903b5dd52aSJames Smart job->reply_payload.sg_cnt, 24917a470277SJames Smart from, size); 24923b5dd52aSJames Smart job->reply->result = 0; 24937a470277SJames Smart 24943b5dd52aSJames Smart dd_data->context_un.mbox.set_job = NULL; 24953b5dd52aSJames Smart job->dd_data = NULL; 24963b5dd52aSJames Smart job->job_done(job); 24977a470277SJames Smart /* need to hold the lock until we call job done to hold off 24987a470277SJames Smart * the timeout handler returning to the midlayer while 24997a470277SJames Smart * we are stillprocessing the job 25007a470277SJames Smart */ 25013b5dd52aSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 25027a470277SJames Smart 25037a470277SJames Smart kfree(dd_data->context_un.mbox.mb); 25043b5dd52aSJames Smart mempool_free(dd_data->context_un.mbox.pmboxq, phba->mbox_mem_pool); 25057a470277SJames Smart kfree(dd_data->context_un.mbox.ext); 25067a470277SJames Smart if (dd_data->context_un.mbox.dmp) { 25077a470277SJames Smart dma_free_coherent(&phba->pcidev->dev, 25087a470277SJames Smart dd_data->context_un.mbox.dmp->size, 25097a470277SJames Smart dd_data->context_un.mbox.dmp->dma.virt, 25107a470277SJames Smart dd_data->context_un.mbox.dmp->dma.phys); 25117a470277SJames Smart kfree(dd_data->context_un.mbox.dmp); 25127a470277SJames Smart } 25137a470277SJames Smart if (dd_data->context_un.mbox.rxbmp) { 25147a470277SJames Smart lpfc_mbuf_free(phba, dd_data->context_un.mbox.rxbmp->virt, 25157a470277SJames Smart dd_data->context_un.mbox.rxbmp->phys); 25167a470277SJames Smart kfree(dd_data->context_un.mbox.rxbmp); 25177a470277SJames Smart } 25183b5dd52aSJames Smart kfree(dd_data); 25193b5dd52aSJames Smart return; 25203b5dd52aSJames Smart } 25213b5dd52aSJames Smart 25223b5dd52aSJames Smart /** 25233b5dd52aSJames Smart * lpfc_bsg_check_cmd_access - test for a supported mailbox command 25243b5dd52aSJames Smart * @phba: Pointer to HBA context object. 25253b5dd52aSJames Smart * @mb: Pointer to a mailbox object. 25263b5dd52aSJames Smart * @vport: Pointer to a vport object. 25273b5dd52aSJames Smart * 25283b5dd52aSJames Smart * Some commands require the port to be offline, some may not be called from 25293b5dd52aSJames Smart * the application. 25303b5dd52aSJames Smart **/ 25313b5dd52aSJames Smart static int lpfc_bsg_check_cmd_access(struct lpfc_hba *phba, 25323b5dd52aSJames Smart MAILBOX_t *mb, struct lpfc_vport *vport) 25333b5dd52aSJames Smart { 25343b5dd52aSJames Smart /* return negative error values for bsg job */ 25353b5dd52aSJames Smart switch (mb->mbxCommand) { 25363b5dd52aSJames Smart /* Offline only */ 25373b5dd52aSJames Smart case MBX_INIT_LINK: 25383b5dd52aSJames Smart case MBX_DOWN_LINK: 25393b5dd52aSJames Smart case MBX_CONFIG_LINK: 25403b5dd52aSJames Smart case MBX_CONFIG_RING: 25413b5dd52aSJames Smart case MBX_RESET_RING: 25423b5dd52aSJames Smart case MBX_UNREG_LOGIN: 25433b5dd52aSJames Smart case MBX_CLEAR_LA: 25443b5dd52aSJames Smart case MBX_DUMP_CONTEXT: 25453b5dd52aSJames Smart case MBX_RUN_DIAGS: 25463b5dd52aSJames Smart case MBX_RESTART: 25473b5dd52aSJames Smart case MBX_SET_MASK: 25483b5dd52aSJames Smart if (!(vport->fc_flag & FC_OFFLINE_MODE)) { 25493b5dd52aSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 25503b5dd52aSJames Smart "2743 Command 0x%x is illegal in on-line " 25513b5dd52aSJames Smart "state\n", 25523b5dd52aSJames Smart mb->mbxCommand); 25533b5dd52aSJames Smart return -EPERM; 25543b5dd52aSJames Smart } 25553b5dd52aSJames Smart case MBX_WRITE_NV: 25563b5dd52aSJames Smart case MBX_WRITE_VPARMS: 25573b5dd52aSJames Smart case MBX_LOAD_SM: 25583b5dd52aSJames Smart case MBX_READ_NV: 25593b5dd52aSJames Smart case MBX_READ_CONFIG: 25603b5dd52aSJames Smart case MBX_READ_RCONFIG: 25613b5dd52aSJames Smart case MBX_READ_STATUS: 25623b5dd52aSJames Smart case MBX_READ_XRI: 25633b5dd52aSJames Smart case MBX_READ_REV: 25643b5dd52aSJames Smart case MBX_READ_LNK_STAT: 25653b5dd52aSJames Smart case MBX_DUMP_MEMORY: 25663b5dd52aSJames Smart case MBX_DOWN_LOAD: 25673b5dd52aSJames Smart case MBX_UPDATE_CFG: 25683b5dd52aSJames Smart case MBX_KILL_BOARD: 25693b5dd52aSJames Smart case MBX_LOAD_AREA: 25703b5dd52aSJames Smart case MBX_LOAD_EXP_ROM: 25713b5dd52aSJames Smart case MBX_BEACON: 25723b5dd52aSJames Smart case MBX_DEL_LD_ENTRY: 25733b5dd52aSJames Smart case MBX_SET_DEBUG: 25743b5dd52aSJames Smart case MBX_WRITE_WWN: 25753b5dd52aSJames Smart case MBX_SLI4_CONFIG: 2576c7495937SJames Smart case MBX_READ_EVENT_LOG: 25773b5dd52aSJames Smart case MBX_READ_EVENT_LOG_STATUS: 25783b5dd52aSJames Smart case MBX_WRITE_EVENT_LOG: 25793b5dd52aSJames Smart case MBX_PORT_CAPABILITIES: 25803b5dd52aSJames Smart case MBX_PORT_IOV_CONTROL: 25817a470277SJames Smart case MBX_RUN_BIU_DIAG64: 25823b5dd52aSJames Smart break; 25833b5dd52aSJames Smart case MBX_SET_VARIABLE: 2584e2aed29fSJames Smart lpfc_printf_log(phba, KERN_INFO, LOG_INIT, 2585e2aed29fSJames Smart "1226 mbox: set_variable 0x%x, 0x%x\n", 2586e2aed29fSJames Smart mb->un.varWords[0], 2587e2aed29fSJames Smart mb->un.varWords[1]); 2588e2aed29fSJames Smart if ((mb->un.varWords[0] == SETVAR_MLOMNT) 2589e2aed29fSJames Smart && (mb->un.varWords[1] == 1)) { 2590e2aed29fSJames Smart phba->wait_4_mlo_maint_flg = 1; 2591e2aed29fSJames Smart } else if (mb->un.varWords[0] == SETVAR_MLORST) { 2592e2aed29fSJames Smart phba->link_flag &= ~LS_LOOPBACK_MODE; 2593e2aed29fSJames Smart phba->fc_topology = TOPOLOGY_PT_PT; 2594e2aed29fSJames Smart } 2595e2aed29fSJames Smart break; 25963b5dd52aSJames Smart case MBX_READ_SPARM64: 25973b5dd52aSJames Smart case MBX_READ_LA: 25983b5dd52aSJames Smart case MBX_READ_LA64: 25993b5dd52aSJames Smart case MBX_REG_LOGIN: 26003b5dd52aSJames Smart case MBX_REG_LOGIN64: 26013b5dd52aSJames Smart case MBX_CONFIG_PORT: 26023b5dd52aSJames Smart case MBX_RUN_BIU_DIAG: 26033b5dd52aSJames Smart default: 26043b5dd52aSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 26053b5dd52aSJames Smart "2742 Unknown Command 0x%x\n", 26063b5dd52aSJames Smart mb->mbxCommand); 26073b5dd52aSJames Smart return -EPERM; 26083b5dd52aSJames Smart } 26093b5dd52aSJames Smart 26103b5dd52aSJames Smart return 0; /* ok */ 26113b5dd52aSJames Smart } 26123b5dd52aSJames Smart 26133b5dd52aSJames Smart /** 26143b5dd52aSJames Smart * lpfc_bsg_issue_mbox - issues a mailbox command on behalf of an app 26153b5dd52aSJames Smart * @phba: Pointer to HBA context object. 26163b5dd52aSJames Smart * @mb: Pointer to a mailbox object. 26173b5dd52aSJames Smart * @vport: Pointer to a vport object. 26183b5dd52aSJames Smart * 26193b5dd52aSJames Smart * Allocate a tracking object, mailbox command memory, get a mailbox 26203b5dd52aSJames Smart * from the mailbox pool, copy the caller mailbox command. 26213b5dd52aSJames Smart * 26223b5dd52aSJames Smart * If offline and the sli is active we need to poll for the command (port is 26233b5dd52aSJames Smart * being reset) and com-plete the job, otherwise issue the mailbox command and 26243b5dd52aSJames Smart * let our completion handler finish the command. 26253b5dd52aSJames Smart **/ 26263b5dd52aSJames Smart static uint32_t 26273b5dd52aSJames Smart lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job, 26283b5dd52aSJames Smart struct lpfc_vport *vport) 26293b5dd52aSJames Smart { 26307a470277SJames Smart LPFC_MBOXQ_t *pmboxq = NULL; /* internal mailbox queue */ 26317a470277SJames Smart MAILBOX_t *pmb; /* shortcut to the pmboxq mailbox */ 26327a470277SJames Smart /* a 4k buffer to hold the mb and extended data from/to the bsg */ 26337a470277SJames Smart MAILBOX_t *mb = NULL; 26347a470277SJames Smart struct bsg_job_data *dd_data = NULL; /* bsg data tracking structure */ 26353b5dd52aSJames Smart uint32_t size; 26367a470277SJames Smart struct lpfc_dmabuf *rxbmp = NULL; /* for biu diag */ 26377a470277SJames Smart struct lpfc_dmabufext *dmp = NULL; /* for biu diag */ 26387a470277SJames Smart struct ulp_bde64 *rxbpl = NULL; 26397a470277SJames Smart struct dfc_mbox_req *mbox_req = (struct dfc_mbox_req *) 26407a470277SJames Smart job->request->rqst_data.h_vendor.vendor_cmd; 26417a470277SJames Smart uint8_t *ext = NULL; 26423b5dd52aSJames Smart int rc = 0; 26437a470277SJames Smart uint8_t *from; 26447a470277SJames Smart 26457a470277SJames Smart /* in case no data is transferred */ 26467a470277SJames Smart job->reply->reply_payload_rcv_len = 0; 26477a470277SJames Smart 26487a470277SJames Smart /* check if requested extended data lengths are valid */ 26497a470277SJames Smart if ((mbox_req->inExtWLen > MAILBOX_EXT_SIZE) || 2650c7495937SJames Smart (mbox_req->outExtWLen > MAILBOX_EXT_SIZE)) { 26517a470277SJames Smart rc = -ERANGE; 26527a470277SJames Smart goto job_done; 26537a470277SJames Smart } 26543b5dd52aSJames Smart 26553b5dd52aSJames Smart /* allocate our bsg tracking structure */ 26563b5dd52aSJames Smart dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL); 26573b5dd52aSJames Smart if (!dd_data) { 26583b5dd52aSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 26593b5dd52aSJames Smart "2727 Failed allocation of dd_data\n"); 26607a470277SJames Smart rc = -ENOMEM; 26617a470277SJames Smart goto job_done; 26623b5dd52aSJames Smart } 26633b5dd52aSJames Smart 266449198b37SJames Smart mb = kzalloc(BSG_MBOX_SIZE, GFP_KERNEL); 26653b5dd52aSJames Smart if (!mb) { 26667a470277SJames Smart rc = -ENOMEM; 26677a470277SJames Smart goto job_done; 26683b5dd52aSJames Smart } 26693b5dd52aSJames Smart 26703b5dd52aSJames Smart pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); 26713b5dd52aSJames Smart if (!pmboxq) { 26727a470277SJames Smart rc = -ENOMEM; 26737a470277SJames Smart goto job_done; 26743b5dd52aSJames Smart } 26757a470277SJames Smart memset(pmboxq, 0, sizeof(LPFC_MBOXQ_t)); 26763b5dd52aSJames Smart 26773b5dd52aSJames Smart size = job->request_payload.payload_len; 26783b5dd52aSJames Smart sg_copy_to_buffer(job->request_payload.sg_list, 26793b5dd52aSJames Smart job->request_payload.sg_cnt, 26803b5dd52aSJames Smart mb, size); 26813b5dd52aSJames Smart 26823b5dd52aSJames Smart rc = lpfc_bsg_check_cmd_access(phba, mb, vport); 26837a470277SJames Smart if (rc != 0) 26847a470277SJames Smart goto job_done; /* must be negative */ 26853b5dd52aSJames Smart 26863b5dd52aSJames Smart pmb = &pmboxq->u.mb; 26873b5dd52aSJames Smart memcpy(pmb, mb, sizeof(*pmb)); 26883b5dd52aSJames Smart pmb->mbxOwner = OWN_HOST; 26893b5dd52aSJames Smart pmboxq->vport = vport; 26903b5dd52aSJames Smart 2691c7495937SJames Smart /* If HBA encountered an error attention, allow only DUMP 2692c7495937SJames Smart * or RESTART mailbox commands until the HBA is restarted. 2693c7495937SJames Smart */ 2694c7495937SJames Smart if (phba->pport->stopped && 2695c7495937SJames Smart pmb->mbxCommand != MBX_DUMP_MEMORY && 2696c7495937SJames Smart pmb->mbxCommand != MBX_RESTART && 2697c7495937SJames Smart pmb->mbxCommand != MBX_WRITE_VPARMS && 2698c7495937SJames Smart pmb->mbxCommand != MBX_WRITE_WWN) 2699c7495937SJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX, 2700c7495937SJames Smart "2797 mbox: Issued mailbox cmd " 2701c7495937SJames Smart "0x%x while in stopped state.\n", 2702c7495937SJames Smart pmb->mbxCommand); 2703c7495937SJames Smart 2704c7495937SJames Smart /* Don't allow mailbox commands to be sent when blocked 2705c7495937SJames Smart * or when in the middle of discovery 2706c7495937SJames Smart */ 2707c7495937SJames Smart if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO) { 2708c7495937SJames Smart rc = -EAGAIN; 2709c7495937SJames Smart goto job_done; 2710c7495937SJames Smart } 2711c7495937SJames Smart 27127a470277SJames Smart /* extended mailbox commands will need an extended buffer */ 2713c7495937SJames Smart if (mbox_req->inExtWLen || mbox_req->outExtWLen) { 27147a470277SJames Smart ext = kzalloc(MAILBOX_EXT_SIZE, GFP_KERNEL); 27157a470277SJames Smart if (!ext) { 27167a470277SJames Smart rc = -ENOMEM; 27177a470277SJames Smart goto job_done; 27183b5dd52aSJames Smart } 27193b5dd52aSJames Smart 27207a470277SJames Smart /* any data for the device? */ 27217a470277SJames Smart if (mbox_req->inExtWLen) { 27227a470277SJames Smart from = (uint8_t *)mb; 27237a470277SJames Smart from += sizeof(MAILBOX_t); 27247a470277SJames Smart memcpy((uint8_t *)ext, from, 27257a470277SJames Smart mbox_req->inExtWLen * sizeof(uint32_t)); 27267a470277SJames Smart } 27277a470277SJames Smart 27287a470277SJames Smart pmboxq->context2 = ext; 27297a470277SJames Smart pmboxq->in_ext_byte_len = 27307a470277SJames Smart mbox_req->inExtWLen * sizeof(uint32_t); 27317a470277SJames Smart pmboxq->out_ext_byte_len = 2732c7495937SJames Smart mbox_req->outExtWLen * sizeof(uint32_t); 27337a470277SJames Smart pmboxq->mbox_offset_word = mbox_req->mbOffset; 27347a470277SJames Smart } 27357a470277SJames Smart 27367a470277SJames Smart /* biu diag will need a kernel buffer to transfer the data 27377a470277SJames Smart * allocate our own buffer and setup the mailbox command to 27387a470277SJames Smart * use ours 27397a470277SJames Smart */ 27407a470277SJames Smart if (pmb->mbxCommand == MBX_RUN_BIU_DIAG64) { 2741c7495937SJames Smart uint32_t transmit_length = pmb->un.varWords[1]; 2742c7495937SJames Smart uint32_t receive_length = pmb->un.varWords[4]; 2743c7495937SJames Smart /* transmit length cannot be greater than receive length or 2744c7495937SJames Smart * mailbox extension size 2745c7495937SJames Smart */ 2746c7495937SJames Smart if ((transmit_length > receive_length) || 2747c7495937SJames Smart (transmit_length > MAILBOX_EXT_SIZE)) { 2748c7495937SJames Smart rc = -ERANGE; 2749c7495937SJames Smart goto job_done; 2750c7495937SJames Smart } 2751c7495937SJames Smart 27527a470277SJames Smart rxbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); 27537a470277SJames Smart if (!rxbmp) { 27547a470277SJames Smart rc = -ENOMEM; 27557a470277SJames Smart goto job_done; 27567a470277SJames Smart } 27577a470277SJames Smart 27587a470277SJames Smart rxbmp->virt = lpfc_mbuf_alloc(phba, 0, &rxbmp->phys); 2759c7495937SJames Smart if (!rxbmp->virt) { 2760c7495937SJames Smart rc = -ENOMEM; 2761c7495937SJames Smart goto job_done; 2762c7495937SJames Smart } 2763c7495937SJames Smart 27647a470277SJames Smart INIT_LIST_HEAD(&rxbmp->list); 27657a470277SJames Smart rxbpl = (struct ulp_bde64 *) rxbmp->virt; 2766c7495937SJames Smart dmp = diag_cmd_data_alloc(phba, rxbpl, transmit_length, 0); 27677a470277SJames Smart if (!dmp) { 27687a470277SJames Smart rc = -ENOMEM; 27697a470277SJames Smart goto job_done; 27707a470277SJames Smart } 27717a470277SJames Smart 27727a470277SJames Smart INIT_LIST_HEAD(&dmp->dma.list); 27737a470277SJames Smart pmb->un.varBIUdiag.un.s2.xmit_bde64.addrHigh = 27747a470277SJames Smart putPaddrHigh(dmp->dma.phys); 27757a470277SJames Smart pmb->un.varBIUdiag.un.s2.xmit_bde64.addrLow = 27767a470277SJames Smart putPaddrLow(dmp->dma.phys); 27777a470277SJames Smart 27787a470277SJames Smart pmb->un.varBIUdiag.un.s2.rcv_bde64.addrHigh = 27797a470277SJames Smart putPaddrHigh(dmp->dma.phys + 27807a470277SJames Smart pmb->un.varBIUdiag.un.s2. 27817a470277SJames Smart xmit_bde64.tus.f.bdeSize); 27827a470277SJames Smart pmb->un.varBIUdiag.un.s2.rcv_bde64.addrLow = 27837a470277SJames Smart putPaddrLow(dmp->dma.phys + 27847a470277SJames Smart pmb->un.varBIUdiag.un.s2. 27857a470277SJames Smart xmit_bde64.tus.f.bdeSize); 2786c7495937SJames Smart 2787c7495937SJames Smart /* copy the transmit data found in the mailbox extension area */ 2788c7495937SJames Smart from = (uint8_t *)mb; 2789c7495937SJames Smart from += sizeof(MAILBOX_t); 2790c7495937SJames Smart memcpy((uint8_t *)dmp->dma.virt, from, transmit_length); 2791c7495937SJames Smart } else if (pmb->mbxCommand == MBX_READ_EVENT_LOG) { 2792c7495937SJames Smart struct READ_EVENT_LOG_VAR *rdEventLog = 2793c7495937SJames Smart &pmb->un.varRdEventLog ; 2794c7495937SJames Smart uint32_t receive_length = rdEventLog->rcv_bde64.tus.f.bdeSize; 2795c7495937SJames Smart uint32_t mode = bf_get(lpfc_event_log, rdEventLog); 2796c7495937SJames Smart 2797c7495937SJames Smart /* receive length cannot be greater than mailbox 2798c7495937SJames Smart * extension size 2799c7495937SJames Smart */ 2800c7495937SJames Smart if (receive_length > MAILBOX_EXT_SIZE) { 2801c7495937SJames Smart rc = -ERANGE; 2802c7495937SJames Smart goto job_done; 2803c7495937SJames Smart } 2804c7495937SJames Smart 2805c7495937SJames Smart /* mode zero uses a bde like biu diags command */ 2806c7495937SJames Smart if (mode == 0) { 2807c7495937SJames Smart 2808c7495937SJames Smart /* rebuild the command for sli4 using our own buffers 2809c7495937SJames Smart * like we do for biu diags 2810c7495937SJames Smart */ 2811c7495937SJames Smart 2812c7495937SJames Smart rxbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); 2813c7495937SJames Smart if (!rxbmp) { 2814c7495937SJames Smart rc = -ENOMEM; 2815c7495937SJames Smart goto job_done; 2816c7495937SJames Smart } 2817c7495937SJames Smart 2818c7495937SJames Smart rxbmp->virt = lpfc_mbuf_alloc(phba, 0, &rxbmp->phys); 2819c7495937SJames Smart rxbpl = (struct ulp_bde64 *) rxbmp->virt; 2820c7495937SJames Smart if (rxbpl) { 2821c7495937SJames Smart INIT_LIST_HEAD(&rxbmp->list); 2822c7495937SJames Smart dmp = diag_cmd_data_alloc(phba, rxbpl, 2823c7495937SJames Smart receive_length, 0); 2824c7495937SJames Smart } 2825c7495937SJames Smart 2826c7495937SJames Smart if (!dmp) { 2827c7495937SJames Smart rc = -ENOMEM; 2828c7495937SJames Smart goto job_done; 2829c7495937SJames Smart } 2830c7495937SJames Smart 2831c7495937SJames Smart INIT_LIST_HEAD(&dmp->dma.list); 2832c7495937SJames Smart pmb->un.varWords[3] = putPaddrLow(dmp->dma.phys); 2833c7495937SJames Smart pmb->un.varWords[4] = putPaddrHigh(dmp->dma.phys); 2834c7495937SJames Smart } 2835c7495937SJames Smart } else if (phba->sli_rev == LPFC_SLI_REV4) { 2836c7495937SJames Smart if (pmb->mbxCommand == MBX_DUMP_MEMORY) { 2837c7495937SJames Smart /* rebuild the command for sli4 using our own buffers 2838c7495937SJames Smart * like we do for biu diags 2839c7495937SJames Smart */ 2840c7495937SJames Smart uint32_t receive_length = pmb->un.varWords[2]; 2841c7495937SJames Smart /* receive length cannot be greater than mailbox 2842c7495937SJames Smart * extension size 2843c7495937SJames Smart */ 2844c7495937SJames Smart if ((receive_length == 0) || 2845c7495937SJames Smart (receive_length > MAILBOX_EXT_SIZE)) { 2846c7495937SJames Smart rc = -ERANGE; 2847c7495937SJames Smart goto job_done; 2848c7495937SJames Smart } 2849c7495937SJames Smart 2850c7495937SJames Smart rxbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); 2851c7495937SJames Smart if (!rxbmp) { 2852c7495937SJames Smart rc = -ENOMEM; 2853c7495937SJames Smart goto job_done; 2854c7495937SJames Smart } 2855c7495937SJames Smart 2856c7495937SJames Smart rxbmp->virt = lpfc_mbuf_alloc(phba, 0, &rxbmp->phys); 2857c7495937SJames Smart if (!rxbmp->virt) { 2858c7495937SJames Smart rc = -ENOMEM; 2859c7495937SJames Smart goto job_done; 2860c7495937SJames Smart } 2861c7495937SJames Smart 2862c7495937SJames Smart INIT_LIST_HEAD(&rxbmp->list); 2863c7495937SJames Smart rxbpl = (struct ulp_bde64 *) rxbmp->virt; 2864c7495937SJames Smart dmp = diag_cmd_data_alloc(phba, rxbpl, receive_length, 2865c7495937SJames Smart 0); 2866c7495937SJames Smart if (!dmp) { 2867c7495937SJames Smart rc = -ENOMEM; 2868c7495937SJames Smart goto job_done; 2869c7495937SJames Smart } 2870c7495937SJames Smart 2871c7495937SJames Smart INIT_LIST_HEAD(&dmp->dma.list); 2872c7495937SJames Smart pmb->un.varWords[3] = putPaddrLow(dmp->dma.phys); 2873c7495937SJames Smart pmb->un.varWords[4] = putPaddrHigh(dmp->dma.phys); 2874c7495937SJames Smart } else if ((pmb->mbxCommand == MBX_UPDATE_CFG) && 2875c7495937SJames Smart pmb->un.varUpdateCfg.co) { 2876c7495937SJames Smart struct ulp_bde64 *bde = 2877c7495937SJames Smart (struct ulp_bde64 *)&pmb->un.varWords[4]; 2878c7495937SJames Smart 2879c7495937SJames Smart /* bde size cannot be greater than mailbox ext size */ 2880c7495937SJames Smart if (bde->tus.f.bdeSize > MAILBOX_EXT_SIZE) { 2881c7495937SJames Smart rc = -ERANGE; 2882c7495937SJames Smart goto job_done; 2883c7495937SJames Smart } 2884c7495937SJames Smart 2885c7495937SJames Smart rxbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); 2886c7495937SJames Smart if (!rxbmp) { 2887c7495937SJames Smart rc = -ENOMEM; 2888c7495937SJames Smart goto job_done; 2889c7495937SJames Smart } 2890c7495937SJames Smart 2891c7495937SJames Smart rxbmp->virt = lpfc_mbuf_alloc(phba, 0, &rxbmp->phys); 2892c7495937SJames Smart if (!rxbmp->virt) { 2893c7495937SJames Smart rc = -ENOMEM; 2894c7495937SJames Smart goto job_done; 2895c7495937SJames Smart } 2896c7495937SJames Smart 2897c7495937SJames Smart INIT_LIST_HEAD(&rxbmp->list); 2898c7495937SJames Smart rxbpl = (struct ulp_bde64 *) rxbmp->virt; 2899c7495937SJames Smart dmp = diag_cmd_data_alloc(phba, rxbpl, 2900c7495937SJames Smart bde->tus.f.bdeSize, 0); 2901c7495937SJames Smart if (!dmp) { 2902c7495937SJames Smart rc = -ENOMEM; 2903c7495937SJames Smart goto job_done; 2904c7495937SJames Smart } 2905c7495937SJames Smart 2906c7495937SJames Smart INIT_LIST_HEAD(&dmp->dma.list); 2907c7495937SJames Smart bde->addrHigh = putPaddrHigh(dmp->dma.phys); 2908c7495937SJames Smart bde->addrLow = putPaddrLow(dmp->dma.phys); 2909c7495937SJames Smart 2910c7495937SJames Smart /* copy the transmit data found in the mailbox 2911c7495937SJames Smart * extension area 2912c7495937SJames Smart */ 2913c7495937SJames Smart from = (uint8_t *)mb; 2914c7495937SJames Smart from += sizeof(MAILBOX_t); 2915c7495937SJames Smart memcpy((uint8_t *)dmp->dma.virt, from, 2916c7495937SJames Smart bde->tus.f.bdeSize); 2917c7495937SJames Smart } 2918c7495937SJames Smart } 2919c7495937SJames Smart 29207a470277SJames Smart dd_data->context_un.mbox.rxbmp = rxbmp; 29217a470277SJames Smart dd_data->context_un.mbox.dmp = dmp; 29223b5dd52aSJames Smart 29233b5dd52aSJames Smart /* setup wake call as IOCB callback */ 29243b5dd52aSJames Smart pmboxq->mbox_cmpl = lpfc_bsg_wake_mbox_wait; 29257a470277SJames Smart 29263b5dd52aSJames Smart /* setup context field to pass wait_queue pointer to wake function */ 29273b5dd52aSJames Smart pmboxq->context1 = dd_data; 29283b5dd52aSJames Smart dd_data->type = TYPE_MBOX; 29293b5dd52aSJames Smart dd_data->context_un.mbox.pmboxq = pmboxq; 29303b5dd52aSJames Smart dd_data->context_un.mbox.mb = mb; 29313b5dd52aSJames Smart dd_data->context_un.mbox.set_job = job; 29327a470277SJames Smart dd_data->context_un.mbox.ext = ext; 29337a470277SJames Smart dd_data->context_un.mbox.mbOffset = mbox_req->mbOffset; 29347a470277SJames Smart dd_data->context_un.mbox.inExtWLen = mbox_req->inExtWLen; 2935c7495937SJames Smart dd_data->context_un.mbox.outExtWLen = mbox_req->outExtWLen; 29363b5dd52aSJames Smart job->dd_data = dd_data; 29377a470277SJames Smart 29387a470277SJames Smart if ((vport->fc_flag & FC_OFFLINE_MODE) || 29397a470277SJames Smart (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE))) { 29407a470277SJames Smart rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL); 29417a470277SJames Smart if (rc != MBX_SUCCESS) { 29427a470277SJames Smart rc = (rc == MBX_TIMEOUT) ? -ETIME : -ENODEV; 29437a470277SJames Smart goto job_done; 29443b5dd52aSJames Smart } 29453b5dd52aSJames Smart 29467a470277SJames Smart /* job finished, copy the data */ 29477a470277SJames Smart memcpy(mb, pmb, sizeof(*pmb)); 29487a470277SJames Smart job->reply->reply_payload_rcv_len = 29497a470277SJames Smart sg_copy_from_buffer(job->reply_payload.sg_list, 29507a470277SJames Smart job->reply_payload.sg_cnt, 29517a470277SJames Smart mb, size); 29527a470277SJames Smart /* not waiting mbox already done */ 29537a470277SJames Smart rc = 0; 29547a470277SJames Smart goto job_done; 29557a470277SJames Smart } 29567a470277SJames Smart 29577a470277SJames Smart rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT); 29587a470277SJames Smart if ((rc == MBX_SUCCESS) || (rc == MBX_BUSY)) 29597a470277SJames Smart return 1; /* job started */ 29607a470277SJames Smart 29617a470277SJames Smart job_done: 29627a470277SJames Smart /* common exit for error or job completed inline */ 29637a470277SJames Smart kfree(mb); 29647a470277SJames Smart if (pmboxq) 29657a470277SJames Smart mempool_free(pmboxq, phba->mbox_mem_pool); 29667a470277SJames Smart kfree(ext); 29677a470277SJames Smart if (dmp) { 29687a470277SJames Smart dma_free_coherent(&phba->pcidev->dev, 29697a470277SJames Smart dmp->size, dmp->dma.virt, 29707a470277SJames Smart dmp->dma.phys); 29717a470277SJames Smart kfree(dmp); 29727a470277SJames Smart } 29737a470277SJames Smart if (rxbmp) { 29747a470277SJames Smart lpfc_mbuf_free(phba, rxbmp->virt, rxbmp->phys); 29757a470277SJames Smart kfree(rxbmp); 29767a470277SJames Smart } 29777a470277SJames Smart kfree(dd_data); 29787a470277SJames Smart 29797a470277SJames Smart return rc; 29803b5dd52aSJames Smart } 29813b5dd52aSJames Smart 29823b5dd52aSJames Smart /** 29833b5dd52aSJames Smart * lpfc_bsg_mbox_cmd - process an fc bsg LPFC_BSG_VENDOR_MBOX command 29843b5dd52aSJames Smart * @job: MBOX fc_bsg_job for LPFC_BSG_VENDOR_MBOX. 29853b5dd52aSJames Smart **/ 29863b5dd52aSJames Smart static int 29873b5dd52aSJames Smart lpfc_bsg_mbox_cmd(struct fc_bsg_job *job) 29883b5dd52aSJames Smart { 29893b5dd52aSJames Smart struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; 29903b5dd52aSJames Smart struct lpfc_hba *phba = vport->phba; 29913b5dd52aSJames Smart int rc = 0; 29923b5dd52aSJames Smart 29933b5dd52aSJames Smart /* in case no data is transferred */ 29943b5dd52aSJames Smart job->reply->reply_payload_rcv_len = 0; 29953b5dd52aSJames Smart if (job->request_len < 29963b5dd52aSJames Smart sizeof(struct fc_bsg_request) + sizeof(struct dfc_mbox_req)) { 29973b5dd52aSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 29983b5dd52aSJames Smart "2737 Received MBOX_REQ request below " 29993b5dd52aSJames Smart "minimum size\n"); 30003b5dd52aSJames Smart rc = -EINVAL; 30013b5dd52aSJames Smart goto job_error; 30023b5dd52aSJames Smart } 30033b5dd52aSJames Smart 300449198b37SJames Smart if (job->request_payload.payload_len != BSG_MBOX_SIZE) { 30053b5dd52aSJames Smart rc = -EINVAL; 30063b5dd52aSJames Smart goto job_error; 30073b5dd52aSJames Smart } 30083b5dd52aSJames Smart 300949198b37SJames Smart if (job->reply_payload.payload_len != BSG_MBOX_SIZE) { 30107a470277SJames Smart rc = -EINVAL; 30117a470277SJames Smart goto job_error; 30127a470277SJames Smart } 30137a470277SJames Smart 30143b5dd52aSJames Smart if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO) { 30153b5dd52aSJames Smart rc = -EAGAIN; 30163b5dd52aSJames Smart goto job_error; 30173b5dd52aSJames Smart } 30183b5dd52aSJames Smart 30193b5dd52aSJames Smart rc = lpfc_bsg_issue_mbox(phba, job, vport); 30203b5dd52aSJames Smart 30213b5dd52aSJames Smart job_error: 30223b5dd52aSJames Smart if (rc == 0) { 30233b5dd52aSJames Smart /* job done */ 30243b5dd52aSJames Smart job->reply->result = 0; 30253b5dd52aSJames Smart job->dd_data = NULL; 30263b5dd52aSJames Smart job->job_done(job); 30273b5dd52aSJames Smart } else if (rc == 1) 30283b5dd52aSJames Smart /* job submitted, will complete later*/ 30293b5dd52aSJames Smart rc = 0; /* return zero, no error */ 30303b5dd52aSJames Smart else { 30313b5dd52aSJames Smart /* some error occurred */ 30323b5dd52aSJames Smart job->reply->result = rc; 30333b5dd52aSJames Smart job->dd_data = NULL; 30343b5dd52aSJames Smart } 30353b5dd52aSJames Smart 30363b5dd52aSJames Smart return rc; 30373b5dd52aSJames Smart } 30383b5dd52aSJames Smart 30393b5dd52aSJames Smart /** 3040e2aed29fSJames Smart * lpfc_bsg_menlo_cmd_cmp - lpfc_menlo_cmd completion handler 3041e2aed29fSJames Smart * @phba: Pointer to HBA context object. 3042e2aed29fSJames Smart * @cmdiocbq: Pointer to command iocb. 3043e2aed29fSJames Smart * @rspiocbq: Pointer to response iocb. 3044e2aed29fSJames Smart * 3045e2aed29fSJames Smart * This function is the completion handler for iocbs issued using 3046e2aed29fSJames Smart * lpfc_menlo_cmd function. This function is called by the 3047e2aed29fSJames Smart * ring event handler function without any lock held. This function 3048e2aed29fSJames Smart * can be called from both worker thread context and interrupt 3049e2aed29fSJames Smart * context. This function also can be called from another thread which 3050e2aed29fSJames Smart * cleans up the SLI layer objects. 3051e2aed29fSJames Smart * This function copies the contents of the response iocb to the 3052e2aed29fSJames Smart * response iocb memory object provided by the caller of 3053e2aed29fSJames Smart * lpfc_sli_issue_iocb_wait and then wakes up the thread which 3054e2aed29fSJames Smart * sleeps for the iocb completion. 3055e2aed29fSJames Smart **/ 3056e2aed29fSJames Smart static void 3057e2aed29fSJames Smart lpfc_bsg_menlo_cmd_cmp(struct lpfc_hba *phba, 3058e2aed29fSJames Smart struct lpfc_iocbq *cmdiocbq, 3059e2aed29fSJames Smart struct lpfc_iocbq *rspiocbq) 3060e2aed29fSJames Smart { 3061e2aed29fSJames Smart struct bsg_job_data *dd_data; 3062e2aed29fSJames Smart struct fc_bsg_job *job; 3063e2aed29fSJames Smart IOCB_t *rsp; 3064e2aed29fSJames Smart struct lpfc_dmabuf *bmp; 3065e2aed29fSJames Smart struct lpfc_bsg_menlo *menlo; 3066e2aed29fSJames Smart unsigned long flags; 3067e2aed29fSJames Smart struct menlo_response *menlo_resp; 3068e2aed29fSJames Smart int rc = 0; 3069e2aed29fSJames Smart 3070e2aed29fSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 3071e2aed29fSJames Smart dd_data = cmdiocbq->context1; 3072e2aed29fSJames Smart if (!dd_data) { 3073e2aed29fSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 3074e2aed29fSJames Smart return; 3075e2aed29fSJames Smart } 3076e2aed29fSJames Smart 3077e2aed29fSJames Smart menlo = &dd_data->context_un.menlo; 3078e2aed29fSJames Smart job = menlo->set_job; 3079e2aed29fSJames Smart job->dd_data = NULL; /* so timeout handler does not reply */ 3080e2aed29fSJames Smart 3081e2aed29fSJames Smart spin_lock_irqsave(&phba->hbalock, flags); 3082e2aed29fSJames Smart cmdiocbq->iocb_flag |= LPFC_IO_WAKE; 3083e2aed29fSJames Smart if (cmdiocbq->context2 && rspiocbq) 3084e2aed29fSJames Smart memcpy(&((struct lpfc_iocbq *)cmdiocbq->context2)->iocb, 3085e2aed29fSJames Smart &rspiocbq->iocb, sizeof(IOCB_t)); 3086e2aed29fSJames Smart spin_unlock_irqrestore(&phba->hbalock, flags); 3087e2aed29fSJames Smart 3088e2aed29fSJames Smart bmp = menlo->bmp; 3089e2aed29fSJames Smart rspiocbq = menlo->rspiocbq; 3090e2aed29fSJames Smart rsp = &rspiocbq->iocb; 3091e2aed29fSJames Smart 3092e2aed29fSJames Smart pci_unmap_sg(phba->pcidev, job->request_payload.sg_list, 3093e2aed29fSJames Smart job->request_payload.sg_cnt, DMA_TO_DEVICE); 3094e2aed29fSJames Smart pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list, 3095e2aed29fSJames Smart job->reply_payload.sg_cnt, DMA_FROM_DEVICE); 3096e2aed29fSJames Smart 3097e2aed29fSJames Smart /* always return the xri, this would be used in the case 3098e2aed29fSJames Smart * of a menlo download to allow the data to be sent as a continuation 3099e2aed29fSJames Smart * of the exchange. 3100e2aed29fSJames Smart */ 3101e2aed29fSJames Smart menlo_resp = (struct menlo_response *) 3102e2aed29fSJames Smart job->reply->reply_data.vendor_reply.vendor_rsp; 3103e2aed29fSJames Smart menlo_resp->xri = rsp->ulpContext; 3104e2aed29fSJames Smart if (rsp->ulpStatus) { 3105e2aed29fSJames Smart if (rsp->ulpStatus == IOSTAT_LOCAL_REJECT) { 3106e2aed29fSJames Smart switch (rsp->un.ulpWord[4] & 0xff) { 3107e2aed29fSJames Smart case IOERR_SEQUENCE_TIMEOUT: 3108e2aed29fSJames Smart rc = -ETIMEDOUT; 3109e2aed29fSJames Smart break; 3110e2aed29fSJames Smart case IOERR_INVALID_RPI: 3111e2aed29fSJames Smart rc = -EFAULT; 3112e2aed29fSJames Smart break; 3113e2aed29fSJames Smart default: 3114e2aed29fSJames Smart rc = -EACCES; 3115e2aed29fSJames Smart break; 3116e2aed29fSJames Smart } 3117e2aed29fSJames Smart } else 3118e2aed29fSJames Smart rc = -EACCES; 3119e2aed29fSJames Smart } else 3120e2aed29fSJames Smart job->reply->reply_payload_rcv_len = 3121e2aed29fSJames Smart rsp->un.genreq64.bdl.bdeSize; 3122e2aed29fSJames Smart 3123e2aed29fSJames Smart lpfc_mbuf_free(phba, bmp->virt, bmp->phys); 3124e2aed29fSJames Smart lpfc_sli_release_iocbq(phba, rspiocbq); 3125e2aed29fSJames Smart lpfc_sli_release_iocbq(phba, cmdiocbq); 3126e2aed29fSJames Smart kfree(bmp); 3127e2aed29fSJames Smart kfree(dd_data); 3128e2aed29fSJames Smart /* make error code available to userspace */ 3129e2aed29fSJames Smart job->reply->result = rc; 3130e2aed29fSJames Smart /* complete the job back to userspace */ 3131e2aed29fSJames Smart job->job_done(job); 3132e2aed29fSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 3133e2aed29fSJames Smart return; 3134e2aed29fSJames Smart } 3135e2aed29fSJames Smart 3136e2aed29fSJames Smart /** 3137e2aed29fSJames Smart * lpfc_menlo_cmd - send an ioctl for menlo hardware 3138e2aed29fSJames Smart * @job: fc_bsg_job to handle 3139e2aed29fSJames Smart * 3140e2aed29fSJames Smart * This function issues a gen request 64 CR ioctl for all menlo cmd requests, 3141e2aed29fSJames Smart * all the command completions will return the xri for the command. 3142e2aed29fSJames Smart * For menlo data requests a gen request 64 CX is used to continue the exchange 3143e2aed29fSJames Smart * supplied in the menlo request header xri field. 3144e2aed29fSJames Smart **/ 3145e2aed29fSJames Smart static int 3146e2aed29fSJames Smart lpfc_menlo_cmd(struct fc_bsg_job *job) 3147e2aed29fSJames Smart { 3148e2aed29fSJames Smart struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; 3149e2aed29fSJames Smart struct lpfc_hba *phba = vport->phba; 3150e2aed29fSJames Smart struct lpfc_iocbq *cmdiocbq, *rspiocbq; 3151e2aed29fSJames Smart IOCB_t *cmd, *rsp; 3152e2aed29fSJames Smart int rc = 0; 3153e2aed29fSJames Smart struct menlo_command *menlo_cmd; 3154e2aed29fSJames Smart struct menlo_response *menlo_resp; 3155e2aed29fSJames Smart struct lpfc_dmabuf *bmp = NULL; 3156e2aed29fSJames Smart int request_nseg; 3157e2aed29fSJames Smart int reply_nseg; 3158e2aed29fSJames Smart struct scatterlist *sgel = NULL; 3159e2aed29fSJames Smart int numbde; 3160e2aed29fSJames Smart dma_addr_t busaddr; 3161e2aed29fSJames Smart struct bsg_job_data *dd_data; 3162e2aed29fSJames Smart struct ulp_bde64 *bpl = NULL; 3163e2aed29fSJames Smart 3164e2aed29fSJames Smart /* in case no data is returned return just the return code */ 3165e2aed29fSJames Smart job->reply->reply_payload_rcv_len = 0; 3166e2aed29fSJames Smart 3167e2aed29fSJames Smart if (job->request_len < 3168e2aed29fSJames Smart sizeof(struct fc_bsg_request) + 3169e2aed29fSJames Smart sizeof(struct menlo_command)) { 3170e2aed29fSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 3171e2aed29fSJames Smart "2784 Received MENLO_CMD request below " 3172e2aed29fSJames Smart "minimum size\n"); 3173e2aed29fSJames Smart rc = -ERANGE; 3174e2aed29fSJames Smart goto no_dd_data; 3175e2aed29fSJames Smart } 3176e2aed29fSJames Smart 3177e2aed29fSJames Smart if (job->reply_len < 3178e2aed29fSJames Smart sizeof(struct fc_bsg_request) + sizeof(struct menlo_response)) { 3179e2aed29fSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 3180e2aed29fSJames Smart "2785 Received MENLO_CMD reply below " 3181e2aed29fSJames Smart "minimum size\n"); 3182e2aed29fSJames Smart rc = -ERANGE; 3183e2aed29fSJames Smart goto no_dd_data; 3184e2aed29fSJames Smart } 3185e2aed29fSJames Smart 3186e2aed29fSJames Smart if (!(phba->menlo_flag & HBA_MENLO_SUPPORT)) { 3187e2aed29fSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 3188e2aed29fSJames Smart "2786 Adapter does not support menlo " 3189e2aed29fSJames Smart "commands\n"); 3190e2aed29fSJames Smart rc = -EPERM; 3191e2aed29fSJames Smart goto no_dd_data; 3192e2aed29fSJames Smart } 3193e2aed29fSJames Smart 3194e2aed29fSJames Smart menlo_cmd = (struct menlo_command *) 3195e2aed29fSJames Smart job->request->rqst_data.h_vendor.vendor_cmd; 3196e2aed29fSJames Smart 3197e2aed29fSJames Smart menlo_resp = (struct menlo_response *) 3198e2aed29fSJames Smart job->reply->reply_data.vendor_reply.vendor_rsp; 3199e2aed29fSJames Smart 3200e2aed29fSJames Smart /* allocate our bsg tracking structure */ 3201e2aed29fSJames Smart dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL); 3202e2aed29fSJames Smart if (!dd_data) { 3203e2aed29fSJames Smart lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, 3204e2aed29fSJames Smart "2787 Failed allocation of dd_data\n"); 3205e2aed29fSJames Smart rc = -ENOMEM; 3206e2aed29fSJames Smart goto no_dd_data; 3207e2aed29fSJames Smart } 3208e2aed29fSJames Smart 3209e2aed29fSJames Smart bmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); 3210e2aed29fSJames Smart if (!bmp) { 3211e2aed29fSJames Smart rc = -ENOMEM; 3212e2aed29fSJames Smart goto free_dd; 3213e2aed29fSJames Smart } 3214e2aed29fSJames Smart 3215e2aed29fSJames Smart cmdiocbq = lpfc_sli_get_iocbq(phba); 3216e2aed29fSJames Smart if (!cmdiocbq) { 3217e2aed29fSJames Smart rc = -ENOMEM; 3218e2aed29fSJames Smart goto free_bmp; 3219e2aed29fSJames Smart } 3220e2aed29fSJames Smart 3221e2aed29fSJames Smart rspiocbq = lpfc_sli_get_iocbq(phba); 3222e2aed29fSJames Smart if (!rspiocbq) { 3223e2aed29fSJames Smart rc = -ENOMEM; 3224e2aed29fSJames Smart goto free_cmdiocbq; 3225e2aed29fSJames Smart } 3226e2aed29fSJames Smart 3227e2aed29fSJames Smart rsp = &rspiocbq->iocb; 3228e2aed29fSJames Smart 3229e2aed29fSJames Smart bmp->virt = lpfc_mbuf_alloc(phba, 0, &bmp->phys); 3230e2aed29fSJames Smart if (!bmp->virt) { 3231e2aed29fSJames Smart rc = -ENOMEM; 3232e2aed29fSJames Smart goto free_rspiocbq; 3233e2aed29fSJames Smart } 3234e2aed29fSJames Smart 3235e2aed29fSJames Smart INIT_LIST_HEAD(&bmp->list); 3236e2aed29fSJames Smart bpl = (struct ulp_bde64 *) bmp->virt; 3237e2aed29fSJames Smart request_nseg = pci_map_sg(phba->pcidev, job->request_payload.sg_list, 3238e2aed29fSJames Smart job->request_payload.sg_cnt, DMA_TO_DEVICE); 3239e2aed29fSJames Smart for_each_sg(job->request_payload.sg_list, sgel, request_nseg, numbde) { 3240e2aed29fSJames Smart busaddr = sg_dma_address(sgel); 3241e2aed29fSJames Smart bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64; 3242e2aed29fSJames Smart bpl->tus.f.bdeSize = sg_dma_len(sgel); 3243e2aed29fSJames Smart bpl->tus.w = cpu_to_le32(bpl->tus.w); 3244e2aed29fSJames Smart bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr)); 3245e2aed29fSJames Smart bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr)); 3246e2aed29fSJames Smart bpl++; 3247e2aed29fSJames Smart } 3248e2aed29fSJames Smart 3249e2aed29fSJames Smart reply_nseg = pci_map_sg(phba->pcidev, job->reply_payload.sg_list, 3250e2aed29fSJames Smart job->reply_payload.sg_cnt, DMA_FROM_DEVICE); 3251e2aed29fSJames Smart for_each_sg(job->reply_payload.sg_list, sgel, reply_nseg, numbde) { 3252e2aed29fSJames Smart busaddr = sg_dma_address(sgel); 3253e2aed29fSJames Smart bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I; 3254e2aed29fSJames Smart bpl->tus.f.bdeSize = sg_dma_len(sgel); 3255e2aed29fSJames Smart bpl->tus.w = cpu_to_le32(bpl->tus.w); 3256e2aed29fSJames Smart bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr)); 3257e2aed29fSJames Smart bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr)); 3258e2aed29fSJames Smart bpl++; 3259e2aed29fSJames Smart } 3260e2aed29fSJames Smart 3261e2aed29fSJames Smart cmd = &cmdiocbq->iocb; 3262e2aed29fSJames Smart cmd->un.genreq64.bdl.ulpIoTag32 = 0; 3263e2aed29fSJames Smart cmd->un.genreq64.bdl.addrHigh = putPaddrHigh(bmp->phys); 3264e2aed29fSJames Smart cmd->un.genreq64.bdl.addrLow = putPaddrLow(bmp->phys); 3265e2aed29fSJames Smart cmd->un.genreq64.bdl.bdeFlags = BUFF_TYPE_BLP_64; 3266e2aed29fSJames Smart cmd->un.genreq64.bdl.bdeSize = 3267e2aed29fSJames Smart (request_nseg + reply_nseg) * sizeof(struct ulp_bde64); 3268e2aed29fSJames Smart cmd->un.genreq64.w5.hcsw.Fctl = (SI | LA); 3269e2aed29fSJames Smart cmd->un.genreq64.w5.hcsw.Dfctl = 0; 3270e2aed29fSJames Smart cmd->un.genreq64.w5.hcsw.Rctl = FC_RCTL_DD_UNSOL_CMD; 3271e2aed29fSJames Smart cmd->un.genreq64.w5.hcsw.Type = MENLO_TRANSPORT_TYPE; /* 0xfe */ 3272e2aed29fSJames Smart cmd->ulpBdeCount = 1; 3273e2aed29fSJames Smart cmd->ulpClass = CLASS3; 3274e2aed29fSJames Smart cmd->ulpOwner = OWN_CHIP; 3275e2aed29fSJames Smart cmd->ulpLe = 1; /* Limited Edition */ 3276e2aed29fSJames Smart cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC; 3277e2aed29fSJames Smart cmdiocbq->vport = phba->pport; 3278e2aed29fSJames Smart /* We want the firmware to timeout before we do */ 3279e2aed29fSJames Smart cmd->ulpTimeout = MENLO_TIMEOUT - 5; 3280e2aed29fSJames Smart cmdiocbq->context3 = bmp; 3281e2aed29fSJames Smart cmdiocbq->context2 = rspiocbq; 3282e2aed29fSJames Smart cmdiocbq->iocb_cmpl = lpfc_bsg_menlo_cmd_cmp; 3283e2aed29fSJames Smart cmdiocbq->context1 = dd_data; 3284e2aed29fSJames Smart cmdiocbq->context2 = rspiocbq; 3285e2aed29fSJames Smart if (menlo_cmd->cmd == LPFC_BSG_VENDOR_MENLO_CMD) { 3286e2aed29fSJames Smart cmd->ulpCommand = CMD_GEN_REQUEST64_CR; 3287e2aed29fSJames Smart cmd->ulpPU = MENLO_PU; /* 3 */ 3288e2aed29fSJames Smart cmd->un.ulpWord[4] = MENLO_DID; /* 0x0000FC0E */ 3289e2aed29fSJames Smart cmd->ulpContext = MENLO_CONTEXT; /* 0 */ 3290e2aed29fSJames Smart } else { 3291e2aed29fSJames Smart cmd->ulpCommand = CMD_GEN_REQUEST64_CX; 3292e2aed29fSJames Smart cmd->ulpPU = 1; 3293e2aed29fSJames Smart cmd->un.ulpWord[4] = 0; 3294e2aed29fSJames Smart cmd->ulpContext = menlo_cmd->xri; 3295e2aed29fSJames Smart } 3296e2aed29fSJames Smart 3297e2aed29fSJames Smart dd_data->type = TYPE_MENLO; 3298e2aed29fSJames Smart dd_data->context_un.menlo.cmdiocbq = cmdiocbq; 3299e2aed29fSJames Smart dd_data->context_un.menlo.rspiocbq = rspiocbq; 3300e2aed29fSJames Smart dd_data->context_un.menlo.set_job = job; 3301e2aed29fSJames Smart dd_data->context_un.menlo.bmp = bmp; 3302e2aed29fSJames Smart 3303e2aed29fSJames Smart rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq, 3304e2aed29fSJames Smart MENLO_TIMEOUT - 5); 3305e2aed29fSJames Smart if (rc == IOCB_SUCCESS) 3306e2aed29fSJames Smart return 0; /* done for now */ 3307e2aed29fSJames Smart 3308e2aed29fSJames Smart /* iocb failed so cleanup */ 3309e2aed29fSJames Smart pci_unmap_sg(phba->pcidev, job->request_payload.sg_list, 3310e2aed29fSJames Smart job->request_payload.sg_cnt, DMA_TO_DEVICE); 3311e2aed29fSJames Smart pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list, 3312e2aed29fSJames Smart job->reply_payload.sg_cnt, DMA_FROM_DEVICE); 3313e2aed29fSJames Smart 3314e2aed29fSJames Smart lpfc_mbuf_free(phba, bmp->virt, bmp->phys); 3315e2aed29fSJames Smart 3316e2aed29fSJames Smart free_rspiocbq: 3317e2aed29fSJames Smart lpfc_sli_release_iocbq(phba, rspiocbq); 3318e2aed29fSJames Smart free_cmdiocbq: 3319e2aed29fSJames Smart lpfc_sli_release_iocbq(phba, cmdiocbq); 3320e2aed29fSJames Smart free_bmp: 3321e2aed29fSJames Smart kfree(bmp); 3322e2aed29fSJames Smart free_dd: 3323e2aed29fSJames Smart kfree(dd_data); 3324e2aed29fSJames Smart no_dd_data: 3325e2aed29fSJames Smart /* make error code available to userspace */ 3326e2aed29fSJames Smart job->reply->result = rc; 3327e2aed29fSJames Smart job->dd_data = NULL; 3328e2aed29fSJames Smart return rc; 3329e2aed29fSJames Smart } 3330e2aed29fSJames Smart /** 3331f1c3b0fcSJames Smart * lpfc_bsg_hst_vendor - process a vendor-specific fc_bsg_job 3332f1c3b0fcSJames Smart * @job: fc_bsg_job to handle 33333b5dd52aSJames Smart **/ 3334f1c3b0fcSJames Smart static int 3335f1c3b0fcSJames Smart lpfc_bsg_hst_vendor(struct fc_bsg_job *job) 3336f1c3b0fcSJames Smart { 3337f1c3b0fcSJames Smart int command = job->request->rqst_data.h_vendor.vendor_cmd[0]; 33384cc0e56eSJames Smart int rc; 3339f1c3b0fcSJames Smart 3340f1c3b0fcSJames Smart switch (command) { 3341f1c3b0fcSJames Smart case LPFC_BSG_VENDOR_SET_CT_EVENT: 33424cc0e56eSJames Smart rc = lpfc_bsg_hba_set_event(job); 3343f1c3b0fcSJames Smart break; 3344f1c3b0fcSJames Smart case LPFC_BSG_VENDOR_GET_CT_EVENT: 33454cc0e56eSJames Smart rc = lpfc_bsg_hba_get_event(job); 3346f1c3b0fcSJames Smart break; 33473b5dd52aSJames Smart case LPFC_BSG_VENDOR_SEND_MGMT_RESP: 33483b5dd52aSJames Smart rc = lpfc_bsg_send_mgmt_rsp(job); 33493b5dd52aSJames Smart break; 33503b5dd52aSJames Smart case LPFC_BSG_VENDOR_DIAG_MODE: 33513b5dd52aSJames Smart rc = lpfc_bsg_diag_mode(job); 33523b5dd52aSJames Smart break; 33533b5dd52aSJames Smart case LPFC_BSG_VENDOR_DIAG_TEST: 33543b5dd52aSJames Smart rc = lpfc_bsg_diag_test(job); 33553b5dd52aSJames Smart break; 33563b5dd52aSJames Smart case LPFC_BSG_VENDOR_GET_MGMT_REV: 33573b5dd52aSJames Smart rc = lpfc_bsg_get_dfc_rev(job); 33583b5dd52aSJames Smart break; 33593b5dd52aSJames Smart case LPFC_BSG_VENDOR_MBOX: 33603b5dd52aSJames Smart rc = lpfc_bsg_mbox_cmd(job); 33613b5dd52aSJames Smart break; 3362e2aed29fSJames Smart case LPFC_BSG_VENDOR_MENLO_CMD: 3363e2aed29fSJames Smart case LPFC_BSG_VENDOR_MENLO_DATA: 3364e2aed29fSJames Smart rc = lpfc_menlo_cmd(job); 3365e2aed29fSJames Smart break; 3366f1c3b0fcSJames Smart default: 33674cc0e56eSJames Smart rc = -EINVAL; 33684cc0e56eSJames Smart job->reply->reply_payload_rcv_len = 0; 33694cc0e56eSJames Smart /* make error code available to userspace */ 33704cc0e56eSJames Smart job->reply->result = rc; 33714cc0e56eSJames Smart break; 3372f1c3b0fcSJames Smart } 33734cc0e56eSJames Smart 33744cc0e56eSJames Smart return rc; 3375f1c3b0fcSJames Smart } 3376f1c3b0fcSJames Smart 3377f1c3b0fcSJames Smart /** 3378f1c3b0fcSJames Smart * lpfc_bsg_request - handle a bsg request from the FC transport 3379f1c3b0fcSJames Smart * @job: fc_bsg_job to handle 33803b5dd52aSJames Smart **/ 3381f1c3b0fcSJames Smart int 3382f1c3b0fcSJames Smart lpfc_bsg_request(struct fc_bsg_job *job) 3383f1c3b0fcSJames Smart { 3384f1c3b0fcSJames Smart uint32_t msgcode; 33854cc0e56eSJames Smart int rc; 3386f1c3b0fcSJames Smart 3387f1c3b0fcSJames Smart msgcode = job->request->msgcode; 3388f1c3b0fcSJames Smart switch (msgcode) { 3389f1c3b0fcSJames Smart case FC_BSG_HST_VENDOR: 3390f1c3b0fcSJames Smart rc = lpfc_bsg_hst_vendor(job); 3391f1c3b0fcSJames Smart break; 3392f1c3b0fcSJames Smart case FC_BSG_RPT_ELS: 3393f1c3b0fcSJames Smart rc = lpfc_bsg_rport_els(job); 3394f1c3b0fcSJames Smart break; 3395f1c3b0fcSJames Smart case FC_BSG_RPT_CT: 33964cc0e56eSJames Smart rc = lpfc_bsg_send_mgmt_cmd(job); 3397f1c3b0fcSJames Smart break; 3398f1c3b0fcSJames Smart default: 33994cc0e56eSJames Smart rc = -EINVAL; 34004cc0e56eSJames Smart job->reply->reply_payload_rcv_len = 0; 34014cc0e56eSJames Smart /* make error code available to userspace */ 34024cc0e56eSJames Smart job->reply->result = rc; 3403f1c3b0fcSJames Smart break; 3404f1c3b0fcSJames Smart } 3405f1c3b0fcSJames Smart 3406f1c3b0fcSJames Smart return rc; 3407f1c3b0fcSJames Smart } 3408f1c3b0fcSJames Smart 3409f1c3b0fcSJames Smart /** 3410f1c3b0fcSJames Smart * lpfc_bsg_timeout - handle timeout of a bsg request from the FC transport 3411f1c3b0fcSJames Smart * @job: fc_bsg_job that has timed out 3412f1c3b0fcSJames Smart * 3413f1c3b0fcSJames Smart * This function just aborts the job's IOCB. The aborted IOCB will return to 3414f1c3b0fcSJames Smart * the waiting function which will handle passing the error back to userspace 34153b5dd52aSJames Smart **/ 3416f1c3b0fcSJames Smart int 3417f1c3b0fcSJames Smart lpfc_bsg_timeout(struct fc_bsg_job *job) 3418f1c3b0fcSJames Smart { 3419f1c3b0fcSJames Smart struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; 3420f1c3b0fcSJames Smart struct lpfc_hba *phba = vport->phba; 34214cc0e56eSJames Smart struct lpfc_iocbq *cmdiocb; 34224cc0e56eSJames Smart struct lpfc_bsg_event *evt; 34234cc0e56eSJames Smart struct lpfc_bsg_iocb *iocb; 34243b5dd52aSJames Smart struct lpfc_bsg_mbox *mbox; 3425e2aed29fSJames Smart struct lpfc_bsg_menlo *menlo; 3426f1c3b0fcSJames Smart struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING]; 34274cc0e56eSJames Smart struct bsg_job_data *dd_data; 34284cc0e56eSJames Smart unsigned long flags; 3429f1c3b0fcSJames Smart 34304cc0e56eSJames Smart spin_lock_irqsave(&phba->ct_ev_lock, flags); 34314cc0e56eSJames Smart dd_data = (struct bsg_job_data *)job->dd_data; 34324cc0e56eSJames Smart /* timeout and completion crossed paths if no dd_data */ 34334cc0e56eSJames Smart if (!dd_data) { 34344cc0e56eSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 34354cc0e56eSJames Smart return 0; 34364cc0e56eSJames Smart } 34374cc0e56eSJames Smart 34384cc0e56eSJames Smart switch (dd_data->type) { 34394cc0e56eSJames Smart case TYPE_IOCB: 34404cc0e56eSJames Smart iocb = &dd_data->context_un.iocb; 34414cc0e56eSJames Smart cmdiocb = iocb->cmdiocbq; 34424cc0e56eSJames Smart /* hint to completion handler that the job timed out */ 34434cc0e56eSJames Smart job->reply->result = -EAGAIN; 34444cc0e56eSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 34454cc0e56eSJames Smart /* this will call our completion handler */ 34464cc0e56eSJames Smart spin_lock_irq(&phba->hbalock); 3447f1c3b0fcSJames Smart lpfc_sli_issue_abort_iotag(phba, pring, cmdiocb); 34484cc0e56eSJames Smart spin_unlock_irq(&phba->hbalock); 34494cc0e56eSJames Smart break; 34504cc0e56eSJames Smart case TYPE_EVT: 34514cc0e56eSJames Smart evt = dd_data->context_un.evt; 34524cc0e56eSJames Smart /* this event has no job anymore */ 34534cc0e56eSJames Smart evt->set_job = NULL; 34544cc0e56eSJames Smart job->dd_data = NULL; 34554cc0e56eSJames Smart job->reply->reply_payload_rcv_len = 0; 34564cc0e56eSJames Smart /* Return -EAGAIN which is our way of signallying the 34574cc0e56eSJames Smart * app to retry. 34584cc0e56eSJames Smart */ 34594cc0e56eSJames Smart job->reply->result = -EAGAIN; 34604cc0e56eSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 34614cc0e56eSJames Smart job->job_done(job); 34624cc0e56eSJames Smart break; 34633b5dd52aSJames Smart case TYPE_MBOX: 34643b5dd52aSJames Smart mbox = &dd_data->context_un.mbox; 34653b5dd52aSJames Smart /* this mbox has no job anymore */ 34663b5dd52aSJames Smart mbox->set_job = NULL; 34673b5dd52aSJames Smart job->dd_data = NULL; 34683b5dd52aSJames Smart job->reply->reply_payload_rcv_len = 0; 34693b5dd52aSJames Smart job->reply->result = -EAGAIN; 34707a470277SJames Smart /* the mbox completion handler can now be run */ 34713b5dd52aSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 34723b5dd52aSJames Smart job->job_done(job); 34733b5dd52aSJames Smart break; 3474e2aed29fSJames Smart case TYPE_MENLO: 3475e2aed29fSJames Smart menlo = &dd_data->context_un.menlo; 3476e2aed29fSJames Smart cmdiocb = menlo->cmdiocbq; 3477e2aed29fSJames Smart /* hint to completion handler that the job timed out */ 3478e2aed29fSJames Smart job->reply->result = -EAGAIN; 3479e2aed29fSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 3480e2aed29fSJames Smart /* this will call our completion handler */ 3481e2aed29fSJames Smart spin_lock_irq(&phba->hbalock); 3482e2aed29fSJames Smart lpfc_sli_issue_abort_iotag(phba, pring, cmdiocb); 3483e2aed29fSJames Smart spin_unlock_irq(&phba->hbalock); 3484e2aed29fSJames Smart break; 34854cc0e56eSJames Smart default: 34864cc0e56eSJames Smart spin_unlock_irqrestore(&phba->ct_ev_lock, flags); 34874cc0e56eSJames Smart break; 34884cc0e56eSJames Smart } 3489f1c3b0fcSJames Smart 34904cc0e56eSJames Smart /* scsi transport fc fc_bsg_job_timeout expects a zero return code, 34914cc0e56eSJames Smart * otherwise an error message will be displayed on the console 34924cc0e56eSJames Smart * so always return success (zero) 34934cc0e56eSJames Smart */ 3494f1c3b0fcSJames Smart return 0; 3495f1c3b0fcSJames Smart } 3496