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