xref: /linux/drivers/scsi/lpfc/lpfc_bsg.c (revision 3417c9574e368f0330637505f00d3814ca8854d2)
1f1c3b0fcSJames Smart /*******************************************************************
2f1c3b0fcSJames Smart  * This file is part of the Emulex Linux Device Driver for         *
3f1c3b0fcSJames Smart  * Fibre Channel Host Bus Adapters.                                *
4ea4044e4SJustin Tee  * Copyright (C) 2017-2024 Broadcom. All Rights Reserved. The term *
54ae2ebdeSJames Smart  * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.     *
6f25e8e79SJames Smart  * Copyright (C) 2009-2015 Emulex.  All rights reserved.           *
7f1c3b0fcSJames Smart  * EMULEX and SLI are trademarks of Emulex.                        *
8d080abe0SJames Smart  * www.broadcom.com                                                *
9f1c3b0fcSJames Smart  *                                                                 *
10f1c3b0fcSJames Smart  * This program is free software; you can redistribute it and/or   *
11f1c3b0fcSJames Smart  * modify it under the terms of version 2 of the GNU General       *
12f1c3b0fcSJames Smart  * Public License as published by the Free Software Foundation.    *
13f1c3b0fcSJames Smart  * This program is distributed in the hope that it will be useful. *
14f1c3b0fcSJames Smart  * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND          *
15f1c3b0fcSJames Smart  * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,  *
16f1c3b0fcSJames Smart  * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE      *
17f1c3b0fcSJames Smart  * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD *
18f1c3b0fcSJames Smart  * TO BE LEGALLY INVALID.  See the GNU General Public License for  *
19f1c3b0fcSJames Smart  * more details, a copy of which can be found in the file COPYING  *
20f1c3b0fcSJames Smart  * included with this package.                                     *
21f1c3b0fcSJames Smart  *******************************************************************/
22f1c3b0fcSJames Smart 
23f1c3b0fcSJames Smart #include <linux/interrupt.h>
24f1c3b0fcSJames Smart #include <linux/mempool.h>
25f1c3b0fcSJames Smart #include <linux/pci.h>
265a0e3ad6STejun Heo #include <linux/slab.h>
27277e76f1SJames Smart #include <linux/delay.h>
287ad20aa9SJames Smart #include <linux/list.h>
29eb340948SJohannes Thumshirn #include <linux/bsg-lib.h>
30d2cc9bcdSJames Smart #include <linux/vmalloc.h>
31f1c3b0fcSJames Smart 
32f1c3b0fcSJames Smart #include <scsi/scsi.h>
33f1c3b0fcSJames Smart #include <scsi/scsi_host.h>
34f1c3b0fcSJames Smart #include <scsi/scsi_transport_fc.h>
35f1c3b0fcSJames Smart #include <scsi/scsi_bsg_fc.h>
366a9c52cfSJames Smart #include <scsi/fc/fc_fs.h>
37f1c3b0fcSJames Smart 
38f1c3b0fcSJames Smart #include "lpfc_hw4.h"
39f1c3b0fcSJames Smart #include "lpfc_hw.h"
40f1c3b0fcSJames Smart #include "lpfc_sli.h"
41f1c3b0fcSJames Smart #include "lpfc_sli4.h"
42f1c3b0fcSJames Smart #include "lpfc_nl.h"
434fede78fSJames Smart #include "lpfc_bsg.h"
44f1c3b0fcSJames Smart #include "lpfc_disc.h"
45f1c3b0fcSJames Smart #include "lpfc_scsi.h"
46f1c3b0fcSJames Smart #include "lpfc.h"
47f1c3b0fcSJames Smart #include "lpfc_logmsg.h"
48f1c3b0fcSJames Smart #include "lpfc_crtn.h"
49b76f2dc9SJames Smart #include "lpfc_debugfs.h"
50f1c3b0fcSJames Smart #include "lpfc_vport.h"
51f1c3b0fcSJames Smart #include "lpfc_version.h"
52f1c3b0fcSJames Smart 
534cc0e56eSJames Smart struct lpfc_bsg_event {
544cc0e56eSJames Smart 	struct list_head node;
554cc0e56eSJames Smart 	struct kref kref;
564cc0e56eSJames Smart 	wait_queue_head_t wq;
574cc0e56eSJames Smart 
584cc0e56eSJames Smart 	/* Event type and waiter identifiers */
594cc0e56eSJames Smart 	uint32_t type_mask;
604cc0e56eSJames Smart 	uint32_t req_id;
614cc0e56eSJames Smart 	uint32_t reg_id;
624cc0e56eSJames Smart 
634cc0e56eSJames Smart 	/* next two flags are here for the auto-delete logic */
644cc0e56eSJames Smart 	unsigned long wait_time_stamp;
654cc0e56eSJames Smart 	int waiting;
664cc0e56eSJames Smart 
674cc0e56eSJames Smart 	/* seen and not seen events */
684cc0e56eSJames Smart 	struct list_head events_to_get;
694cc0e56eSJames Smart 	struct list_head events_to_see;
704cc0e56eSJames Smart 
71a33c4f7bSJames Smart 	/* driver data associated with the job */
72a33c4f7bSJames Smart 	void *dd_data;
734cc0e56eSJames Smart };
744cc0e56eSJames Smart 
754cc0e56eSJames Smart struct lpfc_bsg_iocb {
764cc0e56eSJames Smart 	struct lpfc_iocbq *cmdiocbq;
77a33c4f7bSJames Smart 	struct lpfc_dmabuf *rmp;
784cc0e56eSJames Smart 	struct lpfc_nodelist *ndlp;
794cc0e56eSJames Smart };
804cc0e56eSJames Smart 
813b5dd52aSJames Smart struct lpfc_bsg_mbox {
823b5dd52aSJames Smart 	LPFC_MBOXQ_t *pmboxq;
833b5dd52aSJames Smart 	MAILBOX_t *mb;
847ad20aa9SJames Smart 	struct lpfc_dmabuf *dmabuffers; /* for BIU diags */
857a470277SJames Smart 	uint8_t *ext; /* extended mailbox data */
867a470277SJames Smart 	uint32_t mbOffset; /* from app */
877a470277SJames Smart 	uint32_t inExtWLen; /* from app */
88c7495937SJames Smart 	uint32_t outExtWLen; /* from app */
893b5dd52aSJames Smart };
903b5dd52aSJames Smart 
914cc0e56eSJames Smart #define TYPE_EVT 	1
924cc0e56eSJames Smart #define TYPE_IOCB	2
933b5dd52aSJames Smart #define TYPE_MBOX	3
944cc0e56eSJames Smart struct bsg_job_data {
954cc0e56eSJames Smart 	uint32_t type;
9675cc8cfcSJohannes Thumshirn 	struct bsg_job *set_job; /* job waiting for this iocb to finish */
974cc0e56eSJames Smart 	union {
984cc0e56eSJames Smart 		struct lpfc_bsg_event *evt;
994cc0e56eSJames Smart 		struct lpfc_bsg_iocb iocb;
1003b5dd52aSJames Smart 		struct lpfc_bsg_mbox mbox;
1014cc0e56eSJames Smart 	} context_un;
1024cc0e56eSJames Smart };
1034cc0e56eSJames Smart 
1044cc0e56eSJames Smart struct event_data {
1054cc0e56eSJames Smart 	struct list_head node;
1064cc0e56eSJames Smart 	uint32_t type;
1074cc0e56eSJames Smart 	uint32_t immed_dat;
1084cc0e56eSJames Smart 	void *data;
1094cc0e56eSJames Smart 	uint32_t len;
1104cc0e56eSJames Smart };
1114cc0e56eSJames Smart 
1123b5dd52aSJames Smart #define BUF_SZ_4K 4096
1134cc0e56eSJames Smart #define SLI_CT_ELX_LOOPBACK 0x10
1144cc0e56eSJames Smart 
1154cc0e56eSJames Smart enum ELX_LOOPBACK_CMD {
1164cc0e56eSJames Smart 	ELX_LOOPBACK_XRI_SETUP,
1174cc0e56eSJames Smart 	ELX_LOOPBACK_DATA,
1184cc0e56eSJames Smart };
1194cc0e56eSJames Smart 
1203b5dd52aSJames Smart #define ELX_LOOPBACK_HEADER_SZ \
1213b5dd52aSJames Smart 	(size_t)(&((struct lpfc_sli_ct_request *)NULL)->un)
1223b5dd52aSJames Smart 
1234cc0e56eSJames Smart struct lpfc_dmabufext {
1244cc0e56eSJames Smart 	struct lpfc_dmabuf dma;
1254cc0e56eSJames Smart 	uint32_t size;
1264cc0e56eSJames Smart 	uint32_t flag;
1274cc0e56eSJames Smart };
1284cc0e56eSJames Smart 
129a33c4f7bSJames Smart static void
130a33c4f7bSJames Smart lpfc_free_bsg_buffers(struct lpfc_hba *phba, struct lpfc_dmabuf *mlist)
131a33c4f7bSJames Smart {
132a33c4f7bSJames Smart 	struct lpfc_dmabuf *mlast, *next_mlast;
133a33c4f7bSJames Smart 
134a33c4f7bSJames Smart 	if (mlist) {
135a33c4f7bSJames Smart 		list_for_each_entry_safe(mlast, next_mlast, &mlist->list,
136a33c4f7bSJames Smart 					 list) {
137a33c4f7bSJames Smart 			list_del(&mlast->list);
138bf21c9bbSJustin Tee 			lpfc_mbuf_free(phba, mlast->virt, mlast->phys);
139a33c4f7bSJames Smart 			kfree(mlast);
140a33c4f7bSJames Smart 		}
141a33c4f7bSJames Smart 		lpfc_mbuf_free(phba, mlist->virt, mlist->phys);
142a33c4f7bSJames Smart 		kfree(mlist);
143a33c4f7bSJames Smart 	}
144a33c4f7bSJames Smart 	return;
145a33c4f7bSJames Smart }
146a33c4f7bSJames Smart 
147a33c4f7bSJames Smart static struct lpfc_dmabuf *
148a33c4f7bSJames Smart lpfc_alloc_bsg_buffers(struct lpfc_hba *phba, unsigned int size,
149a33c4f7bSJames Smart 		       int outbound_buffers, struct ulp_bde64 *bpl,
150a33c4f7bSJames Smart 		       int *bpl_entries)
151a33c4f7bSJames Smart {
152a33c4f7bSJames Smart 	struct lpfc_dmabuf *mlist = NULL;
153a33c4f7bSJames Smart 	struct lpfc_dmabuf *mp;
154a33c4f7bSJames Smart 	unsigned int bytes_left = size;
155a33c4f7bSJames Smart 
156a33c4f7bSJames Smart 	/* Verify we can support the size specified */
157a33c4f7bSJames Smart 	if (!size || (size > (*bpl_entries * LPFC_BPL_SIZE)))
158a33c4f7bSJames Smart 		return NULL;
159a33c4f7bSJames Smart 
160a33c4f7bSJames Smart 	/* Determine the number of dma buffers to allocate */
161a33c4f7bSJames Smart 	*bpl_entries = (size % LPFC_BPL_SIZE ? size/LPFC_BPL_SIZE + 1 :
162a33c4f7bSJames Smart 			size/LPFC_BPL_SIZE);
163a33c4f7bSJames Smart 
164a33c4f7bSJames Smart 	/* Allocate dma buffer and place in BPL passed */
165a33c4f7bSJames Smart 	while (bytes_left) {
166a33c4f7bSJames Smart 		/* Allocate dma buffer  */
167a33c4f7bSJames Smart 		mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
168a33c4f7bSJames Smart 		if (!mp) {
169a33c4f7bSJames Smart 			if (mlist)
170a33c4f7bSJames Smart 				lpfc_free_bsg_buffers(phba, mlist);
171a33c4f7bSJames Smart 			return NULL;
172a33c4f7bSJames Smart 		}
173a33c4f7bSJames Smart 
174a33c4f7bSJames Smart 		INIT_LIST_HEAD(&mp->list);
175a33c4f7bSJames Smart 		mp->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &(mp->phys));
176a33c4f7bSJames Smart 
177a33c4f7bSJames Smart 		if (!mp->virt) {
178a33c4f7bSJames Smart 			kfree(mp);
179a33c4f7bSJames Smart 			if (mlist)
180a33c4f7bSJames Smart 				lpfc_free_bsg_buffers(phba, mlist);
181a33c4f7bSJames Smart 			return NULL;
182a33c4f7bSJames Smart 		}
183a33c4f7bSJames Smart 
184a33c4f7bSJames Smart 		/* Queue it to a linked list */
185a33c4f7bSJames Smart 		if (!mlist)
186a33c4f7bSJames Smart 			mlist = mp;
187a33c4f7bSJames Smart 		else
188a33c4f7bSJames Smart 			list_add_tail(&mp->list, &mlist->list);
189a33c4f7bSJames Smart 
190a33c4f7bSJames Smart 		/* Add buffer to buffer pointer list */
191a33c4f7bSJames Smart 		if (outbound_buffers)
192a33c4f7bSJames Smart 			bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
193a33c4f7bSJames Smart 		else
194a33c4f7bSJames Smart 			bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
195a33c4f7bSJames Smart 		bpl->addrLow = le32_to_cpu(putPaddrLow(mp->phys));
196a33c4f7bSJames Smart 		bpl->addrHigh = le32_to_cpu(putPaddrHigh(mp->phys));
197a33c4f7bSJames Smart 		bpl->tus.f.bdeSize = (uint16_t)
198a33c4f7bSJames Smart 			(bytes_left >= LPFC_BPL_SIZE ? LPFC_BPL_SIZE :
199a33c4f7bSJames Smart 			 bytes_left);
200a33c4f7bSJames Smart 		bytes_left -= bpl->tus.f.bdeSize;
201a33c4f7bSJames Smart 		bpl->tus.w = le32_to_cpu(bpl->tus.w);
202a33c4f7bSJames Smart 		bpl++;
203a33c4f7bSJames Smart 	}
204a33c4f7bSJames Smart 	return mlist;
205a33c4f7bSJames Smart }
206a33c4f7bSJames Smart 
207a33c4f7bSJames Smart static unsigned int
208a33c4f7bSJames Smart lpfc_bsg_copy_data(struct lpfc_dmabuf *dma_buffers,
209eb340948SJohannes Thumshirn 		   struct bsg_buffer *bsg_buffers,
210a33c4f7bSJames Smart 		   unsigned int bytes_to_transfer, int to_buffers)
211a33c4f7bSJames Smart {
212a33c4f7bSJames Smart 
213a33c4f7bSJames Smart 	struct lpfc_dmabuf *mp;
214a33c4f7bSJames Smart 	unsigned int transfer_bytes, bytes_copied = 0;
215a33c4f7bSJames Smart 	unsigned int sg_offset, dma_offset;
216a33c4f7bSJames Smart 	unsigned char *dma_address, *sg_address;
217a33c4f7bSJames Smart 	LIST_HEAD(temp_list);
218d5ce53b7SJames Smart 	struct sg_mapping_iter miter;
219d5ce53b7SJames Smart 	unsigned long flags;
220d5ce53b7SJames Smart 	unsigned int sg_flags = SG_MITER_ATOMIC;
221d5ce53b7SJames Smart 	bool sg_valid;
222a33c4f7bSJames Smart 
223a33c4f7bSJames Smart 	list_splice_init(&dma_buffers->list, &temp_list);
224a33c4f7bSJames Smart 	list_add(&dma_buffers->list, &temp_list);
225a33c4f7bSJames Smart 	sg_offset = 0;
226d5ce53b7SJames Smart 	if (to_buffers)
227d5ce53b7SJames Smart 		sg_flags |= SG_MITER_FROM_SG;
228d5ce53b7SJames Smart 	else
229d5ce53b7SJames Smart 		sg_flags |= SG_MITER_TO_SG;
230d5ce53b7SJames Smart 	sg_miter_start(&miter, bsg_buffers->sg_list, bsg_buffers->sg_cnt,
231d5ce53b7SJames Smart 		       sg_flags);
232d5ce53b7SJames Smart 	local_irq_save(flags);
233d5ce53b7SJames Smart 	sg_valid = sg_miter_next(&miter);
234a33c4f7bSJames Smart 	list_for_each_entry(mp, &temp_list, list) {
235a33c4f7bSJames Smart 		dma_offset = 0;
236d5ce53b7SJames Smart 		while (bytes_to_transfer && sg_valid &&
237a33c4f7bSJames Smart 		       (dma_offset < LPFC_BPL_SIZE)) {
238a33c4f7bSJames Smart 			dma_address = mp->virt + dma_offset;
239a33c4f7bSJames Smart 			if (sg_offset) {
240a33c4f7bSJames Smart 				/* Continue previous partial transfer of sg */
241d5ce53b7SJames Smart 				sg_address = miter.addr + sg_offset;
242d5ce53b7SJames Smart 				transfer_bytes = miter.length - sg_offset;
243a33c4f7bSJames Smart 			} else {
244d5ce53b7SJames Smart 				sg_address = miter.addr;
245d5ce53b7SJames Smart 				transfer_bytes = miter.length;
246a33c4f7bSJames Smart 			}
247a33c4f7bSJames Smart 			if (bytes_to_transfer < transfer_bytes)
248a33c4f7bSJames Smart 				transfer_bytes = bytes_to_transfer;
249a33c4f7bSJames Smart 			if (transfer_bytes > (LPFC_BPL_SIZE - dma_offset))
250a33c4f7bSJames Smart 				transfer_bytes = LPFC_BPL_SIZE - dma_offset;
251a33c4f7bSJames Smart 			if (to_buffers)
252a33c4f7bSJames Smart 				memcpy(dma_address, sg_address, transfer_bytes);
253a33c4f7bSJames Smart 			else
254a33c4f7bSJames Smart 				memcpy(sg_address, dma_address, transfer_bytes);
255a33c4f7bSJames Smart 			dma_offset += transfer_bytes;
256a33c4f7bSJames Smart 			sg_offset += transfer_bytes;
257a33c4f7bSJames Smart 			bytes_to_transfer -= transfer_bytes;
258a33c4f7bSJames Smart 			bytes_copied += transfer_bytes;
259d5ce53b7SJames Smart 			if (sg_offset >= miter.length) {
260a33c4f7bSJames Smart 				sg_offset = 0;
261d5ce53b7SJames Smart 				sg_valid = sg_miter_next(&miter);
262a33c4f7bSJames Smart 			}
263a33c4f7bSJames Smart 		}
264a33c4f7bSJames Smart 	}
265d5ce53b7SJames Smart 	sg_miter_stop(&miter);
266d5ce53b7SJames Smart 	local_irq_restore(flags);
267a33c4f7bSJames Smart 	list_del_init(&dma_buffers->list);
268a33c4f7bSJames Smart 	list_splice(&temp_list, &dma_buffers->list);
269a33c4f7bSJames Smart 	return bytes_copied;
270a33c4f7bSJames Smart }
271a33c4f7bSJames Smart 
272f1c3b0fcSJames Smart /**
2734cc0e56eSJames Smart  * lpfc_bsg_send_mgmt_cmd_cmp - lpfc_bsg_send_mgmt_cmd's completion handler
2744cc0e56eSJames Smart  * @phba: Pointer to HBA context object.
2754cc0e56eSJames Smart  * @cmdiocbq: Pointer to command iocb.
2764cc0e56eSJames Smart  * @rspiocbq: Pointer to response iocb.
2774cc0e56eSJames Smart  *
2784cc0e56eSJames Smart  * This function is the completion handler for iocbs issued using
2794cc0e56eSJames Smart  * lpfc_bsg_send_mgmt_cmd function. This function is called by the
2804cc0e56eSJames Smart  * ring event handler function without any lock held. This function
2814cc0e56eSJames Smart  * can be called from both worker thread context and interrupt
2824cc0e56eSJames Smart  * context. This function also can be called from another thread which
2834cc0e56eSJames Smart  * cleans up the SLI layer objects.
2844cc0e56eSJames Smart  * This function copies the contents of the response iocb to the
2854cc0e56eSJames Smart  * response iocb memory object provided by the caller of
2864cc0e56eSJames Smart  * lpfc_sli_issue_iocb_wait and then wakes up the thread which
2874cc0e56eSJames Smart  * sleeps for the iocb completion.
2884cc0e56eSJames Smart  **/
2894cc0e56eSJames Smart static void
2904cc0e56eSJames Smart lpfc_bsg_send_mgmt_cmd_cmp(struct lpfc_hba *phba,
2914cc0e56eSJames Smart 			struct lpfc_iocbq *cmdiocbq,
2924cc0e56eSJames Smart 			struct lpfc_iocbq *rspiocbq)
2934cc0e56eSJames Smart {
2944cc0e56eSJames Smart 	struct bsg_job_data *dd_data;
29575cc8cfcSJohannes Thumshirn 	struct bsg_job *job;
29601e0e15cSJohannes Thumshirn 	struct fc_bsg_reply *bsg_reply;
297a33c4f7bSJames Smart 	struct lpfc_dmabuf *bmp, *cmp, *rmp;
2984cc0e56eSJames Smart 	struct lpfc_nodelist *ndlp;
2994cc0e56eSJames Smart 	struct lpfc_bsg_iocb *iocb;
3004cc0e56eSJames Smart 	unsigned long flags;
3014cc0e56eSJames Smart 	int rc = 0;
30261910d6aSJames Smart 	u32 ulp_status, ulp_word4, total_data_placed;
3034cc0e56eSJames Smart 
304d51cf5bdSJames Smart 	dd_data = cmdiocbq->context_un.dd_data;
305a33c4f7bSJames Smart 
306a33c4f7bSJames Smart 	/* Determine if job has been aborted */
3074cc0e56eSJames Smart 	spin_lock_irqsave(&phba->ct_ev_lock, flags);
308a33c4f7bSJames Smart 	job = dd_data->set_job;
309a33c4f7bSJames Smart 	if (job) {
31001e0e15cSJohannes Thumshirn 		bsg_reply = job->reply;
311a33c4f7bSJames Smart 		/* Prevent timeout handling from trying to abort job */
312a33c4f7bSJames Smart 		job->dd_data = NULL;
3134cc0e56eSJames Smart 	}
314a33c4f7bSJames Smart 	spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
3154cc0e56eSJames Smart 
316b5a9b2dfSJames Smart 	/* Close the timeout handler abort window */
317b5a9b2dfSJames Smart 	spin_lock_irqsave(&phba->hbalock, flags);
318a680a929SJames Smart 	cmdiocbq->cmd_flag &= ~LPFC_IO_CMD_OUTSTANDING;
319b5a9b2dfSJames Smart 	spin_unlock_irqrestore(&phba->hbalock, flags);
320b5a9b2dfSJames Smart 
3214cc0e56eSJames Smart 	iocb = &dd_data->context_un.iocb;
322d51cf5bdSJames Smart 	ndlp = iocb->cmdiocbq->ndlp;
323a33c4f7bSJames Smart 	rmp = iocb->rmp;
324d51cf5bdSJames Smart 	cmp = cmdiocbq->cmd_dmabuf;
325d51cf5bdSJames Smart 	bmp = cmdiocbq->bpl_dmabuf;
32661910d6aSJames Smart 	ulp_status = get_job_ulpstatus(phba, rspiocbq);
32761910d6aSJames Smart 	ulp_word4 = get_job_word4(phba, rspiocbq);
32861910d6aSJames Smart 	total_data_placed = get_job_data_placed(phba, rspiocbq);
3294cc0e56eSJames Smart 
330a33c4f7bSJames Smart 	/* Copy the completed data or set the error status */
3314cc0e56eSJames Smart 
332a33c4f7bSJames Smart 	if (job) {
33361910d6aSJames Smart 		if (ulp_status) {
33461910d6aSJames Smart 			if (ulp_status == IOSTAT_LOCAL_REJECT) {
33561910d6aSJames Smart 				switch (ulp_word4 & IOERR_PARAM_MASK) {
3364cc0e56eSJames Smart 				case IOERR_SEQUENCE_TIMEOUT:
3374cc0e56eSJames Smart 					rc = -ETIMEDOUT;
3384cc0e56eSJames Smart 					break;
3394cc0e56eSJames Smart 				case IOERR_INVALID_RPI:
3404cc0e56eSJames Smart 					rc = -EFAULT;
3414cc0e56eSJames Smart 					break;
3424cc0e56eSJames Smart 				default:
3434cc0e56eSJames Smart 					rc = -EACCES;
3444cc0e56eSJames Smart 					break;
3454cc0e56eSJames Smart 				}
346a33c4f7bSJames Smart 			} else {
3474cc0e56eSJames Smart 				rc = -EACCES;
348a33c4f7bSJames Smart 			}
349a33c4f7bSJames Smart 		} else {
35001e0e15cSJohannes Thumshirn 			bsg_reply->reply_payload_rcv_len =
351a33c4f7bSJames Smart 				lpfc_bsg_copy_data(rmp, &job->reply_payload,
35261910d6aSJames Smart 						   total_data_placed, 0);
353a33c4f7bSJames Smart 		}
354a33c4f7bSJames Smart 	}
3554cc0e56eSJames Smart 
356a33c4f7bSJames Smart 	lpfc_free_bsg_buffers(phba, cmp);
357a33c4f7bSJames Smart 	lpfc_free_bsg_buffers(phba, rmp);
3584cc0e56eSJames Smart 	lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
359a33c4f7bSJames Smart 	kfree(bmp);
3604cc0e56eSJames Smart 	lpfc_nlp_put(ndlp);
3614430f7fdSJames Smart 	lpfc_sli_release_iocbq(phba, cmdiocbq);
3624cc0e56eSJames Smart 	kfree(dd_data);
363a33c4f7bSJames Smart 
364a33c4f7bSJames Smart 	/* Complete the job if the job is still active */
365a33c4f7bSJames Smart 
366a33c4f7bSJames Smart 	if (job) {
36701e0e15cSJohannes Thumshirn 		bsg_reply->result = rc;
36806548160SJohannes Thumshirn 		bsg_job_done(job, bsg_reply->result,
3691abaede7SJohannes Thumshirn 			       bsg_reply->reply_payload_rcv_len);
370a33c4f7bSJames Smart 	}
3714cc0e56eSJames Smart 	return;
3724cc0e56eSJames Smart }
3734cc0e56eSJames Smart 
3744cc0e56eSJames Smart /**
3754cc0e56eSJames Smart  * lpfc_bsg_send_mgmt_cmd - send a CT command from a bsg request
376f1c3b0fcSJames Smart  * @job: fc_bsg_job to handle
3773b5dd52aSJames Smart  **/
378f1c3b0fcSJames Smart static int
37975cc8cfcSJohannes Thumshirn lpfc_bsg_send_mgmt_cmd(struct bsg_job *job)
380f1c3b0fcSJames Smart {
381cd21c605SJohannes Thumshirn 	struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
3821d69b122SJohannes Thumshirn 	struct lpfc_rport_data *rdata = fc_bsg_to_rport(job)->dd_data;
38361910d6aSJames Smart 	struct lpfc_hba *phba = vport->phba;
384f1c3b0fcSJames Smart 	struct lpfc_nodelist *ndlp = rdata->pnode;
38501e0e15cSJohannes Thumshirn 	struct fc_bsg_reply *bsg_reply = job->reply;
386f1c3b0fcSJames Smart 	struct ulp_bde64 *bpl = NULL;
387f1c3b0fcSJames Smart 	struct lpfc_iocbq *cmdiocbq = NULL;
388a33c4f7bSJames Smart 	struct lpfc_dmabuf *bmp = NULL, *cmp = NULL, *rmp = NULL;
38961910d6aSJames Smart 	int request_nseg, reply_nseg;
39061910d6aSJames Smart 	u32 num_entry;
3914cc0e56eSJames Smart 	struct bsg_job_data *dd_data;
392b5a9b2dfSJames Smart 	unsigned long flags;
3934cc0e56eSJames Smart 	uint32_t creg_val;
394f1c3b0fcSJames Smart 	int rc = 0;
395d439d286SJames Smart 	int iocb_stat;
39661910d6aSJames Smart 	u16 ulp_context;
397f1c3b0fcSJames Smart 
398f1c3b0fcSJames Smart 	/* in case no data is transferred */
39901e0e15cSJohannes Thumshirn 	bsg_reply->reply_payload_rcv_len = 0;
400f1c3b0fcSJames Smart 
4014430f7fdSJames Smart 	if (ndlp->nlp_flag & NLP_ELS_SND_MASK)
4024430f7fdSJames Smart 		return -ENODEV;
4034430f7fdSJames Smart 
4044cc0e56eSJames Smart 	/* allocate our bsg tracking structure */
4054cc0e56eSJames Smart 	dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
4064cc0e56eSJames Smart 	if (!dd_data) {
4074cc0e56eSJames Smart 		lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
4084cc0e56eSJames Smart 				"2733 Failed allocation of dd_data\n");
4094cc0e56eSJames Smart 		rc = -ENOMEM;
4104cc0e56eSJames Smart 		goto no_dd_data;
4114cc0e56eSJames Smart 	}
4124cc0e56eSJames Smart 
413f1c3b0fcSJames Smart 	cmdiocbq = lpfc_sli_get_iocbq(phba);
414f1c3b0fcSJames Smart 	if (!cmdiocbq) {
415f1c3b0fcSJames Smart 		rc = -ENOMEM;
4164430f7fdSJames Smart 		goto free_dd;
417f1c3b0fcSJames Smart 	}
418f1c3b0fcSJames Smart 
419a33c4f7bSJames Smart 	bmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
420a33c4f7bSJames Smart 	if (!bmp) {
421f1c3b0fcSJames Smart 		rc = -ENOMEM;
422be858b65SJames Smart 		goto free_cmdiocbq;
423f1c3b0fcSJames Smart 	}
424a33c4f7bSJames Smart 	bmp->virt = lpfc_mbuf_alloc(phba, 0, &bmp->phys);
425a33c4f7bSJames Smart 	if (!bmp->virt) {
426a33c4f7bSJames Smart 		rc = -ENOMEM;
427a33c4f7bSJames Smart 		goto free_bmp;
428f1c3b0fcSJames Smart 	}
429f1c3b0fcSJames Smart 
430a33c4f7bSJames Smart 	INIT_LIST_HEAD(&bmp->list);
431a33c4f7bSJames Smart 
432a33c4f7bSJames Smart 	bpl = (struct ulp_bde64 *) bmp->virt;
433a33c4f7bSJames Smart 	request_nseg = LPFC_BPL_SIZE/sizeof(struct ulp_bde64);
434a33c4f7bSJames Smart 	cmp = lpfc_alloc_bsg_buffers(phba, job->request_payload.payload_len,
435a33c4f7bSJames Smart 				     1, bpl, &request_nseg);
436a33c4f7bSJames Smart 	if (!cmp) {
437a33c4f7bSJames Smart 		rc = -ENOMEM;
438a33c4f7bSJames Smart 		goto free_bmp;
439a33c4f7bSJames Smart 	}
440a33c4f7bSJames Smart 	lpfc_bsg_copy_data(cmp, &job->request_payload,
441a33c4f7bSJames Smart 			   job->request_payload.payload_len, 1);
442a33c4f7bSJames Smart 
443a33c4f7bSJames Smart 	bpl += request_nseg;
444a33c4f7bSJames Smart 	reply_nseg = LPFC_BPL_SIZE/sizeof(struct ulp_bde64) - request_nseg;
445a33c4f7bSJames Smart 	rmp = lpfc_alloc_bsg_buffers(phba, job->reply_payload.payload_len, 0,
446a33c4f7bSJames Smart 				     bpl, &reply_nseg);
447a33c4f7bSJames Smart 	if (!rmp) {
448a33c4f7bSJames Smart 		rc = -ENOMEM;
449a33c4f7bSJames Smart 		goto free_cmp;
450f1c3b0fcSJames Smart 	}
451f1c3b0fcSJames Smart 
45261910d6aSJames Smart 	num_entry = request_nseg + reply_nseg;
45361910d6aSJames Smart 
4546d368e53SJames Smart 	if (phba->sli_rev == LPFC_SLI_REV4)
45561910d6aSJames Smart 		ulp_context = phba->sli4_hba.rpi_ids[ndlp->nlp_rpi];
45661910d6aSJames Smart 	else
45761910d6aSJames Smart 		ulp_context = ndlp->nlp_rpi;
45861910d6aSJames Smart 
45961910d6aSJames Smart 	lpfc_sli_prep_gen_req(phba, cmdiocbq, bmp, ulp_context, num_entry,
46061910d6aSJames Smart 			      phba->fc_ratov * 2);
46161910d6aSJames Smart 
46261910d6aSJames Smart 	cmdiocbq->num_bdes = num_entry;
463f1c3b0fcSJames Smart 	cmdiocbq->vport = phba->pport;
464d51cf5bdSJames Smart 	cmdiocbq->cmd_dmabuf = cmp;
465d51cf5bdSJames Smart 	cmdiocbq->bpl_dmabuf = bmp;
466a680a929SJames Smart 	cmdiocbq->cmd_flag |= LPFC_IO_LIBDFC;
467f1c3b0fcSJames Smart 
468a680a929SJames Smart 	cmdiocbq->cmd_cmpl = lpfc_bsg_send_mgmt_cmd_cmp;
469d51cf5bdSJames Smart 	cmdiocbq->context_un.dd_data = dd_data;
4704430f7fdSJames Smart 
4714cc0e56eSJames Smart 	dd_data->type = TYPE_IOCB;
472a33c4f7bSJames Smart 	dd_data->set_job = job;
4734cc0e56eSJames Smart 	dd_data->context_un.iocb.cmdiocbq = cmdiocbq;
474a33c4f7bSJames Smart 	dd_data->context_un.iocb.rmp = rmp;
475a33c4f7bSJames Smart 	job->dd_data = dd_data;
476f1c3b0fcSJames Smart 
4774cc0e56eSJames Smart 	if (phba->cfg_poll & DISABLE_FCP_RING_INT) {
4789940b97bSJames Smart 		if (lpfc_readl(phba->HCregaddr, &creg_val)) {
4799940b97bSJames Smart 			rc = -EIO ;
480a33c4f7bSJames Smart 			goto free_rmp;
4819940b97bSJames Smart 		}
4824cc0e56eSJames Smart 		creg_val |= (HC_R0INT_ENA << LPFC_FCP_RING);
4834cc0e56eSJames Smart 		writel(creg_val, phba->HCregaddr);
4844cc0e56eSJames Smart 		readl(phba->HCregaddr); /* flush */
4854cc0e56eSJames Smart 	}
4864cc0e56eSJames Smart 
487d51cf5bdSJames Smart 	cmdiocbq->ndlp = lpfc_nlp_get(ndlp);
488d51cf5bdSJames Smart 	if (!cmdiocbq->ndlp) {
4894430f7fdSJames Smart 		rc = -ENODEV;
4904430f7fdSJames Smart 		goto free_rmp;
4914430f7fdSJames Smart 	}
492b5a9b2dfSJames Smart 
4934430f7fdSJames Smart 	iocb_stat = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq, 0);
494b5a9b2dfSJames Smart 	if (iocb_stat == IOCB_SUCCESS) {
495b5a9b2dfSJames Smart 		spin_lock_irqsave(&phba->hbalock, flags);
496b5a9b2dfSJames Smart 		/* make sure the I/O had not been completed yet */
497a680a929SJames Smart 		if (cmdiocbq->cmd_flag & LPFC_IO_LIBDFC) {
498b5a9b2dfSJames Smart 			/* open up abort window to timeout handler */
499a680a929SJames Smart 			cmdiocbq->cmd_flag |= LPFC_IO_CMD_OUTSTANDING;
500b5a9b2dfSJames Smart 		}
501b5a9b2dfSJames Smart 		spin_unlock_irqrestore(&phba->hbalock, flags);
5024cc0e56eSJames Smart 		return 0; /* done for now */
503b5a9b2dfSJames Smart 	} else if (iocb_stat == IOCB_BUSY) {
504d439d286SJames Smart 		rc = -EAGAIN;
505b5a9b2dfSJames Smart 	} else {
506d439d286SJames Smart 		rc = -EIO;
507b5a9b2dfSJames Smart 	}
5082a9bf3d0SJames Smart 
5094cc0e56eSJames Smart 	/* iocb failed so cleanup */
5104430f7fdSJames Smart 	lpfc_nlp_put(ndlp);
511f1c3b0fcSJames Smart 
512a33c4f7bSJames Smart free_rmp:
513a33c4f7bSJames Smart 	lpfc_free_bsg_buffers(phba, rmp);
514a33c4f7bSJames Smart free_cmp:
515a33c4f7bSJames Smart 	lpfc_free_bsg_buffers(phba, cmp);
516a33c4f7bSJames Smart free_bmp:
517a33c4f7bSJames Smart 	if (bmp->virt)
518f1c3b0fcSJames Smart 		lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
519a33c4f7bSJames Smart 	kfree(bmp);
520f1c3b0fcSJames Smart free_cmdiocbq:
521f1c3b0fcSJames Smart 	lpfc_sli_release_iocbq(phba, cmdiocbq);
5224430f7fdSJames Smart free_dd:
5234cc0e56eSJames Smart 	kfree(dd_data);
5244cc0e56eSJames Smart no_dd_data:
525f1c3b0fcSJames Smart 	/* make error code available to userspace */
52601e0e15cSJohannes Thumshirn 	bsg_reply->result = rc;
5274cc0e56eSJames Smart 	job->dd_data = NULL;
5284cc0e56eSJames Smart 	return rc;
5294cc0e56eSJames Smart }
5304cc0e56eSJames Smart 
5314cc0e56eSJames Smart /**
5324cc0e56eSJames Smart  * lpfc_bsg_rport_els_cmp - lpfc_bsg_rport_els's completion handler
5334cc0e56eSJames Smart  * @phba: Pointer to HBA context object.
5344cc0e56eSJames Smart  * @cmdiocbq: Pointer to command iocb.
5354cc0e56eSJames Smart  * @rspiocbq: Pointer to response iocb.
5364cc0e56eSJames Smart  *
5374cc0e56eSJames Smart  * This function is the completion handler for iocbs issued using
5384cc0e56eSJames Smart  * lpfc_bsg_rport_els_cmp function. This function is called by the
5394cc0e56eSJames Smart  * ring event handler function without any lock held. This function
5404cc0e56eSJames Smart  * can be called from both worker thread context and interrupt
5414cc0e56eSJames Smart  * context. This function also can be called from other thread which
5424cc0e56eSJames Smart  * cleans up the SLI layer objects.
5433b5dd52aSJames Smart  * This function copies the contents of the response iocb to the
5444cc0e56eSJames Smart  * response iocb memory object provided by the caller of
5454cc0e56eSJames Smart  * lpfc_sli_issue_iocb_wait and then wakes up the thread which
5464cc0e56eSJames Smart  * sleeps for the iocb completion.
5474cc0e56eSJames Smart  **/
5484cc0e56eSJames Smart static void
5494cc0e56eSJames Smart lpfc_bsg_rport_els_cmp(struct lpfc_hba *phba,
5504cc0e56eSJames Smart 			struct lpfc_iocbq *cmdiocbq,
5514cc0e56eSJames Smart 			struct lpfc_iocbq *rspiocbq)
5524cc0e56eSJames Smart {
5534cc0e56eSJames Smart 	struct bsg_job_data *dd_data;
55475cc8cfcSJohannes Thumshirn 	struct bsg_job *job;
55501e0e15cSJohannes Thumshirn 	struct fc_bsg_reply *bsg_reply;
5564cc0e56eSJames Smart 	struct lpfc_nodelist *ndlp;
557a33c4f7bSJames Smart 	struct lpfc_dmabuf *pcmd = NULL, *prsp = NULL;
5584cc0e56eSJames Smart 	struct fc_bsg_ctels_reply *els_reply;
5594cc0e56eSJames Smart 	uint8_t *rjt_data;
5604cc0e56eSJames Smart 	unsigned long flags;
561a33c4f7bSJames Smart 	unsigned int rsp_size;
5624cc0e56eSJames Smart 	int rc = 0;
5630e082d92SJames Smart 	u32 ulp_status, ulp_word4, total_data_placed;
5644cc0e56eSJames Smart 
565d51cf5bdSJames Smart 	dd_data = cmdiocbq->context_un.dd_data;
5664cc0e56eSJames Smart 	ndlp = dd_data->context_un.iocb.ndlp;
567d51cf5bdSJames Smart 	cmdiocbq->ndlp = ndlp;
5684cc0e56eSJames Smart 
569a33c4f7bSJames Smart 	/* Determine if job has been aborted */
570a33c4f7bSJames Smart 	spin_lock_irqsave(&phba->ct_ev_lock, flags);
571a33c4f7bSJames Smart 	job = dd_data->set_job;
572a33c4f7bSJames Smart 	if (job) {
57301e0e15cSJohannes Thumshirn 		bsg_reply = job->reply;
574a33c4f7bSJames Smart 		/* Prevent timeout handling from trying to abort job  */
575a33c4f7bSJames Smart 		job->dd_data = NULL;
576a33c4f7bSJames Smart 	}
577a33c4f7bSJames Smart 	spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
5784cc0e56eSJames Smart 
579b5a9b2dfSJames Smart 	/* Close the timeout handler abort window */
580b5a9b2dfSJames Smart 	spin_lock_irqsave(&phba->hbalock, flags);
581a680a929SJames Smart 	cmdiocbq->cmd_flag &= ~LPFC_IO_CMD_OUTSTANDING;
582b5a9b2dfSJames Smart 	spin_unlock_irqrestore(&phba->hbalock, flags);
583b5a9b2dfSJames Smart 
5840e082d92SJames Smart 	ulp_status = get_job_ulpstatus(phba, rspiocbq);
5850e082d92SJames Smart 	ulp_word4 = get_job_word4(phba, rspiocbq);
5860e082d92SJames Smart 	total_data_placed = get_job_data_placed(phba, rspiocbq);
587d51cf5bdSJames Smart 	pcmd = cmdiocbq->cmd_dmabuf;
588a33c4f7bSJames Smart 	prsp = (struct lpfc_dmabuf *)pcmd->list.next;
589a33c4f7bSJames Smart 
590a33c4f7bSJames Smart 	/* Copy the completed job data or determine the job status if job is
591a33c4f7bSJames Smart 	 * still active
592a33c4f7bSJames Smart 	 */
593a33c4f7bSJames Smart 
594a33c4f7bSJames Smart 	if (job) {
5950e082d92SJames Smart 		if (ulp_status == IOSTAT_SUCCESS) {
5960e082d92SJames Smart 			rsp_size = total_data_placed;
59701e0e15cSJohannes Thumshirn 			bsg_reply->reply_payload_rcv_len =
598a33c4f7bSJames Smart 				sg_copy_from_buffer(job->reply_payload.sg_list,
599a33c4f7bSJames Smart 						    job->reply_payload.sg_cnt,
600a33c4f7bSJames Smart 						    prsp->virt,
601a33c4f7bSJames Smart 						    rsp_size);
6020e082d92SJames Smart 		} else if (ulp_status == IOSTAT_LS_RJT) {
60301e0e15cSJohannes Thumshirn 			bsg_reply->reply_payload_rcv_len =
6044cc0e56eSJames Smart 				sizeof(struct fc_bsg_ctels_reply);
6054cc0e56eSJames Smart 			/* LS_RJT data returned in word 4 */
6060e082d92SJames Smart 			rjt_data = (uint8_t *)&ulp_word4;
60701e0e15cSJohannes Thumshirn 			els_reply = &bsg_reply->reply_data.ctels_reply;
6084cc0e56eSJames Smart 			els_reply->status = FC_CTELS_STATUS_REJECT;
6094cc0e56eSJames Smart 			els_reply->rjt_data.action = rjt_data[3];
6104cc0e56eSJames Smart 			els_reply->rjt_data.reason_code = rjt_data[2];
6114cc0e56eSJames Smart 			els_reply->rjt_data.reason_explanation = rjt_data[1];
6124cc0e56eSJames Smart 			els_reply->rjt_data.vendor_unique = rjt_data[0];
6130e082d92SJames Smart 		} else if (ulp_status == IOSTAT_LOCAL_REJECT &&
6140e082d92SJames Smart 			   (ulp_word4 & IOERR_PARAM_MASK) ==
6150e082d92SJames Smart 			   IOERR_SEQUENCE_TIMEOUT) {
6160e082d92SJames Smart 			rc = -ETIMEDOUT;
617a33c4f7bSJames Smart 		} else {
6184cc0e56eSJames Smart 			rc = -EIO;
619a33c4f7bSJames Smart 		}
620a33c4f7bSJames Smart 	}
6214cc0e56eSJames Smart 
622a33c4f7bSJames Smart 	lpfc_els_free_iocb(phba, cmdiocbq);
6234430f7fdSJames Smart 
6244430f7fdSJames Smart 	lpfc_nlp_put(ndlp);
6254cc0e56eSJames Smart 	kfree(dd_data);
626a33c4f7bSJames Smart 
627a33c4f7bSJames Smart 	/* Complete the job if the job is still active */
628a33c4f7bSJames Smart 
629a33c4f7bSJames Smart 	if (job) {
63001e0e15cSJohannes Thumshirn 		bsg_reply->result = rc;
63106548160SJohannes Thumshirn 		bsg_job_done(job, bsg_reply->result,
6321abaede7SJohannes Thumshirn 			       bsg_reply->reply_payload_rcv_len);
633a33c4f7bSJames Smart 	}
6344cc0e56eSJames Smart 	return;
635f1c3b0fcSJames Smart }
636f1c3b0fcSJames Smart 
637f1c3b0fcSJames Smart /**
638f1c3b0fcSJames Smart  * lpfc_bsg_rport_els - send an ELS command from a bsg request
639f1c3b0fcSJames Smart  * @job: fc_bsg_job to handle
6403b5dd52aSJames Smart  **/
641f1c3b0fcSJames Smart static int
64275cc8cfcSJohannes Thumshirn lpfc_bsg_rport_els(struct bsg_job *job)
643f1c3b0fcSJames Smart {
644cd21c605SJohannes Thumshirn 	struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
645f1c3b0fcSJames Smart 	struct lpfc_hba *phba = vport->phba;
6461d69b122SJohannes Thumshirn 	struct lpfc_rport_data *rdata = fc_bsg_to_rport(job)->dd_data;
647f1c3b0fcSJames Smart 	struct lpfc_nodelist *ndlp = rdata->pnode;
64801e0e15cSJohannes Thumshirn 	struct fc_bsg_request *bsg_request = job->request;
64901e0e15cSJohannes Thumshirn 	struct fc_bsg_reply *bsg_reply = job->reply;
650f1c3b0fcSJames Smart 	uint32_t elscmd;
651f1c3b0fcSJames Smart 	uint32_t cmdsize;
652f1c3b0fcSJames Smart 	struct lpfc_iocbq *cmdiocbq;
653f1c3b0fcSJames Smart 	uint16_t rpi = 0;
6544cc0e56eSJames Smart 	struct bsg_job_data *dd_data;
655b5a9b2dfSJames Smart 	unsigned long flags;
6564cc0e56eSJames Smart 	uint32_t creg_val;
657f1c3b0fcSJames Smart 	int rc = 0;
658f1c3b0fcSJames Smart 
659f1c3b0fcSJames Smart 	/* in case no data is transferred */
66001e0e15cSJohannes Thumshirn 	bsg_reply->reply_payload_rcv_len = 0;
661f1c3b0fcSJames Smart 
662a33c4f7bSJames Smart 	/* verify the els command is not greater than the
663a33c4f7bSJames Smart 	 * maximum ELS transfer size.
664a33c4f7bSJames Smart 	 */
665a33c4f7bSJames Smart 
666a33c4f7bSJames Smart 	if (job->request_payload.payload_len > FCELSSIZE) {
667a33c4f7bSJames Smart 		rc = -EINVAL;
668a33c4f7bSJames Smart 		goto no_dd_data;
669a33c4f7bSJames Smart 	}
670a33c4f7bSJames Smart 
6714cc0e56eSJames Smart 	/* allocate our bsg tracking structure */
6724cc0e56eSJames Smart 	dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
6734cc0e56eSJames Smart 	if (!dd_data) {
6744cc0e56eSJames Smart 		lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
6754cc0e56eSJames Smart 				"2735 Failed allocation of dd_data\n");
6764cc0e56eSJames Smart 		rc = -ENOMEM;
6774cc0e56eSJames Smart 		goto no_dd_data;
6784cc0e56eSJames Smart 	}
6794cc0e56eSJames Smart 
68001e0e15cSJohannes Thumshirn 	elscmd = bsg_request->rqst_data.r_els.els_code;
681a33c4f7bSJames Smart 	cmdsize = job->request_payload.payload_len;
682a33c4f7bSJames Smart 
683f1c3b0fcSJames Smart 	if (!lpfc_nlp_get(ndlp)) {
684f1c3b0fcSJames Smart 		rc = -ENODEV;
6854cc0e56eSJames Smart 		goto free_dd_data;
686f1c3b0fcSJames Smart 	}
687f1c3b0fcSJames Smart 
688a33c4f7bSJames Smart 	/* We will use the allocated dma buffers by prep els iocb for command
689a33c4f7bSJames Smart 	 * and response to ensure if the job times out and the request is freed,
690a33c4f7bSJames Smart 	 * we won't be dma into memory that is no longer allocated to for the
691a33c4f7bSJames Smart 	 * request.
692a33c4f7bSJames Smart 	 */
6934cc0e56eSJames Smart 	cmdiocbq = lpfc_prep_els_iocb(vport, 1, cmdsize, 0, ndlp,
694f1c3b0fcSJames Smart 				      ndlp->nlp_DID, elscmd);
695f1c3b0fcSJames Smart 	if (!cmdiocbq) {
6964cc0e56eSJames Smart 		rc = -EIO;
697a33c4f7bSJames Smart 		goto release_ndlp;
698f1c3b0fcSJames Smart 	}
699f1c3b0fcSJames Smart 
700a33c4f7bSJames Smart 	/* Transfer the request payload to allocated command dma buffer */
701a33c4f7bSJames Smart 	sg_copy_to_buffer(job->request_payload.sg_list,
702a33c4f7bSJames Smart 			  job->request_payload.sg_cnt,
703d51cf5bdSJames Smart 			  cmdiocbq->cmd_dmabuf->virt,
704d51cf5bdSJames Smart 			  cmdsize);
705f1c3b0fcSJames Smart 
7064430f7fdSJames Smart 	rpi = ndlp->nlp_rpi;
7074430f7fdSJames Smart 
7083ef6d24cSJames Smart 	if (phba->sli_rev == LPFC_SLI_REV4)
7090e082d92SJames Smart 		bf_set(wqe_ctxt_tag, &cmdiocbq->wqe.generic.wqe_com,
7100e082d92SJames Smart 		       phba->sli4_hba.rpi_ids[rpi]);
7113ef6d24cSJames Smart 	else
712f1c3b0fcSJames Smart 		cmdiocbq->iocb.ulpContext = rpi;
713a680a929SJames Smart 	cmdiocbq->cmd_flag |= LPFC_IO_LIBDFC;
714d51cf5bdSJames Smart 	cmdiocbq->context_un.dd_data = dd_data;
715d51cf5bdSJames Smart 	cmdiocbq->ndlp = ndlp;
716a680a929SJames Smart 	cmdiocbq->cmd_cmpl = lpfc_bsg_rport_els_cmp;
7174cc0e56eSJames Smart 	dd_data->type = TYPE_IOCB;
718a33c4f7bSJames Smart 	dd_data->set_job = job;
7194cc0e56eSJames Smart 	dd_data->context_un.iocb.cmdiocbq = cmdiocbq;
7204cc0e56eSJames Smart 	dd_data->context_un.iocb.ndlp = ndlp;
721a33c4f7bSJames Smart 	dd_data->context_un.iocb.rmp = NULL;
722a33c4f7bSJames Smart 	job->dd_data = dd_data;
723f1c3b0fcSJames Smart 
7244cc0e56eSJames Smart 	if (phba->cfg_poll & DISABLE_FCP_RING_INT) {
7259940b97bSJames Smart 		if (lpfc_readl(phba->HCregaddr, &creg_val)) {
7269940b97bSJames Smart 			rc = -EIO;
7279940b97bSJames Smart 			goto linkdown_err;
7289940b97bSJames Smart 		}
7294cc0e56eSJames Smart 		creg_val |= (HC_R0INT_ENA << LPFC_FCP_RING);
7304cc0e56eSJames Smart 		writel(creg_val, phba->HCregaddr);
7314cc0e56eSJames Smart 		readl(phba->HCregaddr); /* flush */
7324cc0e56eSJames Smart 	}
733a33c4f7bSJames Smart 
7344430f7fdSJames Smart 	rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq, 0);
735b5a9b2dfSJames Smart 	if (rc == IOCB_SUCCESS) {
736b5a9b2dfSJames Smart 		spin_lock_irqsave(&phba->hbalock, flags);
737b5a9b2dfSJames Smart 		/* make sure the I/O had not been completed/released */
738a680a929SJames Smart 		if (cmdiocbq->cmd_flag & LPFC_IO_LIBDFC) {
739b5a9b2dfSJames Smart 			/* open up abort window to timeout handler */
740a680a929SJames Smart 			cmdiocbq->cmd_flag |= LPFC_IO_CMD_OUTSTANDING;
741b5a9b2dfSJames Smart 		}
742b5a9b2dfSJames Smart 		spin_unlock_irqrestore(&phba->hbalock, flags);
7434cc0e56eSJames Smart 		return 0; /* done for now */
744b5a9b2dfSJames Smart 	} else if (rc == IOCB_BUSY) {
745d439d286SJames Smart 		rc = -EAGAIN;
746b5a9b2dfSJames Smart 	} else {
747d439d286SJames Smart 		rc = -EIO;
748b5a9b2dfSJames Smart 	}
749b5a9b2dfSJames Smart 
7504430f7fdSJames Smart 	/* I/O issue failed.  Cleanup resources. */
7514cc0e56eSJames Smart 
7529940b97bSJames Smart linkdown_err:
753a33c4f7bSJames Smart 	lpfc_els_free_iocb(phba, cmdiocbq);
754f1c3b0fcSJames Smart 
755a33c4f7bSJames Smart release_ndlp:
756a33c4f7bSJames Smart 	lpfc_nlp_put(ndlp);
757f1c3b0fcSJames Smart 
7584cc0e56eSJames Smart free_dd_data:
7594cc0e56eSJames Smart 	kfree(dd_data);
7604cc0e56eSJames Smart 
7614cc0e56eSJames Smart no_dd_data:
762f1c3b0fcSJames Smart 	/* make error code available to userspace */
76301e0e15cSJohannes Thumshirn 	bsg_reply->result = rc;
7644cc0e56eSJames Smart 	job->dd_data = NULL;
7654cc0e56eSJames Smart 	return rc;
766f1c3b0fcSJames Smart }
767f1c3b0fcSJames Smart 
7683b5dd52aSJames Smart /**
7693b5dd52aSJames Smart  * lpfc_bsg_event_free - frees an allocated event structure
7703b5dd52aSJames Smart  * @kref: Pointer to a kref.
7713b5dd52aSJames Smart  *
7723b5dd52aSJames Smart  * Called from kref_put. Back cast the kref into an event structure address.
7733b5dd52aSJames Smart  * Free any events to get, delete associated nodes, free any events to see,
7743b5dd52aSJames Smart  * free any data then free the event itself.
7753b5dd52aSJames Smart  **/
776f1c3b0fcSJames Smart static void
7774cc0e56eSJames Smart lpfc_bsg_event_free(struct kref *kref)
778f1c3b0fcSJames Smart {
7794cc0e56eSJames Smart 	struct lpfc_bsg_event *evt = container_of(kref, struct lpfc_bsg_event,
7804cc0e56eSJames Smart 						  kref);
781f1c3b0fcSJames Smart 	struct event_data *ed;
782f1c3b0fcSJames Smart 
783f1c3b0fcSJames Smart 	list_del(&evt->node);
784f1c3b0fcSJames Smart 
785f1c3b0fcSJames Smart 	while (!list_empty(&evt->events_to_get)) {
786f1c3b0fcSJames Smart 		ed = list_entry(evt->events_to_get.next, typeof(*ed), node);
787f1c3b0fcSJames Smart 		list_del(&ed->node);
788f1c3b0fcSJames Smart 		kfree(ed->data);
789f1c3b0fcSJames Smart 		kfree(ed);
790f1c3b0fcSJames Smart 	}
791f1c3b0fcSJames Smart 
792f1c3b0fcSJames Smart 	while (!list_empty(&evt->events_to_see)) {
793f1c3b0fcSJames Smart 		ed = list_entry(evt->events_to_see.next, typeof(*ed), node);
794f1c3b0fcSJames Smart 		list_del(&ed->node);
795f1c3b0fcSJames Smart 		kfree(ed->data);
796f1c3b0fcSJames Smart 		kfree(ed);
797f1c3b0fcSJames Smart 	}
798f1c3b0fcSJames Smart 
799a33c4f7bSJames Smart 	kfree(evt->dd_data);
800f1c3b0fcSJames Smart 	kfree(evt);
801f1c3b0fcSJames Smart }
802f1c3b0fcSJames Smart 
8033b5dd52aSJames Smart /**
8043b5dd52aSJames Smart  * lpfc_bsg_event_ref - increments the kref for an event
8053b5dd52aSJames Smart  * @evt: Pointer to an event structure.
8063b5dd52aSJames Smart  **/
807f1c3b0fcSJames Smart static inline void
8084cc0e56eSJames Smart lpfc_bsg_event_ref(struct lpfc_bsg_event *evt)
809f1c3b0fcSJames Smart {
8104cc0e56eSJames Smart 	kref_get(&evt->kref);
811f1c3b0fcSJames Smart }
812f1c3b0fcSJames Smart 
8133b5dd52aSJames Smart /**
8143b5dd52aSJames Smart  * lpfc_bsg_event_unref - Uses kref_put to free an event structure
8153b5dd52aSJames Smart  * @evt: Pointer to an event structure.
8163b5dd52aSJames Smart  **/
817f1c3b0fcSJames Smart static inline void
8184cc0e56eSJames Smart lpfc_bsg_event_unref(struct lpfc_bsg_event *evt)
819f1c3b0fcSJames Smart {
8204cc0e56eSJames Smart 	kref_put(&evt->kref, lpfc_bsg_event_free);
821f1c3b0fcSJames Smart }
822f1c3b0fcSJames Smart 
8233b5dd52aSJames Smart /**
8243b5dd52aSJames Smart  * lpfc_bsg_event_new - allocate and initialize a event structure
8253b5dd52aSJames Smart  * @ev_mask: Mask of events.
8263b5dd52aSJames Smart  * @ev_reg_id: Event reg id.
8273b5dd52aSJames Smart  * @ev_req_id: Event request id.
8283b5dd52aSJames Smart  **/
8294cc0e56eSJames Smart static struct lpfc_bsg_event *
8304cc0e56eSJames Smart lpfc_bsg_event_new(uint32_t ev_mask, int ev_reg_id, uint32_t ev_req_id)
8314cc0e56eSJames Smart {
8324cc0e56eSJames Smart 	struct lpfc_bsg_event *evt = kzalloc(sizeof(*evt), GFP_KERNEL);
833f1c3b0fcSJames Smart 
8344cc0e56eSJames Smart 	if (!evt)
8354cc0e56eSJames Smart 		return NULL;
8364cc0e56eSJames Smart 
8374cc0e56eSJames Smart 	INIT_LIST_HEAD(&evt->events_to_get);
8384cc0e56eSJames Smart 	INIT_LIST_HEAD(&evt->events_to_see);
8394cc0e56eSJames Smart 	evt->type_mask = ev_mask;
8404cc0e56eSJames Smart 	evt->req_id = ev_req_id;
8414cc0e56eSJames Smart 	evt->reg_id = ev_reg_id;
8424cc0e56eSJames Smart 	evt->wait_time_stamp = jiffies;
843a33c4f7bSJames Smart 	evt->dd_data = NULL;
8444cc0e56eSJames Smart 	init_waitqueue_head(&evt->wq);
8454cc0e56eSJames Smart 	kref_init(&evt->kref);
8464cc0e56eSJames Smart 	return evt;
8474cc0e56eSJames Smart }
8484cc0e56eSJames Smart 
8493b5dd52aSJames Smart /**
8503b5dd52aSJames Smart  * diag_cmd_data_free - Frees an lpfc dma buffer extension
8513b5dd52aSJames Smart  * @phba: Pointer to HBA context object.
8523b5dd52aSJames Smart  * @mlist: Pointer to an lpfc dma buffer extension.
8533b5dd52aSJames Smart  **/
8544cc0e56eSJames Smart static int
8553b5dd52aSJames Smart diag_cmd_data_free(struct lpfc_hba *phba, struct lpfc_dmabufext *mlist)
8564cc0e56eSJames Smart {
8574cc0e56eSJames Smart 	struct lpfc_dmabufext *mlast;
8584cc0e56eSJames Smart 	struct pci_dev *pcidev;
8594cc0e56eSJames Smart 	struct list_head head, *curr, *next;
8604cc0e56eSJames Smart 
8614cc0e56eSJames Smart 	if ((!mlist) || (!lpfc_is_link_up(phba) &&
8624cc0e56eSJames Smart 		(phba->link_flag & LS_LOOPBACK_MODE))) {
8634cc0e56eSJames Smart 		return 0;
8644cc0e56eSJames Smart 	}
8654cc0e56eSJames Smart 
8664cc0e56eSJames Smart 	pcidev = phba->pcidev;
8674cc0e56eSJames Smart 	list_add_tail(&head, &mlist->dma.list);
8684cc0e56eSJames Smart 
8694cc0e56eSJames Smart 	list_for_each_safe(curr, next, &head) {
8704cc0e56eSJames Smart 		mlast = list_entry(curr, struct lpfc_dmabufext , dma.list);
8714cc0e56eSJames Smart 		if (mlast->dma.virt)
8724cc0e56eSJames Smart 			dma_free_coherent(&pcidev->dev,
8734cc0e56eSJames Smart 					  mlast->size,
8744cc0e56eSJames Smart 					  mlast->dma.virt,
8754cc0e56eSJames Smart 					  mlast->dma.phys);
8764cc0e56eSJames Smart 		kfree(mlast);
8774cc0e56eSJames Smart 	}
8784cc0e56eSJames Smart 	return 0;
8794cc0e56eSJames Smart }
880f1c3b0fcSJames Smart 
881ea085dabSLee Jones /*
882f1c3b0fcSJames Smart  * lpfc_bsg_ct_unsol_event - process an unsolicited CT command
883f1c3b0fcSJames Smart  *
884f1c3b0fcSJames Smart  * This function is called when an unsolicited CT command is received.  It
8854cc0e56eSJames Smart  * forwards the event to any processes registered to receive CT events.
8863b5dd52aSJames Smart  **/
8874fede78fSJames Smart int
888f1c3b0fcSJames Smart lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
889f1c3b0fcSJames Smart 			struct lpfc_iocbq *piocbq)
890f1c3b0fcSJames Smart {
891f1c3b0fcSJames Smart 	uint32_t evt_req_id = 0;
8929cefd6e7SJustin Tee 	u16 cmd;
893f1c3b0fcSJames Smart 	struct lpfc_dmabuf *dmabuf = NULL;
8944cc0e56eSJames Smart 	struct lpfc_bsg_event *evt;
895f1c3b0fcSJames Smart 	struct event_data *evt_dat = NULL;
896f1c3b0fcSJames Smart 	struct lpfc_iocbq *iocbq;
89761910d6aSJames Smart 	IOCB_t *iocb = NULL;
898f1c3b0fcSJames Smart 	size_t offset = 0;
899f1c3b0fcSJames Smart 	struct list_head head;
900f1c3b0fcSJames Smart 	struct ulp_bde64 *bde;
901f1c3b0fcSJames Smart 	dma_addr_t dma_addr;
902f1c3b0fcSJames Smart 	int i;
903d51cf5bdSJames Smart 	struct lpfc_dmabuf *bdeBuf1 = piocbq->cmd_dmabuf;
904d51cf5bdSJames Smart 	struct lpfc_dmabuf *bdeBuf2 = piocbq->bpl_dmabuf;
905f1c3b0fcSJames Smart 	struct lpfc_sli_ct_request *ct_req;
90675cc8cfcSJohannes Thumshirn 	struct bsg_job *job = NULL;
90701e0e15cSJohannes Thumshirn 	struct fc_bsg_reply *bsg_reply;
908a33c4f7bSJames Smart 	struct bsg_job_data *dd_data = NULL;
9094fede78fSJames Smart 	unsigned long flags;
9104cc0e56eSJames Smart 	int size = 0;
91161910d6aSJames Smart 	u32 bde_count = 0;
912f1c3b0fcSJames Smart 
913f1c3b0fcSJames Smart 	INIT_LIST_HEAD(&head);
914f1c3b0fcSJames Smart 	list_add_tail(&head, &piocbq->list);
915f1c3b0fcSJames Smart 
91683adbba7SJames Smart 	ct_req = (struct lpfc_sli_ct_request *)bdeBuf1->virt;
917f1c3b0fcSJames Smart 	evt_req_id = ct_req->FsType;
9189cefd6e7SJustin Tee 	cmd = be16_to_cpu(ct_req->CommandResponse.bits.CmdRsp);
919f1c3b0fcSJames Smart 
9204fede78fSJames Smart 	spin_lock_irqsave(&phba->ct_ev_lock, flags);
921f1c3b0fcSJames Smart 	list_for_each_entry(evt, &phba->ct_ev_waiters, node) {
9224cc0e56eSJames Smart 		if (!(evt->type_mask & FC_REG_CT_EVENT) ||
9234cc0e56eSJames Smart 			evt->req_id != evt_req_id)
924f1c3b0fcSJames Smart 			continue;
925f1c3b0fcSJames Smart 
9264cc0e56eSJames Smart 		lpfc_bsg_event_ref(evt);
9274cc0e56eSJames Smart 		spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
928f1c3b0fcSJames Smart 		evt_dat = kzalloc(sizeof(*evt_dat), GFP_KERNEL);
9294cc0e56eSJames Smart 		if (evt_dat == NULL) {
9304cc0e56eSJames Smart 			spin_lock_irqsave(&phba->ct_ev_lock, flags);
9314cc0e56eSJames Smart 			lpfc_bsg_event_unref(evt);
932f1c3b0fcSJames Smart 			lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
933f1c3b0fcSJames Smart 					"2614 Memory allocation failed for "
934f1c3b0fcSJames Smart 					"CT event\n");
935f1c3b0fcSJames Smart 			break;
936f1c3b0fcSJames Smart 		}
937f1c3b0fcSJames Smart 
938f1c3b0fcSJames Smart 		if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) {
939f1c3b0fcSJames Smart 			/* take accumulated byte count from the last iocbq */
940f1c3b0fcSJames Smart 			iocbq = list_entry(head.prev, typeof(*iocbq), list);
94161910d6aSJames Smart 			if (phba->sli_rev == LPFC_SLI_REV4)
94261910d6aSJames Smart 				evt_dat->len = iocbq->wcqe_cmpl.total_data_placed;
94361910d6aSJames Smart 			else
944f1c3b0fcSJames Smart 				evt_dat->len = iocbq->iocb.unsli3.rcvsli3.acc_len;
945f1c3b0fcSJames Smart 		} else {
946f1c3b0fcSJames Smart 			list_for_each_entry(iocbq, &head, list) {
94761910d6aSJames Smart 				iocb = &iocbq->iocb;
94861910d6aSJames Smart 				for (i = 0; i < iocb->ulpBdeCount;
94961910d6aSJames Smart 				     i++)
950f1c3b0fcSJames Smart 					evt_dat->len +=
95161910d6aSJames Smart 					iocb->un.cont64[i].tus.f.bdeSize;
952f1c3b0fcSJames Smart 			}
953f1c3b0fcSJames Smart 		}
954f1c3b0fcSJames Smart 
955f1c3b0fcSJames Smart 		evt_dat->data = kzalloc(evt_dat->len, GFP_KERNEL);
9564cc0e56eSJames Smart 		if (evt_dat->data == NULL) {
957f1c3b0fcSJames Smart 			lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
958f1c3b0fcSJames Smart 					"2615 Memory allocation failed for "
959f1c3b0fcSJames Smart 					"CT event data, size %d\n",
960f1c3b0fcSJames Smart 					evt_dat->len);
961f1c3b0fcSJames Smart 			kfree(evt_dat);
9624fede78fSJames Smart 			spin_lock_irqsave(&phba->ct_ev_lock, flags);
9634cc0e56eSJames Smart 			lpfc_bsg_event_unref(evt);
9644fede78fSJames Smart 			spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
965f1c3b0fcSJames Smart 			goto error_ct_unsol_exit;
966f1c3b0fcSJames Smart 		}
967f1c3b0fcSJames Smart 
968f1c3b0fcSJames Smart 		list_for_each_entry(iocbq, &head, list) {
9694cc0e56eSJames Smart 			size = 0;
970f1c3b0fcSJames Smart 			if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) {
971d51cf5bdSJames Smart 				bdeBuf1 = iocbq->cmd_dmabuf;
972d51cf5bdSJames Smart 				bdeBuf2 = iocbq->bpl_dmabuf;
973f1c3b0fcSJames Smart 			}
97461910d6aSJames Smart 			if (phba->sli_rev == LPFC_SLI_REV4)
97561910d6aSJames Smart 				bde_count = iocbq->wcqe_cmpl.word3;
97661910d6aSJames Smart 			else
97761910d6aSJames Smart 				bde_count = iocbq->iocb.ulpBdeCount;
97861910d6aSJames Smart 			for (i = 0; i < bde_count; i++) {
979f1c3b0fcSJames Smart 				if (phba->sli3_options &
980f1c3b0fcSJames Smart 				    LPFC_SLI3_HBQ_ENABLED) {
981f1c3b0fcSJames Smart 					if (i == 0) {
98261910d6aSJames Smart 						size = iocbq->wqe.gen_req.bde.tus.f.bdeSize;
983f1c3b0fcSJames Smart 						dmabuf = bdeBuf1;
984f1c3b0fcSJames Smart 					} else if (i == 1) {
98561910d6aSJames Smart 						size = iocbq->unsol_rcv_len;
986f1c3b0fcSJames Smart 						dmabuf = bdeBuf2;
987f1c3b0fcSJames Smart 					}
988f1c3b0fcSJames Smart 					if ((offset + size) > evt_dat->len)
989f1c3b0fcSJames Smart 						size = evt_dat->len - offset;
990f1c3b0fcSJames Smart 				} else {
991f1c3b0fcSJames Smart 					size = iocbq->iocb.un.cont64[i].
992f1c3b0fcSJames Smart 						tus.f.bdeSize;
993f1c3b0fcSJames Smart 					bde = &iocbq->iocb.un.cont64[i];
994f1c3b0fcSJames Smart 					dma_addr = getPaddr(bde->addrHigh,
995f1c3b0fcSJames Smart 							    bde->addrLow);
996f1c3b0fcSJames Smart 					dmabuf = lpfc_sli_ringpostbuf_get(phba,
997f1c3b0fcSJames Smart 							pring, dma_addr);
998f1c3b0fcSJames Smart 				}
999f1c3b0fcSJames Smart 				if (!dmabuf) {
1000f1c3b0fcSJames Smart 					lpfc_printf_log(phba, KERN_ERR,
1001f1c3b0fcSJames Smart 						LOG_LIBDFC, "2616 No dmabuf "
100232350664SJames Smart 						"found for iocbq x%px\n",
1003f1c3b0fcSJames Smart 						iocbq);
1004f1c3b0fcSJames Smart 					kfree(evt_dat->data);
1005f1c3b0fcSJames Smart 					kfree(evt_dat);
10064fede78fSJames Smart 					spin_lock_irqsave(&phba->ct_ev_lock,
10074fede78fSJames Smart 						flags);
10084cc0e56eSJames Smart 					lpfc_bsg_event_unref(evt);
10094fede78fSJames Smart 					spin_unlock_irqrestore(
10104fede78fSJames Smart 						&phba->ct_ev_lock, flags);
1011f1c3b0fcSJames Smart 					goto error_ct_unsol_exit;
1012f1c3b0fcSJames Smart 				}
1013f1c3b0fcSJames Smart 				memcpy((char *)(evt_dat->data) + offset,
1014f1c3b0fcSJames Smart 				       dmabuf->virt, size);
1015f1c3b0fcSJames Smart 				offset += size;
1016f1c3b0fcSJames Smart 				if (evt_req_id != SLI_CT_ELX_LOOPBACK &&
1017f1c3b0fcSJames Smart 				    !(phba->sli3_options &
1018f1c3b0fcSJames Smart 				      LPFC_SLI3_HBQ_ENABLED)) {
1019f1c3b0fcSJames Smart 					lpfc_sli_ringpostbuf_put(phba, pring,
1020f1c3b0fcSJames Smart 								 dmabuf);
1021f1c3b0fcSJames Smart 				} else {
1022f1c3b0fcSJames Smart 					switch (cmd) {
10234cc0e56eSJames Smart 					case ELX_LOOPBACK_DATA:
10241b51197dSJames Smart 						if (phba->sli_rev <
10251b51197dSJames Smart 						    LPFC_SLI_REV4)
10263b5dd52aSJames Smart 							diag_cmd_data_free(phba,
10271b51197dSJames Smart 							(struct lpfc_dmabufext
10281b51197dSJames Smart 							 *)dmabuf);
10294cc0e56eSJames Smart 						break;
1030f1c3b0fcSJames Smart 					case ELX_LOOPBACK_XRI_SETUP:
10314cc0e56eSJames Smart 						if ((phba->sli_rev ==
10324cc0e56eSJames Smart 							LPFC_SLI_REV2) ||
10334cc0e56eSJames Smart 							(phba->sli3_options &
10344cc0e56eSJames Smart 							LPFC_SLI3_HBQ_ENABLED
10354cc0e56eSJames Smart 							)) {
10364cc0e56eSJames Smart 							lpfc_in_buf_free(phba,
10374cc0e56eSJames Smart 									dmabuf);
10384cc0e56eSJames Smart 						} else {
1039a680a929SJames Smart 							lpfc_sli3_post_buffer(phba,
1040f1c3b0fcSJames Smart 									      pring,
1041f1c3b0fcSJames Smart 									      1);
10424cc0e56eSJames Smart 						}
1043f1c3b0fcSJames Smart 						break;
1044f1c3b0fcSJames Smart 					default:
1045f1c3b0fcSJames Smart 						if (!(phba->sli3_options &
1046f1c3b0fcSJames Smart 						      LPFC_SLI3_HBQ_ENABLED))
1047a680a929SJames Smart 							lpfc_sli3_post_buffer(phba,
1048f1c3b0fcSJames Smart 									      pring,
1049f1c3b0fcSJames Smart 									      1);
1050f1c3b0fcSJames Smart 						break;
1051f1c3b0fcSJames Smart 					}
1052f1c3b0fcSJames Smart 				}
1053f1c3b0fcSJames Smart 			}
1054f1c3b0fcSJames Smart 		}
1055f1c3b0fcSJames Smart 
10564fede78fSJames Smart 		spin_lock_irqsave(&phba->ct_ev_lock, flags);
1057f1c3b0fcSJames Smart 		if (phba->sli_rev == LPFC_SLI_REV4) {
1058f1c3b0fcSJames Smart 			evt_dat->immed_dat = phba->ctx_idx;
10596dd9e31cSJames Smart 			phba->ctx_idx = (phba->ctx_idx + 1) % LPFC_CT_CTX_MAX;
1060589a52d6SJames Smart 			/* Provide warning for over-run of the ct_ctx array */
10616dd9e31cSJames Smart 			if (phba->ct_ctx[evt_dat->immed_dat].valid ==
1062589a52d6SJames Smart 			    UNSOL_VALID)
1063589a52d6SJames Smart 				lpfc_printf_log(phba, KERN_WARNING, LOG_ELS,
1064589a52d6SJames Smart 						"2717 CT context array entry "
1065589a52d6SJames Smart 						"[%d] over-run: oxid:x%x, "
1066589a52d6SJames Smart 						"sid:x%x\n", phba->ctx_idx,
1067589a52d6SJames Smart 						phba->ct_ctx[
1068589a52d6SJames Smart 						    evt_dat->immed_dat].oxid,
1069589a52d6SJames Smart 						phba->ct_ctx[
1070589a52d6SJames Smart 						    evt_dat->immed_dat].SID);
10717851fe2cSJames Smart 			phba->ct_ctx[evt_dat->immed_dat].rxid =
107261910d6aSJames Smart 				get_job_ulpcontext(phba, piocbq);
10737851fe2cSJames Smart 			phba->ct_ctx[evt_dat->immed_dat].oxid =
107461910d6aSJames Smart 				get_job_rcvoxid(phba, piocbq);
1075f1c3b0fcSJames Smart 			phba->ct_ctx[evt_dat->immed_dat].SID =
107661910d6aSJames Smart 				bf_get(wqe_els_did,
107761910d6aSJames Smart 				       &piocbq->wqe.xmit_els_rsp.wqe_dest);
10786dd9e31cSJames Smart 			phba->ct_ctx[evt_dat->immed_dat].valid = UNSOL_VALID;
1079f1c3b0fcSJames Smart 		} else
108061910d6aSJames Smart 			evt_dat->immed_dat = get_job_ulpcontext(phba, piocbq);
1081f1c3b0fcSJames Smart 
1082f1c3b0fcSJames Smart 		evt_dat->type = FC_REG_CT_EVENT;
1083f1c3b0fcSJames Smart 		list_add(&evt_dat->node, &evt->events_to_see);
10844cc0e56eSJames Smart 		if (evt_req_id == SLI_CT_ELX_LOOPBACK) {
1085f1c3b0fcSJames Smart 			wake_up_interruptible(&evt->wq);
10864cc0e56eSJames Smart 			lpfc_bsg_event_unref(evt);
1087f1c3b0fcSJames Smart 			break;
1088f1c3b0fcSJames Smart 		}
10894cc0e56eSJames Smart 
10904cc0e56eSJames Smart 		list_move(evt->events_to_see.prev, &evt->events_to_get);
10914cc0e56eSJames Smart 
1092a33c4f7bSJames Smart 		dd_data = (struct bsg_job_data *)evt->dd_data;
1093a33c4f7bSJames Smart 		job = dd_data->set_job;
1094a33c4f7bSJames Smart 		dd_data->set_job = NULL;
1095a33c4f7bSJames Smart 		lpfc_bsg_event_unref(evt);
10964cc0e56eSJames Smart 		if (job) {
109701e0e15cSJohannes Thumshirn 			bsg_reply = job->reply;
109801e0e15cSJohannes Thumshirn 			bsg_reply->reply_payload_rcv_len = size;
10994cc0e56eSJames Smart 			/* make error code available to userspace */
110001e0e15cSJohannes Thumshirn 			bsg_reply->result = 0;
11014cc0e56eSJames Smart 			job->dd_data = NULL;
11024cc0e56eSJames Smart 			/* complete the job back to userspace */
11034cc0e56eSJames Smart 			spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
110406548160SJohannes Thumshirn 			bsg_job_done(job, bsg_reply->result,
11051abaede7SJohannes Thumshirn 				       bsg_reply->reply_payload_rcv_len);
11064cc0e56eSJames Smart 			spin_lock_irqsave(&phba->ct_ev_lock, flags);
11074cc0e56eSJames Smart 		}
11084cc0e56eSJames Smart 	}
11094fede78fSJames Smart 	spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
1110f1c3b0fcSJames Smart 
1111f1c3b0fcSJames Smart error_ct_unsol_exit:
1112f1c3b0fcSJames Smart 	if (!list_empty(&head))
1113f1c3b0fcSJames Smart 		list_del(&head);
11141b51197dSJames Smart 	if ((phba->sli_rev < LPFC_SLI_REV4) &&
11151b51197dSJames Smart 	    (evt_req_id == SLI_CT_ELX_LOOPBACK))
11164cc0e56eSJames Smart 		return 0;
11174fede78fSJames Smart 	return 1;
1118f1c3b0fcSJames Smart }
1119f1c3b0fcSJames Smart 
1120f1c3b0fcSJames Smart /**
11216dd9e31cSJames Smart  * lpfc_bsg_ct_unsol_abort - handler ct abort to management plane
11226dd9e31cSJames Smart  * @phba: Pointer to HBA context object.
11236dd9e31cSJames Smart  * @dmabuf: pointer to a dmabuf that describes the FC sequence
11246dd9e31cSJames Smart  *
11256dd9e31cSJames Smart  * This function handles abort to the CT command toward management plane
11266dd9e31cSJames Smart  * for SLI4 port.
11276dd9e31cSJames Smart  *
11286dd9e31cSJames Smart  * If the pending context of a CT command to management plane present, clears
11296dd9e31cSJames Smart  * such context and returns 1 for handled; otherwise, it returns 0 indicating
11306dd9e31cSJames Smart  * no context exists.
11316dd9e31cSJames Smart  **/
11326dd9e31cSJames Smart int
11336dd9e31cSJames Smart lpfc_bsg_ct_unsol_abort(struct lpfc_hba *phba, struct hbq_dmabuf *dmabuf)
11346dd9e31cSJames Smart {
11356dd9e31cSJames Smart 	struct fc_frame_header fc_hdr;
11366dd9e31cSJames Smart 	struct fc_frame_header *fc_hdr_ptr = &fc_hdr;
11376dd9e31cSJames Smart 	int ctx_idx, handled = 0;
11386dd9e31cSJames Smart 	uint16_t oxid, rxid;
11396dd9e31cSJames Smart 	uint32_t sid;
11406dd9e31cSJames Smart 
11416dd9e31cSJames Smart 	memcpy(fc_hdr_ptr, dmabuf->hbuf.virt, sizeof(struct fc_frame_header));
11426dd9e31cSJames Smart 	sid = sli4_sid_from_fc_hdr(fc_hdr_ptr);
11436dd9e31cSJames Smart 	oxid = be16_to_cpu(fc_hdr_ptr->fh_ox_id);
11446dd9e31cSJames Smart 	rxid = be16_to_cpu(fc_hdr_ptr->fh_rx_id);
11456dd9e31cSJames Smart 
11466dd9e31cSJames Smart 	for (ctx_idx = 0; ctx_idx < LPFC_CT_CTX_MAX; ctx_idx++) {
11476dd9e31cSJames Smart 		if (phba->ct_ctx[ctx_idx].valid != UNSOL_VALID)
11486dd9e31cSJames Smart 			continue;
11496dd9e31cSJames Smart 		if (phba->ct_ctx[ctx_idx].rxid != rxid)
11506dd9e31cSJames Smart 			continue;
11516dd9e31cSJames Smart 		if (phba->ct_ctx[ctx_idx].oxid != oxid)
11526dd9e31cSJames Smart 			continue;
11536dd9e31cSJames Smart 		if (phba->ct_ctx[ctx_idx].SID != sid)
11546dd9e31cSJames Smart 			continue;
11556dd9e31cSJames Smart 		phba->ct_ctx[ctx_idx].valid = UNSOL_INVALID;
11566dd9e31cSJames Smart 		handled = 1;
11576dd9e31cSJames Smart 	}
11586dd9e31cSJames Smart 	return handled;
11596dd9e31cSJames Smart }
11606dd9e31cSJames Smart 
11616dd9e31cSJames Smart /**
11624cc0e56eSJames Smart  * lpfc_bsg_hba_set_event - process a SET_EVENT bsg vendor command
1163f1c3b0fcSJames Smart  * @job: SET_EVENT fc_bsg_job
11643b5dd52aSJames Smart  **/
1165f1c3b0fcSJames Smart static int
116675cc8cfcSJohannes Thumshirn lpfc_bsg_hba_set_event(struct bsg_job *job)
1167f1c3b0fcSJames Smart {
1168cd21c605SJohannes Thumshirn 	struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
1169f1c3b0fcSJames Smart 	struct lpfc_hba *phba = vport->phba;
117001e0e15cSJohannes Thumshirn 	struct fc_bsg_request *bsg_request = job->request;
1171f1c3b0fcSJames Smart 	struct set_ct_event *event_req;
11724cc0e56eSJames Smart 	struct lpfc_bsg_event *evt;
1173f1c3b0fcSJames Smart 	int rc = 0;
11744cc0e56eSJames Smart 	struct bsg_job_data *dd_data = NULL;
11754cc0e56eSJames Smart 	uint32_t ev_mask;
11764cc0e56eSJames Smart 	unsigned long flags;
1177f1c3b0fcSJames Smart 
1178f1c3b0fcSJames Smart 	if (job->request_len <
1179f1c3b0fcSJames Smart 	    sizeof(struct fc_bsg_request) + sizeof(struct set_ct_event)) {
1180f1c3b0fcSJames Smart 		lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
1181f1c3b0fcSJames Smart 				"2612 Received SET_CT_EVENT below minimum "
1182f1c3b0fcSJames Smart 				"size\n");
11834cc0e56eSJames Smart 		rc = -EINVAL;
11844cc0e56eSJames Smart 		goto job_error;
11854cc0e56eSJames Smart 	}
11864cc0e56eSJames Smart 
1187f1c3b0fcSJames Smart 	event_req = (struct set_ct_event *)
118801e0e15cSJohannes Thumshirn 		bsg_request->rqst_data.h_vendor.vendor_cmd;
11894cc0e56eSJames Smart 	ev_mask = ((uint32_t)(unsigned long)event_req->type_mask &
11904cc0e56eSJames Smart 				FC_REG_EVENT_MASK);
11914fede78fSJames Smart 	spin_lock_irqsave(&phba->ct_ev_lock, flags);
1192f1c3b0fcSJames Smart 	list_for_each_entry(evt, &phba->ct_ev_waiters, node) {
1193f1c3b0fcSJames Smart 		if (evt->reg_id == event_req->ev_reg_id) {
11944cc0e56eSJames Smart 			lpfc_bsg_event_ref(evt);
1195f1c3b0fcSJames Smart 			evt->wait_time_stamp = jiffies;
1196a33c4f7bSJames Smart 			dd_data = (struct bsg_job_data *)evt->dd_data;
1197f1c3b0fcSJames Smart 			break;
1198f1c3b0fcSJames Smart 		}
1199f1c3b0fcSJames Smart 	}
12004fede78fSJames Smart 	spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
1201f1c3b0fcSJames Smart 
1202f1c3b0fcSJames Smart 	if (&evt->node == &phba->ct_ev_waiters) {
1203f1c3b0fcSJames Smart 		/* no event waiting struct yet - first call */
1204a33c4f7bSJames Smart 		dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
1205a33c4f7bSJames Smart 		if (dd_data == NULL) {
1206a33c4f7bSJames Smart 			lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
1207a33c4f7bSJames Smart 					"2734 Failed allocation of dd_data\n");
1208a33c4f7bSJames Smart 			rc = -ENOMEM;
1209a33c4f7bSJames Smart 			goto job_error;
1210a33c4f7bSJames Smart 		}
12114cc0e56eSJames Smart 		evt = lpfc_bsg_event_new(ev_mask, event_req->ev_reg_id,
1212f1c3b0fcSJames Smart 					event_req->ev_req_id);
1213f1c3b0fcSJames Smart 		if (!evt) {
1214f1c3b0fcSJames Smart 			lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
1215f1c3b0fcSJames Smart 					"2617 Failed allocation of event "
1216f1c3b0fcSJames Smart 					"waiter\n");
12174cc0e56eSJames Smart 			rc = -ENOMEM;
12184cc0e56eSJames Smart 			goto job_error;
1219f1c3b0fcSJames Smart 		}
1220a33c4f7bSJames Smart 		dd_data->type = TYPE_EVT;
1221a33c4f7bSJames Smart 		dd_data->set_job = NULL;
1222a33c4f7bSJames Smart 		dd_data->context_un.evt = evt;
1223a33c4f7bSJames Smart 		evt->dd_data = (void *)dd_data;
12244fede78fSJames Smart 		spin_lock_irqsave(&phba->ct_ev_lock, flags);
1225f1c3b0fcSJames Smart 		list_add(&evt->node, &phba->ct_ev_waiters);
12264cc0e56eSJames Smart 		lpfc_bsg_event_ref(evt);
1227f1c3b0fcSJames Smart 		evt->wait_time_stamp = jiffies;
12284cc0e56eSJames Smart 		spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
12294cc0e56eSJames Smart 	}
1230f1c3b0fcSJames Smart 
12314fede78fSJames Smart 	spin_lock_irqsave(&phba->ct_ev_lock, flags);
12324cc0e56eSJames Smart 	evt->waiting = 1;
1233a33c4f7bSJames Smart 	dd_data->set_job = job; /* for unsolicited command */
12344cc0e56eSJames Smart 	job->dd_data = dd_data; /* for fc transport timeout callback*/
12354fede78fSJames Smart 	spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
12364cc0e56eSJames Smart 	return 0; /* call job done later */
1237f1c3b0fcSJames Smart 
12384cc0e56eSJames Smart job_error:
12394cc0e56eSJames Smart 	kfree(dd_data);
12404cc0e56eSJames Smart 	job->dd_data = NULL;
12414cc0e56eSJames Smart 	return rc;
1242f1c3b0fcSJames Smart }
1243f1c3b0fcSJames Smart 
1244f1c3b0fcSJames Smart /**
12454cc0e56eSJames Smart  * lpfc_bsg_hba_get_event - process a GET_EVENT bsg vendor command
1246f1c3b0fcSJames Smart  * @job: GET_EVENT fc_bsg_job
12473b5dd52aSJames Smart  **/
1248f1c3b0fcSJames Smart static int
124975cc8cfcSJohannes Thumshirn lpfc_bsg_hba_get_event(struct bsg_job *job)
1250f1c3b0fcSJames Smart {
1251cd21c605SJohannes Thumshirn 	struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
1252f1c3b0fcSJames Smart 	struct lpfc_hba *phba = vport->phba;
125301e0e15cSJohannes Thumshirn 	struct fc_bsg_request *bsg_request = job->request;
125401e0e15cSJohannes Thumshirn 	struct fc_bsg_reply *bsg_reply = job->reply;
1255f1c3b0fcSJames Smart 	struct get_ct_event *event_req;
1256f1c3b0fcSJames Smart 	struct get_ct_event_reply *event_reply;
12579a803a74SJames Smart 	struct lpfc_bsg_event *evt, *evt_next;
1258f1c3b0fcSJames Smart 	struct event_data *evt_dat = NULL;
12594fede78fSJames Smart 	unsigned long flags;
12604cc0e56eSJames Smart 	uint32_t rc = 0;
1261f1c3b0fcSJames Smart 
1262f1c3b0fcSJames Smart 	if (job->request_len <
1263f1c3b0fcSJames Smart 	    sizeof(struct fc_bsg_request) + sizeof(struct get_ct_event)) {
1264f1c3b0fcSJames Smart 		lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
1265f1c3b0fcSJames Smart 				"2613 Received GET_CT_EVENT request below "
1266f1c3b0fcSJames Smart 				"minimum size\n");
12674cc0e56eSJames Smart 		rc = -EINVAL;
12684cc0e56eSJames Smart 		goto job_error;
1269f1c3b0fcSJames Smart 	}
1270f1c3b0fcSJames Smart 
1271f1c3b0fcSJames Smart 	event_req = (struct get_ct_event *)
127201e0e15cSJohannes Thumshirn 		bsg_request->rqst_data.h_vendor.vendor_cmd;
1273f1c3b0fcSJames Smart 
1274f1c3b0fcSJames Smart 	event_reply = (struct get_ct_event_reply *)
127501e0e15cSJohannes Thumshirn 		bsg_reply->reply_data.vendor_reply.vendor_rsp;
12764fede78fSJames Smart 	spin_lock_irqsave(&phba->ct_ev_lock, flags);
12779a803a74SJames Smart 	list_for_each_entry_safe(evt, evt_next, &phba->ct_ev_waiters, node) {
1278f1c3b0fcSJames Smart 		if (evt->reg_id == event_req->ev_reg_id) {
1279f1c3b0fcSJames Smart 			if (list_empty(&evt->events_to_get))
1280f1c3b0fcSJames Smart 				break;
12814cc0e56eSJames Smart 			lpfc_bsg_event_ref(evt);
1282f1c3b0fcSJames Smart 			evt->wait_time_stamp = jiffies;
1283f1c3b0fcSJames Smart 			evt_dat = list_entry(evt->events_to_get.prev,
1284f1c3b0fcSJames Smart 					     struct event_data, node);
1285f1c3b0fcSJames Smart 			list_del(&evt_dat->node);
1286f1c3b0fcSJames Smart 			break;
1287f1c3b0fcSJames Smart 		}
1288f1c3b0fcSJames Smart 	}
12894fede78fSJames Smart 	spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
1290f1c3b0fcSJames Smart 
12914cc0e56eSJames Smart 	/* The app may continue to ask for event data until it gets
12924cc0e56eSJames Smart 	 * an error indicating that there isn't anymore
12934cc0e56eSJames Smart 	 */
12944cc0e56eSJames Smart 	if (evt_dat == NULL) {
129501e0e15cSJohannes Thumshirn 		bsg_reply->reply_payload_rcv_len = 0;
1296f1c3b0fcSJames Smart 		rc = -ENOENT;
12974cc0e56eSJames Smart 		goto job_error;
1298f1c3b0fcSJames Smart 	}
1299f1c3b0fcSJames Smart 
13004cc0e56eSJames Smart 	if (evt_dat->len > job->request_payload.payload_len) {
13014cc0e56eSJames Smart 		evt_dat->len = job->request_payload.payload_len;
1302f1c3b0fcSJames Smart 		lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
1303f1c3b0fcSJames Smart 				"2618 Truncated event data at %d "
1304f1c3b0fcSJames Smart 				"bytes\n",
13054cc0e56eSJames Smart 				job->request_payload.payload_len);
1306f1c3b0fcSJames Smart 	}
1307f1c3b0fcSJames Smart 
13084cc0e56eSJames Smart 	event_reply->type = evt_dat->type;
1309f1c3b0fcSJames Smart 	event_reply->immed_data = evt_dat->immed_dat;
1310f1c3b0fcSJames Smart 	if (evt_dat->len > 0)
131101e0e15cSJohannes Thumshirn 		bsg_reply->reply_payload_rcv_len =
13124cc0e56eSJames Smart 			sg_copy_from_buffer(job->request_payload.sg_list,
13134cc0e56eSJames Smart 					    job->request_payload.sg_cnt,
1314f1c3b0fcSJames Smart 					    evt_dat->data, evt_dat->len);
1315f1c3b0fcSJames Smart 	else
131601e0e15cSJohannes Thumshirn 		bsg_reply->reply_payload_rcv_len = 0;
1317f1c3b0fcSJames Smart 
13184cc0e56eSJames Smart 	if (evt_dat) {
1319f1c3b0fcSJames Smart 		kfree(evt_dat->data);
1320f1c3b0fcSJames Smart 		kfree(evt_dat);
13214cc0e56eSJames Smart 	}
13224cc0e56eSJames Smart 
13234fede78fSJames Smart 	spin_lock_irqsave(&phba->ct_ev_lock, flags);
13244cc0e56eSJames Smart 	lpfc_bsg_event_unref(evt);
13254fede78fSJames Smart 	spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
13264cc0e56eSJames Smart 	job->dd_data = NULL;
132701e0e15cSJohannes Thumshirn 	bsg_reply->result = 0;
132806548160SJohannes Thumshirn 	bsg_job_done(job, bsg_reply->result,
13291abaede7SJohannes Thumshirn 		       bsg_reply->reply_payload_rcv_len);
13304cc0e56eSJames Smart 	return 0;
1331f1c3b0fcSJames Smart 
13324cc0e56eSJames Smart job_error:
13334cc0e56eSJames Smart 	job->dd_data = NULL;
133401e0e15cSJohannes Thumshirn 	bsg_reply->result = rc;
1335f1c3b0fcSJames Smart 	return rc;
1336f1c3b0fcSJames Smart }
1337f1c3b0fcSJames Smart 
1338f1c3b0fcSJames Smart /**
13393b5dd52aSJames Smart  * lpfc_issue_ct_rsp_cmp - lpfc_issue_ct_rsp's completion handler
13403b5dd52aSJames Smart  * @phba: Pointer to HBA context object.
13413b5dd52aSJames Smart  * @cmdiocbq: Pointer to command iocb.
13423b5dd52aSJames Smart  * @rspiocbq: Pointer to response iocb.
13433b5dd52aSJames Smart  *
13443b5dd52aSJames Smart  * This function is the completion handler for iocbs issued using
13453b5dd52aSJames Smart  * lpfc_issue_ct_rsp_cmp function. This function is called by the
13463b5dd52aSJames Smart  * ring event handler function without any lock held. This function
13473b5dd52aSJames Smart  * can be called from both worker thread context and interrupt
13483b5dd52aSJames Smart  * context. This function also can be called from other thread which
13493b5dd52aSJames Smart  * cleans up the SLI layer objects.
13503b5dd52aSJames Smart  * This function copy the contents of the response iocb to the
13513b5dd52aSJames Smart  * response iocb memory object provided by the caller of
13523b5dd52aSJames Smart  * lpfc_sli_issue_iocb_wait and then wakes up the thread which
13533b5dd52aSJames Smart  * sleeps for the iocb completion.
13543b5dd52aSJames Smart  **/
13553b5dd52aSJames Smart static void
13563b5dd52aSJames Smart lpfc_issue_ct_rsp_cmp(struct lpfc_hba *phba,
13573b5dd52aSJames Smart 			struct lpfc_iocbq *cmdiocbq,
13583b5dd52aSJames Smart 			struct lpfc_iocbq *rspiocbq)
13593b5dd52aSJames Smart {
13603b5dd52aSJames Smart 	struct bsg_job_data *dd_data;
136175cc8cfcSJohannes Thumshirn 	struct bsg_job *job;
136201e0e15cSJohannes Thumshirn 	struct fc_bsg_reply *bsg_reply;
1363a33c4f7bSJames Smart 	struct lpfc_dmabuf *bmp, *cmp;
13643b5dd52aSJames Smart 	struct lpfc_nodelist *ndlp;
13653b5dd52aSJames Smart 	unsigned long flags;
13663b5dd52aSJames Smart 	int rc = 0;
13670e082d92SJames Smart 	u32 ulp_status, ulp_word4;
13683b5dd52aSJames Smart 
1369d51cf5bdSJames Smart 	dd_data = cmdiocbq->context_un.dd_data;
1370a33c4f7bSJames Smart 
1371a33c4f7bSJames Smart 	/* Determine if job has been aborted */
13723b5dd52aSJames Smart 	spin_lock_irqsave(&phba->ct_ev_lock, flags);
1373a33c4f7bSJames Smart 	job = dd_data->set_job;
1374a33c4f7bSJames Smart 	if (job) {
1375a33c4f7bSJames Smart 		/* Prevent timeout handling from trying to abort job  */
1376a33c4f7bSJames Smart 		job->dd_data = NULL;
13773b5dd52aSJames Smart 	}
1378a33c4f7bSJames Smart 	spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
13793b5dd52aSJames Smart 
1380b5a9b2dfSJames Smart 	/* Close the timeout handler abort window */
1381b5a9b2dfSJames Smart 	spin_lock_irqsave(&phba->hbalock, flags);
1382a680a929SJames Smart 	cmdiocbq->cmd_flag &= ~LPFC_IO_CMD_OUTSTANDING;
1383b5a9b2dfSJames Smart 	spin_unlock_irqrestore(&phba->hbalock, flags);
1384b5a9b2dfSJames Smart 
13853b5dd52aSJames Smart 	ndlp = dd_data->context_un.iocb.ndlp;
1386d51cf5bdSJames Smart 	cmp = cmdiocbq->cmd_dmabuf;
1387d51cf5bdSJames Smart 	bmp = cmdiocbq->bpl_dmabuf;
13880e082d92SJames Smart 
13890e082d92SJames Smart 	ulp_status = get_job_ulpstatus(phba, rspiocbq);
13900e082d92SJames Smart 	ulp_word4 = get_job_word4(phba, rspiocbq);
13913b5dd52aSJames Smart 
1392a33c4f7bSJames Smart 	/* Copy the completed job data or set the error status */
13933b5dd52aSJames Smart 
1394a33c4f7bSJames Smart 	if (job) {
139501e0e15cSJohannes Thumshirn 		bsg_reply = job->reply;
13960e082d92SJames Smart 		if (ulp_status) {
13970e082d92SJames Smart 			if (ulp_status == IOSTAT_LOCAL_REJECT) {
13980e082d92SJames Smart 				switch (ulp_word4 & IOERR_PARAM_MASK) {
13993b5dd52aSJames Smart 				case IOERR_SEQUENCE_TIMEOUT:
14003b5dd52aSJames Smart 					rc = -ETIMEDOUT;
14013b5dd52aSJames Smart 					break;
14023b5dd52aSJames Smart 				case IOERR_INVALID_RPI:
14033b5dd52aSJames Smart 					rc = -EFAULT;
14043b5dd52aSJames Smart 					break;
14053b5dd52aSJames Smart 				default:
14063b5dd52aSJames Smart 					rc = -EACCES;
14073b5dd52aSJames Smart 					break;
14083b5dd52aSJames Smart 				}
1409a33c4f7bSJames Smart 			} else {
14103b5dd52aSJames Smart 				rc = -EACCES;
1411a33c4f7bSJames Smart 			}
1412a33c4f7bSJames Smart 		} else {
141301e0e15cSJohannes Thumshirn 			bsg_reply->reply_payload_rcv_len = 0;
1414a33c4f7bSJames Smart 		}
1415a33c4f7bSJames Smart 	}
14163b5dd52aSJames Smart 
1417a33c4f7bSJames Smart 	lpfc_free_bsg_buffers(phba, cmp);
14183b5dd52aSJames Smart 	lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
1419a33c4f7bSJames Smart 	kfree(bmp);
14203b5dd52aSJames Smart 	lpfc_sli_release_iocbq(phba, cmdiocbq);
14213b5dd52aSJames Smart 	lpfc_nlp_put(ndlp);
14223b5dd52aSJames Smart 	kfree(dd_data);
1423a33c4f7bSJames Smart 
1424a33c4f7bSJames Smart 	/* Complete the job if the job is still active */
1425a33c4f7bSJames Smart 
1426a33c4f7bSJames Smart 	if (job) {
142701e0e15cSJohannes Thumshirn 		bsg_reply->result = rc;
142806548160SJohannes Thumshirn 		bsg_job_done(job, bsg_reply->result,
14291abaede7SJohannes Thumshirn 			       bsg_reply->reply_payload_rcv_len);
1430a33c4f7bSJames Smart 	}
14313b5dd52aSJames Smart 	return;
14323b5dd52aSJames Smart }
14333b5dd52aSJames Smart 
14343b5dd52aSJames Smart /**
14353b5dd52aSJames Smart  * lpfc_issue_ct_rsp - issue a ct response
14363b5dd52aSJames Smart  * @phba: Pointer to HBA context object.
14373b5dd52aSJames Smart  * @job: Pointer to the job object.
14383b5dd52aSJames Smart  * @tag: tag index value into the ports context exchange array.
1439ea085dabSLee Jones  * @cmp: Pointer to a cmp dma buffer descriptor.
1440ea085dabSLee Jones  * @bmp: Pointer to a bmp dma buffer descriptor.
14413b5dd52aSJames Smart  * @num_entry: Number of enties in the bde.
14423b5dd52aSJames Smart  **/
14433b5dd52aSJames Smart static int
144475cc8cfcSJohannes Thumshirn lpfc_issue_ct_rsp(struct lpfc_hba *phba, struct bsg_job *job, uint32_t tag,
1445a33c4f7bSJames Smart 		  struct lpfc_dmabuf *cmp, struct lpfc_dmabuf *bmp,
1446a33c4f7bSJames Smart 		  int num_entry)
14473b5dd52aSJames Smart {
14483b5dd52aSJames Smart 	struct lpfc_iocbq *ctiocb = NULL;
14493b5dd52aSJames Smart 	int rc = 0;
14503b5dd52aSJames Smart 	struct lpfc_nodelist *ndlp = NULL;
14513b5dd52aSJames Smart 	struct bsg_job_data *dd_data;
1452b5a9b2dfSJames Smart 	unsigned long flags;
14533b5dd52aSJames Smart 	uint32_t creg_val;
145461910d6aSJames Smart 	u16 ulp_context, iotag;
14553b5dd52aSJames Smart 
14564430f7fdSJames Smart 	ndlp = lpfc_findnode_did(phba->pport, phba->ct_ctx[tag].SID);
14574430f7fdSJames Smart 	if (!ndlp) {
14584430f7fdSJames Smart 		lpfc_printf_log(phba, KERN_WARNING, LOG_ELS,
14594430f7fdSJames Smart 				"2721 ndlp null for oxid %x SID %x\n",
14604430f7fdSJames Smart 				phba->ct_ctx[tag].rxid,
14614430f7fdSJames Smart 				phba->ct_ctx[tag].SID);
14624430f7fdSJames Smart 		return IOCB_ERROR;
14634430f7fdSJames Smart 	}
14644430f7fdSJames Smart 
14653b5dd52aSJames Smart 	/* allocate our bsg tracking structure */
14663b5dd52aSJames Smart 	dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
14673b5dd52aSJames Smart 	if (!dd_data) {
14683b5dd52aSJames Smart 		lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
14693b5dd52aSJames Smart 				"2736 Failed allocation of dd_data\n");
14703b5dd52aSJames Smart 		rc = -ENOMEM;
14713b5dd52aSJames Smart 		goto no_dd_data;
14723b5dd52aSJames Smart 	}
14733b5dd52aSJames Smart 
14743b5dd52aSJames Smart 	/* Allocate buffer for  command iocb */
14753b5dd52aSJames Smart 	ctiocb = lpfc_sli_get_iocbq(phba);
14763b5dd52aSJames Smart 	if (!ctiocb) {
1477d439d286SJames Smart 		rc = -ENOMEM;
14783b5dd52aSJames Smart 		goto no_ctiocb;
14793b5dd52aSJames Smart 	}
14803b5dd52aSJames Smart 
14813b5dd52aSJames Smart 	if (phba->sli_rev == LPFC_SLI_REV4) {
14823b5dd52aSJames Smart 		/* Do not issue unsol response if oxid not marked as valid */
14836dd9e31cSJames Smart 		if (phba->ct_ctx[tag].valid != UNSOL_VALID) {
14843b5dd52aSJames Smart 			rc = IOCB_ERROR;
14853b5dd52aSJames Smart 			goto issue_ct_rsp_exit;
14863b5dd52aSJames Smart 		}
1487589a52d6SJames Smart 
148861910d6aSJames Smart 		lpfc_sli_prep_xmit_seq64(phba, ctiocb, bmp,
148961910d6aSJames Smart 					 phba->sli4_hba.rpi_ids[ndlp->nlp_rpi],
149061910d6aSJames Smart 					 phba->ct_ctx[tag].oxid, num_entry,
149161910d6aSJames Smart 					 FC_RCTL_DD_SOL_CTL, 1,
149261910d6aSJames Smart 					 CMD_XMIT_SEQUENCE64_WQE);
14936d368e53SJames Smart 
14943b5dd52aSJames Smart 		/* The exchange is done, mark the entry as invalid */
14956dd9e31cSJames Smart 		phba->ct_ctx[tag].valid = UNSOL_INVALID;
149661910d6aSJames Smart 		iotag = get_wqe_reqtag(ctiocb);
149761910d6aSJames Smart 	} else {
149861910d6aSJames Smart 		lpfc_sli_prep_xmit_seq64(phba, ctiocb, bmp, 0, tag, num_entry,
149961910d6aSJames Smart 					 FC_RCTL_DD_SOL_CTL, 1,
150061910d6aSJames Smart 					 CMD_XMIT_SEQUENCE64_CX);
150161910d6aSJames Smart 		ctiocb->num_bdes = num_entry;
150261910d6aSJames Smart 		iotag = ctiocb->iocb.ulpIoTag;
150361910d6aSJames Smart 	}
15043b5dd52aSJames Smart 
150561910d6aSJames Smart 	ulp_context = get_job_ulpcontext(phba, ctiocb);
15063b5dd52aSJames Smart 
15073b5dd52aSJames Smart 	/* Xmit CT response on exchange <xid> */
15083b5dd52aSJames Smart 	lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
15097851fe2cSJames Smart 			"2722 Xmit CT response on exchange x%x Data: x%x x%x x%x\n",
151061910d6aSJames Smart 			ulp_context, iotag, tag, phba->link_state);
15113b5dd52aSJames Smart 
1512a680a929SJames Smart 	ctiocb->cmd_flag |= LPFC_IO_LIBDFC;
15133b5dd52aSJames Smart 	ctiocb->vport = phba->pport;
1514d51cf5bdSJames Smart 	ctiocb->context_un.dd_data = dd_data;
1515d51cf5bdSJames Smart 	ctiocb->cmd_dmabuf = cmp;
1516d51cf5bdSJames Smart 	ctiocb->bpl_dmabuf = bmp;
1517d51cf5bdSJames Smart 	ctiocb->ndlp = ndlp;
1518a680a929SJames Smart 	ctiocb->cmd_cmpl = lpfc_issue_ct_rsp_cmp;
1519a33c4f7bSJames Smart 
15203b5dd52aSJames Smart 	dd_data->type = TYPE_IOCB;
1521a33c4f7bSJames Smart 	dd_data->set_job = job;
15223b5dd52aSJames Smart 	dd_data->context_un.iocb.cmdiocbq = ctiocb;
15234430f7fdSJames Smart 	dd_data->context_un.iocb.ndlp = lpfc_nlp_get(ndlp);
15244430f7fdSJames Smart 	if (!dd_data->context_un.iocb.ndlp) {
15254430f7fdSJames Smart 		rc = -IOCB_ERROR;
15264430f7fdSJames Smart 		goto issue_ct_rsp_exit;
15274430f7fdSJames Smart 	}
1528a33c4f7bSJames Smart 	dd_data->context_un.iocb.rmp = NULL;
1529a33c4f7bSJames Smart 	job->dd_data = dd_data;
15303b5dd52aSJames Smart 
15313b5dd52aSJames Smart 	if (phba->cfg_poll & DISABLE_FCP_RING_INT) {
15329940b97bSJames Smart 		if (lpfc_readl(phba->HCregaddr, &creg_val)) {
15339940b97bSJames Smart 			rc = -IOCB_ERROR;
15349940b97bSJames Smart 			goto issue_ct_rsp_exit;
15359940b97bSJames Smart 		}
15363b5dd52aSJames Smart 		creg_val |= (HC_R0INT_ENA << LPFC_FCP_RING);
15373b5dd52aSJames Smart 		writel(creg_val, phba->HCregaddr);
15383b5dd52aSJames Smart 		readl(phba->HCregaddr); /* flush */
15393b5dd52aSJames Smart 	}
15403b5dd52aSJames Smart 
15413b5dd52aSJames Smart 	rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, ctiocb, 0);
1542b5a9b2dfSJames Smart 	if (rc == IOCB_SUCCESS) {
1543b5a9b2dfSJames Smart 		spin_lock_irqsave(&phba->hbalock, flags);
1544b5a9b2dfSJames Smart 		/* make sure the I/O had not been completed/released */
1545a680a929SJames Smart 		if (ctiocb->cmd_flag & LPFC_IO_LIBDFC) {
1546b5a9b2dfSJames Smart 			/* open up abort window to timeout handler */
1547a680a929SJames Smart 			ctiocb->cmd_flag |= LPFC_IO_CMD_OUTSTANDING;
1548b5a9b2dfSJames Smart 		}
1549b5a9b2dfSJames Smart 		spin_unlock_irqrestore(&phba->hbalock, flags);
15503b5dd52aSJames Smart 		return 0; /* done for now */
1551b5a9b2dfSJames Smart 	}
1552b5a9b2dfSJames Smart 
1553b5a9b2dfSJames Smart 	/* iocb failed so cleanup */
1554b5a9b2dfSJames Smart 	job->dd_data = NULL;
15554430f7fdSJames Smart 	lpfc_nlp_put(ndlp);
15563b5dd52aSJames Smart 
15573b5dd52aSJames Smart issue_ct_rsp_exit:
15583b5dd52aSJames Smart 	lpfc_sli_release_iocbq(phba, ctiocb);
15593b5dd52aSJames Smart no_ctiocb:
15603b5dd52aSJames Smart 	kfree(dd_data);
15613b5dd52aSJames Smart no_dd_data:
15623b5dd52aSJames Smart 	return rc;
15633b5dd52aSJames Smart }
15643b5dd52aSJames Smart 
15653b5dd52aSJames Smart /**
15663b5dd52aSJames Smart  * lpfc_bsg_send_mgmt_rsp - process a SEND_MGMT_RESP bsg vendor command
15673b5dd52aSJames Smart  * @job: SEND_MGMT_RESP fc_bsg_job
15683b5dd52aSJames Smart  **/
15693b5dd52aSJames Smart static int
157075cc8cfcSJohannes Thumshirn lpfc_bsg_send_mgmt_rsp(struct bsg_job *job)
15713b5dd52aSJames Smart {
1572cd21c605SJohannes Thumshirn 	struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
15733b5dd52aSJames Smart 	struct lpfc_hba *phba = vport->phba;
157401e0e15cSJohannes Thumshirn 	struct fc_bsg_request *bsg_request = job->request;
157501e0e15cSJohannes Thumshirn 	struct fc_bsg_reply *bsg_reply = job->reply;
15763b5dd52aSJames Smart 	struct send_mgmt_resp *mgmt_resp = (struct send_mgmt_resp *)
157701e0e15cSJohannes Thumshirn 		bsg_request->rqst_data.h_vendor.vendor_cmd;
15783b5dd52aSJames Smart 	struct ulp_bde64 *bpl;
1579a33c4f7bSJames Smart 	struct lpfc_dmabuf *bmp = NULL, *cmp = NULL;
1580a33c4f7bSJames Smart 	int bpl_entries;
15813b5dd52aSJames Smart 	uint32_t tag = mgmt_resp->tag;
15823b5dd52aSJames Smart 	unsigned long reqbfrcnt =
15833b5dd52aSJames Smart 			(unsigned long)job->request_payload.payload_len;
15843b5dd52aSJames Smart 	int rc = 0;
15853b5dd52aSJames Smart 
15863b5dd52aSJames Smart 	/* in case no data is transferred */
158701e0e15cSJohannes Thumshirn 	bsg_reply->reply_payload_rcv_len = 0;
15883b5dd52aSJames Smart 
15893b5dd52aSJames Smart 	if (!reqbfrcnt || (reqbfrcnt > (80 * BUF_SZ_4K))) {
15903b5dd52aSJames Smart 		rc = -ERANGE;
15913b5dd52aSJames Smart 		goto send_mgmt_rsp_exit;
15923b5dd52aSJames Smart 	}
15933b5dd52aSJames Smart 
15943b5dd52aSJames Smart 	bmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
15953b5dd52aSJames Smart 	if (!bmp) {
15963b5dd52aSJames Smart 		rc = -ENOMEM;
15973b5dd52aSJames Smart 		goto send_mgmt_rsp_exit;
15983b5dd52aSJames Smart 	}
15993b5dd52aSJames Smart 
16003b5dd52aSJames Smart 	bmp->virt = lpfc_mbuf_alloc(phba, 0, &bmp->phys);
16013b5dd52aSJames Smart 	if (!bmp->virt) {
16023b5dd52aSJames Smart 		rc = -ENOMEM;
16033b5dd52aSJames Smart 		goto send_mgmt_rsp_free_bmp;
16043b5dd52aSJames Smart 	}
16053b5dd52aSJames Smart 
16063b5dd52aSJames Smart 	INIT_LIST_HEAD(&bmp->list);
16073b5dd52aSJames Smart 	bpl = (struct ulp_bde64 *) bmp->virt;
1608a33c4f7bSJames Smart 	bpl_entries = (LPFC_BPL_SIZE/sizeof(struct ulp_bde64));
1609a33c4f7bSJames Smart 	cmp = lpfc_alloc_bsg_buffers(phba, job->request_payload.payload_len,
1610a33c4f7bSJames Smart 				     1, bpl, &bpl_entries);
1611a33c4f7bSJames Smart 	if (!cmp) {
1612a33c4f7bSJames Smart 		rc = -ENOMEM;
1613a33c4f7bSJames Smart 		goto send_mgmt_rsp_free_bmp;
16143b5dd52aSJames Smart 	}
1615a33c4f7bSJames Smart 	lpfc_bsg_copy_data(cmp, &job->request_payload,
1616a33c4f7bSJames Smart 			   job->request_payload.payload_len, 1);
16173b5dd52aSJames Smart 
1618a33c4f7bSJames Smart 	rc = lpfc_issue_ct_rsp(phba, job, tag, cmp, bmp, bpl_entries);
16193b5dd52aSJames Smart 
16203b5dd52aSJames Smart 	if (rc == IOCB_SUCCESS)
16213b5dd52aSJames Smart 		return 0; /* done for now */
16223b5dd52aSJames Smart 
16233b5dd52aSJames Smart 	rc = -EACCES;
1624a33c4f7bSJames Smart 
1625a33c4f7bSJames Smart 	lpfc_free_bsg_buffers(phba, cmp);
16263b5dd52aSJames Smart 
16273b5dd52aSJames Smart send_mgmt_rsp_free_bmp:
1628a33c4f7bSJames Smart 	if (bmp->virt)
1629a33c4f7bSJames Smart 		lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
16303b5dd52aSJames Smart 	kfree(bmp);
16313b5dd52aSJames Smart send_mgmt_rsp_exit:
16323b5dd52aSJames Smart 	/* make error code available to userspace */
163301e0e15cSJohannes Thumshirn 	bsg_reply->result = rc;
16343b5dd52aSJames Smart 	job->dd_data = NULL;
16353b5dd52aSJames Smart 	return rc;
16363b5dd52aSJames Smart }
16373b5dd52aSJames Smart 
16383b5dd52aSJames Smart /**
16397ad20aa9SJames Smart  * lpfc_bsg_diag_mode_enter - process preparing into device diag loopback mode
16407ad20aa9SJames Smart  * @phba: Pointer to HBA context object.
16413b5dd52aSJames Smart  *
16427ad20aa9SJames Smart  * This function is responsible for preparing driver for diag loopback
16437ad20aa9SJames Smart  * on device.
16443b5dd52aSJames Smart  */
16453b5dd52aSJames Smart static int
164688a2cfbbSJames Smart lpfc_bsg_diag_mode_enter(struct lpfc_hba *phba)
16473b5dd52aSJames Smart {
16483b5dd52aSJames Smart 	struct lpfc_vport **vports;
16497ad20aa9SJames Smart 	struct Scsi_Host *shost;
16507ad20aa9SJames Smart 	struct lpfc_sli *psli;
1651895427bdSJames Smart 	struct lpfc_queue *qp = NULL;
16527ad20aa9SJames Smart 	struct lpfc_sli_ring *pring;
16533b5dd52aSJames Smart 	int i = 0;
16543b5dd52aSJames Smart 
16557ad20aa9SJames Smart 	psli = &phba->sli;
16567ad20aa9SJames Smart 	if (!psli)
16577ad20aa9SJames Smart 		return -ENODEV;
16583b5dd52aSJames Smart 
16593b5dd52aSJames Smart 
16603b5dd52aSJames Smart 	if ((phba->link_state == LPFC_HBA_ERROR) ||
16613b5dd52aSJames Smart 	    (psli->sli_flag & LPFC_BLOCK_MGMT_IO) ||
16627ad20aa9SJames Smart 	    (!(psli->sli_flag & LPFC_SLI_ACTIVE)))
16637ad20aa9SJames Smart 		return -EACCES;
16643b5dd52aSJames Smart 
16653b5dd52aSJames Smart 	vports = lpfc_create_vport_work_array(phba);
16663b5dd52aSJames Smart 	if (vports) {
16673b5dd52aSJames Smart 		for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
16683b5dd52aSJames Smart 			shost = lpfc_shost_from_vport(vports[i]);
16693b5dd52aSJames Smart 			scsi_block_requests(shost);
16703b5dd52aSJames Smart 		}
16713b5dd52aSJames Smart 		lpfc_destroy_vport_work_array(phba, vports);
16723b5dd52aSJames Smart 	} else {
16733b5dd52aSJames Smart 		shost = lpfc_shost_from_vport(phba->pport);
16743b5dd52aSJames Smart 		scsi_block_requests(shost);
16753b5dd52aSJames Smart 	}
16763b5dd52aSJames Smart 
1677895427bdSJames Smart 	if (phba->sli_rev != LPFC_SLI_REV4) {
1678895427bdSJames Smart 		pring = &psli->sli3_ring[LPFC_FCP_RING];
1679895427bdSJames Smart 		lpfc_emptyq_wait(phba, &pring->txcmplq, &phba->hbalock);
1680895427bdSJames Smart 		return 0;
1681895427bdSJames Smart 	}
1682895427bdSJames Smart 	list_for_each_entry(qp, &phba->sli4_hba.lpfc_wq_list, wq_list) {
1683895427bdSJames Smart 		pring = qp->pring;
1684895427bdSJames Smart 		if (!pring || (pring->ringno != LPFC_FCP_RING))
1685895427bdSJames Smart 			continue;
1686895427bdSJames Smart 		if (!lpfc_emptyq_wait(phba, &pring->txcmplq,
1687895427bdSJames Smart 				      &pring->ring_lock))
16883b5dd52aSJames Smart 			break;
16893b5dd52aSJames Smart 	}
16907ad20aa9SJames Smart 	return 0;
16917ad20aa9SJames Smart }
16923b5dd52aSJames Smart 
16937ad20aa9SJames Smart /**
16947ad20aa9SJames Smart  * lpfc_bsg_diag_mode_exit - exit process from device diag loopback mode
16957ad20aa9SJames Smart  * @phba: Pointer to HBA context object.
16967ad20aa9SJames Smart  *
16977ad20aa9SJames Smart  * This function is responsible for driver exit processing of setting up
16987ad20aa9SJames Smart  * diag loopback mode on device.
16997ad20aa9SJames Smart  */
17007ad20aa9SJames Smart static void
17017ad20aa9SJames Smart lpfc_bsg_diag_mode_exit(struct lpfc_hba *phba)
17027ad20aa9SJames Smart {
17037ad20aa9SJames Smart 	struct Scsi_Host *shost;
17047ad20aa9SJames Smart 	struct lpfc_vport **vports;
17057ad20aa9SJames Smart 	int i;
17067ad20aa9SJames Smart 
17077ad20aa9SJames Smart 	vports = lpfc_create_vport_work_array(phba);
17087ad20aa9SJames Smart 	if (vports) {
17097ad20aa9SJames Smart 		for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
17107ad20aa9SJames Smart 			shost = lpfc_shost_from_vport(vports[i]);
17117ad20aa9SJames Smart 			scsi_unblock_requests(shost);
17127ad20aa9SJames Smart 		}
17137ad20aa9SJames Smart 		lpfc_destroy_vport_work_array(phba, vports);
17147ad20aa9SJames Smart 	} else {
17157ad20aa9SJames Smart 		shost = lpfc_shost_from_vport(phba->pport);
17167ad20aa9SJames Smart 		scsi_unblock_requests(shost);
17177ad20aa9SJames Smart 	}
17187ad20aa9SJames Smart 	return;
17197ad20aa9SJames Smart }
17207ad20aa9SJames Smart 
17217ad20aa9SJames Smart /**
17227ad20aa9SJames Smart  * lpfc_sli3_bsg_diag_loopback_mode - process an sli3 bsg vendor command
17237ad20aa9SJames Smart  * @phba: Pointer to HBA context object.
17247ad20aa9SJames Smart  * @job: LPFC_BSG_VENDOR_DIAG_MODE
17257ad20aa9SJames Smart  *
17267ad20aa9SJames Smart  * This function is responsible for placing an sli3  port into diagnostic
17277ad20aa9SJames Smart  * loopback mode in order to perform a diagnostic loopback test.
17287ad20aa9SJames Smart  * All new scsi requests are blocked, a small delay is used to allow the
17297ad20aa9SJames Smart  * scsi requests to complete then the link is brought down. If the link is
17307ad20aa9SJames Smart  * is placed in loopback mode then scsi requests are again allowed
17317ad20aa9SJames Smart  * so the scsi mid-layer doesn't give up on the port.
17327ad20aa9SJames Smart  * All of this is done in-line.
17337ad20aa9SJames Smart  */
17347ad20aa9SJames Smart static int
173575cc8cfcSJohannes Thumshirn lpfc_sli3_bsg_diag_loopback_mode(struct lpfc_hba *phba, struct bsg_job *job)
17367ad20aa9SJames Smart {
173701e0e15cSJohannes Thumshirn 	struct fc_bsg_request *bsg_request = job->request;
173801e0e15cSJohannes Thumshirn 	struct fc_bsg_reply *bsg_reply = job->reply;
17397ad20aa9SJames Smart 	struct diag_mode_set *loopback_mode;
17407ad20aa9SJames Smart 	uint32_t link_flags;
17417ad20aa9SJames Smart 	uint32_t timeout;
17421b51197dSJames Smart 	LPFC_MBOXQ_t *pmboxq  = NULL;
1743b76f2dc9SJames Smart 	int mbxstatus = MBX_SUCCESS;
17447ad20aa9SJames Smart 	int i = 0;
17457ad20aa9SJames Smart 	int rc = 0;
17467ad20aa9SJames Smart 
17477ad20aa9SJames Smart 	/* no data to return just the return code */
174801e0e15cSJohannes Thumshirn 	bsg_reply->reply_payload_rcv_len = 0;
17497ad20aa9SJames Smart 
17507ad20aa9SJames Smart 	if (job->request_len < sizeof(struct fc_bsg_request) +
17517ad20aa9SJames Smart 	    sizeof(struct diag_mode_set)) {
17527ad20aa9SJames Smart 		lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
17537ad20aa9SJames Smart 				"2738 Received DIAG MODE request size:%d "
17547ad20aa9SJames Smart 				"below the minimum size:%d\n",
17557ad20aa9SJames Smart 				job->request_len,
17567ad20aa9SJames Smart 				(int)(sizeof(struct fc_bsg_request) +
17577ad20aa9SJames Smart 				sizeof(struct diag_mode_set)));
17587ad20aa9SJames Smart 		rc = -EINVAL;
17597ad20aa9SJames Smart 		goto job_error;
17607ad20aa9SJames Smart 	}
17617ad20aa9SJames Smart 
176288a2cfbbSJames Smart 	rc = lpfc_bsg_diag_mode_enter(phba);
17637ad20aa9SJames Smart 	if (rc)
17647ad20aa9SJames Smart 		goto job_error;
17657ad20aa9SJames Smart 
17667ad20aa9SJames Smart 	/* bring the link to diagnostic mode */
17677ad20aa9SJames Smart 	loopback_mode = (struct diag_mode_set *)
176801e0e15cSJohannes Thumshirn 		bsg_request->rqst_data.h_vendor.vendor_cmd;
17697ad20aa9SJames Smart 	link_flags = loopback_mode->type;
17707ad20aa9SJames Smart 	timeout = loopback_mode->timeout * 100;
17717ad20aa9SJames Smart 
17727ad20aa9SJames Smart 	pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
17737ad20aa9SJames Smart 	if (!pmboxq) {
17747ad20aa9SJames Smart 		rc = -ENOMEM;
17757ad20aa9SJames Smart 		goto loopback_mode_exit;
17767ad20aa9SJames Smart 	}
17773b5dd52aSJames Smart 	memset((void *)pmboxq, 0, sizeof(LPFC_MBOXQ_t));
17783b5dd52aSJames Smart 	pmboxq->u.mb.mbxCommand = MBX_DOWN_LINK;
17793b5dd52aSJames Smart 	pmboxq->u.mb.mbxOwner = OWN_HOST;
17803b5dd52aSJames Smart 
17813b5dd52aSJames Smart 	mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq, LPFC_MBOX_TMO);
17823b5dd52aSJames Smart 
17833b5dd52aSJames Smart 	if ((mbxstatus == MBX_SUCCESS) && (pmboxq->u.mb.mbxStatus == 0)) {
17843b5dd52aSJames Smart 		/* wait for link down before proceeding */
17853b5dd52aSJames Smart 		i = 0;
17863b5dd52aSJames Smart 		while (phba->link_state != LPFC_LINK_DOWN) {
17873b5dd52aSJames Smart 			if (i++ > timeout) {
17883b5dd52aSJames Smart 				rc = -ETIMEDOUT;
17893b5dd52aSJames Smart 				goto loopback_mode_exit;
17903b5dd52aSJames Smart 			}
17913b5dd52aSJames Smart 			msleep(10);
17923b5dd52aSJames Smart 		}
17933b5dd52aSJames Smart 
17943b5dd52aSJames Smart 		memset((void *)pmboxq, 0, sizeof(LPFC_MBOXQ_t));
17953b5dd52aSJames Smart 		if (link_flags == INTERNAL_LOOP_BACK)
17963b5dd52aSJames Smart 			pmboxq->u.mb.un.varInitLnk.link_flags = FLAGS_LOCAL_LB;
17973b5dd52aSJames Smart 		else
17983b5dd52aSJames Smart 			pmboxq->u.mb.un.varInitLnk.link_flags =
17993b5dd52aSJames Smart 				FLAGS_TOPOLOGY_MODE_LOOP;
18003b5dd52aSJames Smart 
18013b5dd52aSJames Smart 		pmboxq->u.mb.mbxCommand = MBX_INIT_LINK;
18023b5dd52aSJames Smart 		pmboxq->u.mb.mbxOwner = OWN_HOST;
18033b5dd52aSJames Smart 
18043b5dd52aSJames Smart 		mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq,
18053b5dd52aSJames Smart 						     LPFC_MBOX_TMO);
18063b5dd52aSJames Smart 
18073b5dd52aSJames Smart 		if ((mbxstatus != MBX_SUCCESS) || (pmboxq->u.mb.mbxStatus))
18083b5dd52aSJames Smart 			rc = -ENODEV;
18093b5dd52aSJames Smart 		else {
18101b51197dSJames Smart 			spin_lock_irq(&phba->hbalock);
18113b5dd52aSJames Smart 			phba->link_flag |= LS_LOOPBACK_MODE;
18121b51197dSJames Smart 			spin_unlock_irq(&phba->hbalock);
18133b5dd52aSJames Smart 			/* wait for the link attention interrupt */
18143b5dd52aSJames Smart 			msleep(100);
18153b5dd52aSJames Smart 
18163b5dd52aSJames Smart 			i = 0;
18173b5dd52aSJames Smart 			while (phba->link_state != LPFC_HBA_READY) {
18183b5dd52aSJames Smart 				if (i++ > timeout) {
18193b5dd52aSJames Smart 					rc = -ETIMEDOUT;
18203b5dd52aSJames Smart 					break;
18213b5dd52aSJames Smart 				}
18223b5dd52aSJames Smart 
18233b5dd52aSJames Smart 				msleep(10);
18243b5dd52aSJames Smart 			}
18253b5dd52aSJames Smart 		}
18263b5dd52aSJames Smart 
18273b5dd52aSJames Smart 	} else
18283b5dd52aSJames Smart 		rc = -ENODEV;
18293b5dd52aSJames Smart 
18303b5dd52aSJames Smart loopback_mode_exit:
18317ad20aa9SJames Smart 	lpfc_bsg_diag_mode_exit(phba);
18323b5dd52aSJames Smart 
18333b5dd52aSJames Smart 	/*
18343b5dd52aSJames Smart 	 * Let SLI layer release mboxq if mbox command completed after timeout.
18353b5dd52aSJames Smart 	 */
18361b51197dSJames Smart 	if (pmboxq && mbxstatus != MBX_TIMEOUT)
18373b5dd52aSJames Smart 		mempool_free(pmboxq, phba->mbox_mem_pool);
18383b5dd52aSJames Smart 
18393b5dd52aSJames Smart job_error:
18403b5dd52aSJames Smart 	/* make error code available to userspace */
184101e0e15cSJohannes Thumshirn 	bsg_reply->result = rc;
18423b5dd52aSJames Smart 	/* complete the job back to userspace if no error */
18433b5dd52aSJames Smart 	if (rc == 0)
184406548160SJohannes Thumshirn 		bsg_job_done(job, bsg_reply->result,
18451abaede7SJohannes Thumshirn 			       bsg_reply->reply_payload_rcv_len);
18463b5dd52aSJames Smart 	return rc;
18473b5dd52aSJames Smart }
18483b5dd52aSJames Smart 
18493b5dd52aSJames Smart /**
18507ad20aa9SJames Smart  * lpfc_sli4_bsg_set_link_diag_state - set sli4 link diag state
18517ad20aa9SJames Smart  * @phba: Pointer to HBA context object.
18527ad20aa9SJames Smart  * @diag: Flag for set link to diag or nomral operation state.
18537ad20aa9SJames Smart  *
18547ad20aa9SJames Smart  * This function is responsible for issuing a sli4 mailbox command for setting
18557ad20aa9SJames Smart  * link to either diag state or normal operation state.
18567ad20aa9SJames Smart  */
18577ad20aa9SJames Smart static int
18587ad20aa9SJames Smart lpfc_sli4_bsg_set_link_diag_state(struct lpfc_hba *phba, uint32_t diag)
18597ad20aa9SJames Smart {
18607ad20aa9SJames Smart 	LPFC_MBOXQ_t *pmboxq;
18617ad20aa9SJames Smart 	struct lpfc_mbx_set_link_diag_state *link_diag_state;
18627ad20aa9SJames Smart 	uint32_t req_len, alloc_len;
18637ad20aa9SJames Smart 	int mbxstatus = MBX_SUCCESS, rc;
18647ad20aa9SJames Smart 
18657ad20aa9SJames Smart 	pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
18667ad20aa9SJames Smart 	if (!pmboxq)
18677ad20aa9SJames Smart 		return -ENOMEM;
18687ad20aa9SJames Smart 
18697ad20aa9SJames Smart 	req_len = (sizeof(struct lpfc_mbx_set_link_diag_state) -
18707ad20aa9SJames Smart 		   sizeof(struct lpfc_sli4_cfg_mhdr));
18717ad20aa9SJames Smart 	alloc_len = lpfc_sli4_config(phba, pmboxq, LPFC_MBOX_SUBSYSTEM_FCOE,
18727ad20aa9SJames Smart 				LPFC_MBOX_OPCODE_FCOE_LINK_DIAG_STATE,
18737ad20aa9SJames Smart 				req_len, LPFC_SLI4_MBX_EMBED);
18747ad20aa9SJames Smart 	if (alloc_len != req_len) {
18757ad20aa9SJames Smart 		rc = -ENOMEM;
18767ad20aa9SJames Smart 		goto link_diag_state_set_out;
18777ad20aa9SJames Smart 	}
18781b51197dSJames Smart 	lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
18791b51197dSJames Smart 			"3128 Set link to diagnostic state:x%x (x%x/x%x)\n",
18801b51197dSJames Smart 			diag, phba->sli4_hba.lnk_info.lnk_tp,
18811b51197dSJames Smart 			phba->sli4_hba.lnk_info.lnk_no);
18821b51197dSJames Smart 
18837ad20aa9SJames Smart 	link_diag_state = &pmboxq->u.mqe.un.link_diag_state;
18849731592bSJames Smart 	bf_set(lpfc_mbx_set_diag_state_diag_bit_valid, &link_diag_state->u.req,
18859731592bSJames Smart 	       LPFC_DIAG_STATE_DIAG_BIT_VALID_CHANGE);
18867ad20aa9SJames Smart 	bf_set(lpfc_mbx_set_diag_state_link_num, &link_diag_state->u.req,
18871b51197dSJames Smart 	       phba->sli4_hba.lnk_info.lnk_no);
18887ad20aa9SJames Smart 	bf_set(lpfc_mbx_set_diag_state_link_type, &link_diag_state->u.req,
18891b51197dSJames Smart 	       phba->sli4_hba.lnk_info.lnk_tp);
18907ad20aa9SJames Smart 	if (diag)
18917ad20aa9SJames Smart 		bf_set(lpfc_mbx_set_diag_state_diag,
18927ad20aa9SJames Smart 		       &link_diag_state->u.req, 1);
18937ad20aa9SJames Smart 	else
18947ad20aa9SJames Smart 		bf_set(lpfc_mbx_set_diag_state_diag,
18957ad20aa9SJames Smart 		       &link_diag_state->u.req, 0);
18967ad20aa9SJames Smart 
18977ad20aa9SJames Smart 	mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq, LPFC_MBOX_TMO);
18987ad20aa9SJames Smart 
18997ad20aa9SJames Smart 	if ((mbxstatus == MBX_SUCCESS) && (pmboxq->u.mb.mbxStatus == 0))
19007ad20aa9SJames Smart 		rc = 0;
19017ad20aa9SJames Smart 	else
19027ad20aa9SJames Smart 		rc = -ENODEV;
19037ad20aa9SJames Smart 
19047ad20aa9SJames Smart link_diag_state_set_out:
19057ad20aa9SJames Smart 	if (pmboxq && (mbxstatus != MBX_TIMEOUT))
19067ad20aa9SJames Smart 		mempool_free(pmboxq, phba->mbox_mem_pool);
19077ad20aa9SJames Smart 
19087ad20aa9SJames Smart 	return rc;
19097ad20aa9SJames Smart }
19107ad20aa9SJames Smart 
19117ad20aa9SJames Smart /**
19129a66d990SJames Smart  * lpfc_sli4_bsg_set_loopback_mode - set sli4 internal loopback diagnostic
19131b51197dSJames Smart  * @phba: Pointer to HBA context object.
19149a66d990SJames Smart  * @mode: loopback mode to set
19159a66d990SJames Smart  * @link_no: link number for loopback mode to set
19161b51197dSJames Smart  *
19171b51197dSJames Smart  * This function is responsible for issuing a sli4 mailbox command for setting
19189a66d990SJames Smart  * up loopback diagnostic for a link.
19191b51197dSJames Smart  */
19201b51197dSJames Smart static int
19219a66d990SJames Smart lpfc_sli4_bsg_set_loopback_mode(struct lpfc_hba *phba, int mode,
19229a66d990SJames Smart 				uint32_t link_no)
19231b51197dSJames Smart {
19241b51197dSJames Smart 	LPFC_MBOXQ_t *pmboxq;
19251b51197dSJames Smart 	uint32_t req_len, alloc_len;
19261b51197dSJames Smart 	struct lpfc_mbx_set_link_diag_loopback *link_diag_loopback;
19271b51197dSJames Smart 	int mbxstatus = MBX_SUCCESS, rc = 0;
19281b51197dSJames Smart 
19291b51197dSJames Smart 	pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
19301b51197dSJames Smart 	if (!pmboxq)
19311b51197dSJames Smart 		return -ENOMEM;
19321b51197dSJames Smart 	req_len = (sizeof(struct lpfc_mbx_set_link_diag_loopback) -
19331b51197dSJames Smart 		   sizeof(struct lpfc_sli4_cfg_mhdr));
19341b51197dSJames Smart 	alloc_len = lpfc_sli4_config(phba, pmboxq, LPFC_MBOX_SUBSYSTEM_FCOE,
19351b51197dSJames Smart 				LPFC_MBOX_OPCODE_FCOE_LINK_DIAG_LOOPBACK,
19361b51197dSJames Smart 				req_len, LPFC_SLI4_MBX_EMBED);
19371b51197dSJames Smart 	if (alloc_len != req_len) {
19381b51197dSJames Smart 		mempool_free(pmboxq, phba->mbox_mem_pool);
19391b51197dSJames Smart 		return -ENOMEM;
19401b51197dSJames Smart 	}
19411b51197dSJames Smart 	link_diag_loopback = &pmboxq->u.mqe.un.link_diag_loopback;
19421b51197dSJames Smart 	bf_set(lpfc_mbx_set_diag_state_link_num,
19439a66d990SJames Smart 	       &link_diag_loopback->u.req, link_no);
19449a66d990SJames Smart 
19459a66d990SJames Smart 	if (phba->sli4_hba.conf_trunk & (1 << link_no)) {
19461b51197dSJames Smart 		bf_set(lpfc_mbx_set_diag_state_link_type,
19479a66d990SJames Smart 		       &link_diag_loopback->u.req, LPFC_LNK_FC_TRUNKED);
19489a66d990SJames Smart 	} else {
19499a66d990SJames Smart 		bf_set(lpfc_mbx_set_diag_state_link_type,
19509a66d990SJames Smart 		       &link_diag_loopback->u.req,
19519a66d990SJames Smart 		       phba->sli4_hba.lnk_info.lnk_tp);
19529a66d990SJames Smart 	}
19539a66d990SJames Smart 
19541b51197dSJames Smart 	bf_set(lpfc_mbx_set_diag_lpbk_type, &link_diag_loopback->u.req,
19559a66d990SJames Smart 	       mode);
19561b51197dSJames Smart 
19571b51197dSJames Smart 	mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq, LPFC_MBOX_TMO);
19581b51197dSJames Smart 	if ((mbxstatus != MBX_SUCCESS) || (pmboxq->u.mb.mbxStatus)) {
19591b51197dSJames Smart 		lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
19601b51197dSJames Smart 				"3127 Failed setup loopback mode mailbox "
19611b51197dSJames Smart 				"command, rc:x%x, status:x%x\n", mbxstatus,
19621b51197dSJames Smart 				pmboxq->u.mb.mbxStatus);
19631b51197dSJames Smart 		rc = -ENODEV;
19641b51197dSJames Smart 	}
19651b51197dSJames Smart 	if (pmboxq && (mbxstatus != MBX_TIMEOUT))
19661b51197dSJames Smart 		mempool_free(pmboxq, phba->mbox_mem_pool);
19671b51197dSJames Smart 	return rc;
19681b51197dSJames Smart }
19691b51197dSJames Smart 
19701b51197dSJames Smart /**
19711b51197dSJames Smart  * lpfc_sli4_diag_fcport_reg_setup - setup port registrations for diagnostic
19721b51197dSJames Smart  * @phba: Pointer to HBA context object.
19731b51197dSJames Smart  *
19741b51197dSJames Smart  * This function set up SLI4 FC port registrations for diagnostic run, which
19751b51197dSJames Smart  * includes all the rpis, vfi, and also vpi.
19761b51197dSJames Smart  */
19771b51197dSJames Smart static int
19781b51197dSJames Smart lpfc_sli4_diag_fcport_reg_setup(struct lpfc_hba *phba)
19791b51197dSJames Smart {
1980a645b8c1SJustin Tee 	if (test_bit(FC_VFI_REGISTERED, &phba->pport->fc_flag)) {
19811b51197dSJames Smart 		lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
19821b51197dSJames Smart 				"3136 Port still had vfi registered: "
19831b51197dSJames Smart 				"mydid:x%x, fcfi:%d, vfi:%d, vpi:%d\n",
19841b51197dSJames Smart 				phba->pport->fc_myDID, phba->fcf.fcfi,
19851b51197dSJames Smart 				phba->sli4_hba.vfi_ids[phba->pport->vfi],
19861b51197dSJames Smart 				phba->vpi_ids[phba->pport->vpi]);
19871b51197dSJames Smart 		return -EINVAL;
19881b51197dSJames Smart 	}
1989de05e484Sye xingchen 	return lpfc_issue_reg_vfi(phba->pport);
19901b51197dSJames Smart }
19911b51197dSJames Smart 
19921b51197dSJames Smart /**
19937ad20aa9SJames Smart  * lpfc_sli4_bsg_diag_loopback_mode - process an sli4 bsg vendor command
19947ad20aa9SJames Smart  * @phba: Pointer to HBA context object.
19957ad20aa9SJames Smart  * @job: LPFC_BSG_VENDOR_DIAG_MODE
19967ad20aa9SJames Smart  *
19977ad20aa9SJames Smart  * This function is responsible for placing an sli4 port into diagnostic
19987ad20aa9SJames Smart  * loopback mode in order to perform a diagnostic loopback test.
19997ad20aa9SJames Smart  */
20007ad20aa9SJames Smart static int
200175cc8cfcSJohannes Thumshirn lpfc_sli4_bsg_diag_loopback_mode(struct lpfc_hba *phba, struct bsg_job *job)
20027ad20aa9SJames Smart {
200301e0e15cSJohannes Thumshirn 	struct fc_bsg_request *bsg_request = job->request;
200401e0e15cSJohannes Thumshirn 	struct fc_bsg_reply *bsg_reply = job->reply;
20057ad20aa9SJames Smart 	struct diag_mode_set *loopback_mode;
20069a66d990SJames Smart 	uint32_t link_flags, timeout, link_no;
20071b51197dSJames Smart 	int i, rc = 0;
20087ad20aa9SJames Smart 
20097ad20aa9SJames Smart 	/* no data to return just the return code */
201001e0e15cSJohannes Thumshirn 	bsg_reply->reply_payload_rcv_len = 0;
20117ad20aa9SJames Smart 
20127ad20aa9SJames Smart 	if (job->request_len < sizeof(struct fc_bsg_request) +
20137ad20aa9SJames Smart 	    sizeof(struct diag_mode_set)) {
20147ad20aa9SJames Smart 		lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
20157ad20aa9SJames Smart 				"3011 Received DIAG MODE request size:%d "
20167ad20aa9SJames Smart 				"below the minimum size:%d\n",
20177ad20aa9SJames Smart 				job->request_len,
20187ad20aa9SJames Smart 				(int)(sizeof(struct fc_bsg_request) +
20197ad20aa9SJames Smart 				sizeof(struct diag_mode_set)));
20207ad20aa9SJames Smart 		rc = -EINVAL;
20219a66d990SJames Smart 		goto job_done;
20229a66d990SJames Smart 	}
20239a66d990SJames Smart 
20249a66d990SJames Smart 	loopback_mode = (struct diag_mode_set *)
20259a66d990SJames Smart 		bsg_request->rqst_data.h_vendor.vendor_cmd;
20269a66d990SJames Smart 	link_flags = loopback_mode->type;
20279a66d990SJames Smart 	timeout = loopback_mode->timeout * 100;
20289a66d990SJames Smart 
20299a66d990SJames Smart 	if (loopback_mode->physical_link == -1)
20309a66d990SJames Smart 		link_no = phba->sli4_hba.lnk_info.lnk_no;
20319a66d990SJames Smart 	else
20329a66d990SJames Smart 		link_no = loopback_mode->physical_link;
20339a66d990SJames Smart 
20349a66d990SJames Smart 	if (link_flags == DISABLE_LOOP_BACK) {
20359a66d990SJames Smart 		rc = lpfc_sli4_bsg_set_loopback_mode(phba,
20369a66d990SJames Smart 					LPFC_DIAG_LOOPBACK_TYPE_DISABLE,
20379a66d990SJames Smart 					link_no);
20389a66d990SJames Smart 		if (!rc) {
20399a66d990SJames Smart 			/* Unset the need disable bit */
20409a66d990SJames Smart 			phba->sli4_hba.conf_trunk &= ~((1 << link_no) << 4);
20419a66d990SJames Smart 		}
20429a66d990SJames Smart 		goto job_done;
20439a66d990SJames Smart 	} else {
20449a66d990SJames Smart 		/* Check if we need to disable the loopback state */
20459a66d990SJames Smart 		if (phba->sli4_hba.conf_trunk & ((1 << link_no) << 4)) {
20469a66d990SJames Smart 			rc = -EPERM;
20479a66d990SJames Smart 			goto job_done;
20489a66d990SJames Smart 		}
20497ad20aa9SJames Smart 	}
20507ad20aa9SJames Smart 
205188a2cfbbSJames Smart 	rc = lpfc_bsg_diag_mode_enter(phba);
20527ad20aa9SJames Smart 	if (rc)
20539a66d990SJames Smart 		goto job_done;
20547ad20aa9SJames Smart 
20551b51197dSJames Smart 	/* indicate we are in loobpack diagnostic mode */
20561b51197dSJames Smart 	spin_lock_irq(&phba->hbalock);
20571b51197dSJames Smart 	phba->link_flag |= LS_LOOPBACK_MODE;
20581b51197dSJames Smart 	spin_unlock_irq(&phba->hbalock);
20591b51197dSJames Smart 
20601b51197dSJames Smart 	/* reset port to start frome scratch */
20611b51197dSJames Smart 	rc = lpfc_selective_reset(phba);
20621b51197dSJames Smart 	if (rc)
20639a66d990SJames Smart 		goto job_done;
20641b51197dSJames Smart 
20657ad20aa9SJames Smart 	/* bring the link to diagnostic mode */
20661b51197dSJames Smart 	lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
20671b51197dSJames Smart 			"3129 Bring link to diagnostic state.\n");
20687ad20aa9SJames Smart 
20697ad20aa9SJames Smart 	rc = lpfc_sli4_bsg_set_link_diag_state(phba, 1);
20701b51197dSJames Smart 	if (rc) {
20711b51197dSJames Smart 		lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
20721b51197dSJames Smart 				"3130 Failed to bring link to diagnostic "
20731b51197dSJames Smart 				"state, rc:x%x\n", rc);
20747ad20aa9SJames Smart 		goto loopback_mode_exit;
20751b51197dSJames Smart 	}
20767ad20aa9SJames Smart 
20777ad20aa9SJames Smart 	/* wait for link down before proceeding */
20787ad20aa9SJames Smart 	i = 0;
20797ad20aa9SJames Smart 	while (phba->link_state != LPFC_LINK_DOWN) {
20807ad20aa9SJames Smart 		if (i++ > timeout) {
20817ad20aa9SJames Smart 			rc = -ETIMEDOUT;
20821b51197dSJames Smart 			lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
20831b51197dSJames Smart 					"3131 Timeout waiting for link to "
20841b51197dSJames Smart 					"diagnostic mode, timeout:%d ms\n",
20851b51197dSJames Smart 					timeout * 10);
20867ad20aa9SJames Smart 			goto loopback_mode_exit;
20877ad20aa9SJames Smart 		}
20887ad20aa9SJames Smart 		msleep(10);
20897ad20aa9SJames Smart 	}
20907ad20aa9SJames Smart 
20911b51197dSJames Smart 	/* set up loopback mode */
20921b51197dSJames Smart 	lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
20931b51197dSJames Smart 			"3132 Set up loopback mode:x%x\n", link_flags);
20941b51197dSJames Smart 
20959a66d990SJames Smart 	switch (link_flags) {
20969a66d990SJames Smart 	case INTERNAL_LOOP_BACK:
20979a66d990SJames Smart 		if (phba->sli4_hba.conf_trunk & (1 << link_no)) {
20989a66d990SJames Smart 			rc = lpfc_sli4_bsg_set_loopback_mode(phba,
20999a66d990SJames Smart 					LPFC_DIAG_LOOPBACK_TYPE_INTERNAL,
21009a66d990SJames Smart 					link_no);
21019a66d990SJames Smart 		} else {
21029a66d990SJames Smart 			/* Trunk is configured, but link is not in this trunk */
21039a66d990SJames Smart 			if (phba->sli4_hba.conf_trunk) {
21049a66d990SJames Smart 				rc = -ELNRNG;
21059a66d990SJames Smart 				goto loopback_mode_exit;
21069a66d990SJames Smart 			}
21079a66d990SJames Smart 
21089a66d990SJames Smart 			rc = lpfc_sli4_bsg_set_loopback_mode(phba,
21099a66d990SJames Smart 					LPFC_DIAG_LOOPBACK_TYPE_INTERNAL,
21109a66d990SJames Smart 					link_no);
21119a66d990SJames Smart 		}
21129a66d990SJames Smart 
21139a66d990SJames Smart 		if (!rc) {
21149a66d990SJames Smart 			/* Set the need disable bit */
21159a66d990SJames Smart 			phba->sli4_hba.conf_trunk |= (1 << link_no) << 4;
21169a66d990SJames Smart 		}
21179a66d990SJames Smart 
21189a66d990SJames Smart 		break;
21199a66d990SJames Smart 	case EXTERNAL_LOOP_BACK:
21209a66d990SJames Smart 		if (phba->sli4_hba.conf_trunk & (1 << link_no)) {
21219a66d990SJames Smart 			rc = lpfc_sli4_bsg_set_loopback_mode(phba,
21229a66d990SJames Smart 				LPFC_DIAG_LOOPBACK_TYPE_EXTERNAL_TRUNKED,
21239a66d990SJames Smart 				link_no);
21249a66d990SJames Smart 		} else {
21259a66d990SJames Smart 			/* Trunk is configured, but link is not in this trunk */
21269a66d990SJames Smart 			if (phba->sli4_hba.conf_trunk) {
21279a66d990SJames Smart 				rc = -ELNRNG;
21289a66d990SJames Smart 				goto loopback_mode_exit;
21299a66d990SJames Smart 			}
21309a66d990SJames Smart 
21319a66d990SJames Smart 			rc = lpfc_sli4_bsg_set_loopback_mode(phba,
21329a66d990SJames Smart 						LPFC_DIAG_LOOPBACK_TYPE_SERDES,
21339a66d990SJames Smart 						link_no);
21349a66d990SJames Smart 		}
21359a66d990SJames Smart 
21369a66d990SJames Smart 		if (!rc) {
21379a66d990SJames Smart 			/* Set the need disable bit */
21389a66d990SJames Smart 			phba->sli4_hba.conf_trunk |= (1 << link_no) << 4;
21399a66d990SJames Smart 		}
21409a66d990SJames Smart 
21419a66d990SJames Smart 		break;
21429a66d990SJames Smart 	default:
21431b51197dSJames Smart 		rc = -EINVAL;
21441b51197dSJames Smart 		lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
21451b51197dSJames Smart 				"3141 Loopback mode:x%x not supported\n",
21461b51197dSJames Smart 				link_flags);
21471b51197dSJames Smart 		goto loopback_mode_exit;
21481b51197dSJames Smart 	}
21491b51197dSJames Smart 
21501b51197dSJames Smart 	if (!rc) {
21517ad20aa9SJames Smart 		/* wait for the link attention interrupt */
21527ad20aa9SJames Smart 		msleep(100);
21537ad20aa9SJames Smart 		i = 0;
21541b51197dSJames Smart 		while (phba->link_state < LPFC_LINK_UP) {
21551b51197dSJames Smart 			if (i++ > timeout) {
21561b51197dSJames Smart 				rc = -ETIMEDOUT;
21571b51197dSJames Smart 				lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
21581b51197dSJames Smart 					"3137 Timeout waiting for link up "
21591b51197dSJames Smart 					"in loopback mode, timeout:%d ms\n",
21601b51197dSJames Smart 					timeout * 10);
21611b51197dSJames Smart 				break;
21621b51197dSJames Smart 			}
21631b51197dSJames Smart 			msleep(10);
21641b51197dSJames Smart 		}
21651b51197dSJames Smart 	}
21661b51197dSJames Smart 
21671b51197dSJames Smart 	/* port resource registration setup for loopback diagnostic */
21681b51197dSJames Smart 	if (!rc) {
21691b51197dSJames Smart 		/* set up a none zero myDID for loopback test */
21701b51197dSJames Smart 		phba->pport->fc_myDID = 1;
21711b51197dSJames Smart 		rc = lpfc_sli4_diag_fcport_reg_setup(phba);
21721b51197dSJames Smart 	} else
21731b51197dSJames Smart 		goto loopback_mode_exit;
21741b51197dSJames Smart 
21751b51197dSJames Smart 	if (!rc) {
21761b51197dSJames Smart 		/* wait for the port ready */
21771b51197dSJames Smart 		msleep(100);
21781b51197dSJames Smart 		i = 0;
21797ad20aa9SJames Smart 		while (phba->link_state != LPFC_HBA_READY) {
21807ad20aa9SJames Smart 			if (i++ > timeout) {
21817ad20aa9SJames Smart 				rc = -ETIMEDOUT;
21821b51197dSJames Smart 				lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
21831b51197dSJames Smart 					"3133 Timeout waiting for port "
21841b51197dSJames Smart 					"loopback mode ready, timeout:%d ms\n",
21851b51197dSJames Smart 					timeout * 10);
21867ad20aa9SJames Smart 				break;
21877ad20aa9SJames Smart 			}
21887ad20aa9SJames Smart 			msleep(10);
21897ad20aa9SJames Smart 		}
21907ad20aa9SJames Smart 	}
21917ad20aa9SJames Smart 
21927ad20aa9SJames Smart loopback_mode_exit:
21931b51197dSJames Smart 	/* clear loopback diagnostic mode */
21941b51197dSJames Smart 	if (rc) {
21951b51197dSJames Smart 		spin_lock_irq(&phba->hbalock);
21961b51197dSJames Smart 		phba->link_flag &= ~LS_LOOPBACK_MODE;
21971b51197dSJames Smart 		spin_unlock_irq(&phba->hbalock);
21981b51197dSJames Smart 	}
21997ad20aa9SJames Smart 	lpfc_bsg_diag_mode_exit(phba);
22007ad20aa9SJames Smart 
22019a66d990SJames Smart job_done:
22027ad20aa9SJames Smart 	/* make error code available to userspace */
220301e0e15cSJohannes Thumshirn 	bsg_reply->result = rc;
22047ad20aa9SJames Smart 	/* complete the job back to userspace if no error */
22057ad20aa9SJames Smart 	if (rc == 0)
220606548160SJohannes Thumshirn 		bsg_job_done(job, bsg_reply->result,
22071abaede7SJohannes Thumshirn 			       bsg_reply->reply_payload_rcv_len);
22087ad20aa9SJames Smart 	return rc;
22097ad20aa9SJames Smart }
22107ad20aa9SJames Smart 
22117ad20aa9SJames Smart /**
22127ad20aa9SJames Smart  * lpfc_bsg_diag_loopback_mode - bsg vendor command for diag loopback mode
22137ad20aa9SJames Smart  * @job: LPFC_BSG_VENDOR_DIAG_MODE
22147ad20aa9SJames Smart  *
22157ad20aa9SJames Smart  * This function is responsible for responding to check and dispatch bsg diag
22167ad20aa9SJames Smart  * command from the user to proper driver action routines.
22177ad20aa9SJames Smart  */
22187ad20aa9SJames Smart static int
221975cc8cfcSJohannes Thumshirn lpfc_bsg_diag_loopback_mode(struct bsg_job *job)
22207ad20aa9SJames Smart {
22217ad20aa9SJames Smart 	struct Scsi_Host *shost;
22227ad20aa9SJames Smart 	struct lpfc_vport *vport;
22237ad20aa9SJames Smart 	struct lpfc_hba *phba;
22247ad20aa9SJames Smart 	int rc;
22257ad20aa9SJames Smart 
2226cd21c605SJohannes Thumshirn 	shost = fc_bsg_to_shost(job);
22277ad20aa9SJames Smart 	if (!shost)
22287ad20aa9SJames Smart 		return -ENODEV;
2229cd21c605SJohannes Thumshirn 	vport = shost_priv(shost);
22307ad20aa9SJames Smart 	if (!vport)
22317ad20aa9SJames Smart 		return -ENODEV;
22327ad20aa9SJames Smart 	phba = vport->phba;
22337ad20aa9SJames Smart 	if (!phba)
22347ad20aa9SJames Smart 		return -ENODEV;
22357ad20aa9SJames Smart 
22367ad20aa9SJames Smart 	if (phba->sli_rev < LPFC_SLI_REV4)
22377ad20aa9SJames Smart 		rc = lpfc_sli3_bsg_diag_loopback_mode(phba, job);
2238719162bdSJames Smart 	else if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) >=
22397ad20aa9SJames Smart 		 LPFC_SLI_INTF_IF_TYPE_2)
22407ad20aa9SJames Smart 		rc = lpfc_sli4_bsg_diag_loopback_mode(phba, job);
22417ad20aa9SJames Smart 	else
22427ad20aa9SJames Smart 		rc = -ENODEV;
22437ad20aa9SJames Smart 
22447ad20aa9SJames Smart 	return rc;
22457ad20aa9SJames Smart }
22467ad20aa9SJames Smart 
22477ad20aa9SJames Smart /**
22487ad20aa9SJames Smart  * lpfc_sli4_bsg_diag_mode_end - sli4 bsg vendor command for ending diag mode
22497ad20aa9SJames Smart  * @job: LPFC_BSG_VENDOR_DIAG_MODE_END
22507ad20aa9SJames Smart  *
22517ad20aa9SJames Smart  * This function is responsible for responding to check and dispatch bsg diag
22527ad20aa9SJames Smart  * command from the user to proper driver action routines.
22537ad20aa9SJames Smart  */
22547ad20aa9SJames Smart static int
225575cc8cfcSJohannes Thumshirn lpfc_sli4_bsg_diag_mode_end(struct bsg_job *job)
22567ad20aa9SJames Smart {
225701e0e15cSJohannes Thumshirn 	struct fc_bsg_request *bsg_request = job->request;
225801e0e15cSJohannes Thumshirn 	struct fc_bsg_reply *bsg_reply = job->reply;
22597ad20aa9SJames Smart 	struct Scsi_Host *shost;
22607ad20aa9SJames Smart 	struct lpfc_vport *vport;
22617ad20aa9SJames Smart 	struct lpfc_hba *phba;
22621b51197dSJames Smart 	struct diag_mode_set *loopback_mode_end_cmd;
22631b51197dSJames Smart 	uint32_t timeout;
22641b51197dSJames Smart 	int rc, i;
22657ad20aa9SJames Smart 
2266cd21c605SJohannes Thumshirn 	shost = fc_bsg_to_shost(job);
22677ad20aa9SJames Smart 	if (!shost)
22687ad20aa9SJames Smart 		return -ENODEV;
2269cd21c605SJohannes Thumshirn 	vport = shost_priv(shost);
22707ad20aa9SJames Smart 	if (!vport)
22717ad20aa9SJames Smart 		return -ENODEV;
22727ad20aa9SJames Smart 	phba = vport->phba;
22737ad20aa9SJames Smart 	if (!phba)
22747ad20aa9SJames Smart 		return -ENODEV;
22757ad20aa9SJames Smart 
22767ad20aa9SJames Smart 	if (phba->sli_rev < LPFC_SLI_REV4)
22777ad20aa9SJames Smart 		return -ENODEV;
2278719162bdSJames Smart 	if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) <
22797ad20aa9SJames Smart 	    LPFC_SLI_INTF_IF_TYPE_2)
22807ad20aa9SJames Smart 		return -ENODEV;
22817ad20aa9SJames Smart 
22821b51197dSJames Smart 	/* clear loopback diagnostic mode */
22831b51197dSJames Smart 	spin_lock_irq(&phba->hbalock);
22841b51197dSJames Smart 	phba->link_flag &= ~LS_LOOPBACK_MODE;
22851b51197dSJames Smart 	spin_unlock_irq(&phba->hbalock);
22861b51197dSJames Smart 	loopback_mode_end_cmd = (struct diag_mode_set *)
228701e0e15cSJohannes Thumshirn 			bsg_request->rqst_data.h_vendor.vendor_cmd;
22881b51197dSJames Smart 	timeout = loopback_mode_end_cmd->timeout * 100;
22891b51197dSJames Smart 
22907ad20aa9SJames Smart 	rc = lpfc_sli4_bsg_set_link_diag_state(phba, 0);
22911b51197dSJames Smart 	if (rc) {
22921b51197dSJames Smart 		lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
22931b51197dSJames Smart 				"3139 Failed to bring link to diagnostic "
22941b51197dSJames Smart 				"state, rc:x%x\n", rc);
22951b51197dSJames Smart 		goto loopback_mode_end_exit;
22961b51197dSJames Smart 	}
22977ad20aa9SJames Smart 
22981b51197dSJames Smart 	/* wait for link down before proceeding */
22991b51197dSJames Smart 	i = 0;
23001b51197dSJames Smart 	while (phba->link_state != LPFC_LINK_DOWN) {
23011b51197dSJames Smart 		if (i++ > timeout) {
23021b51197dSJames Smart 			lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
23031b51197dSJames Smart 					"3140 Timeout waiting for link to "
23041b51197dSJames Smart 					"diagnostic mode_end, timeout:%d ms\n",
23051b51197dSJames Smart 					timeout * 10);
23061b51197dSJames Smart 			/* there is nothing much we can do here */
23071b51197dSJames Smart 			break;
23081b51197dSJames Smart 		}
23091b51197dSJames Smart 		msleep(10);
23101b51197dSJames Smart 	}
23117ad20aa9SJames Smart 
23121b51197dSJames Smart 	/* reset port resource registrations */
23131b51197dSJames Smart 	rc = lpfc_selective_reset(phba);
23141b51197dSJames Smart 	phba->pport->fc_myDID = 0;
23151b51197dSJames Smart 
23161b51197dSJames Smart loopback_mode_end_exit:
23171b51197dSJames Smart 	/* make return code available to userspace */
231801e0e15cSJohannes Thumshirn 	bsg_reply->result = rc;
23191b51197dSJames Smart 	/* complete the job back to userspace if no error */
23201b51197dSJames Smart 	if (rc == 0)
232106548160SJohannes Thumshirn 		bsg_job_done(job, bsg_reply->result,
23221abaede7SJohannes Thumshirn 			       bsg_reply->reply_payload_rcv_len);
23237ad20aa9SJames Smart 	return rc;
23247ad20aa9SJames Smart }
23257ad20aa9SJames Smart 
23267ad20aa9SJames Smart /**
23277ad20aa9SJames Smart  * lpfc_sli4_bsg_link_diag_test - sli4 bsg vendor command for diag link test
23287ad20aa9SJames Smart  * @job: LPFC_BSG_VENDOR_DIAG_LINK_TEST
23297ad20aa9SJames Smart  *
23307ad20aa9SJames Smart  * This function is to perform SLI4 diag link test request from the user
23317ad20aa9SJames Smart  * applicaiton.
23327ad20aa9SJames Smart  */
23337ad20aa9SJames Smart static int
233475cc8cfcSJohannes Thumshirn lpfc_sli4_bsg_link_diag_test(struct bsg_job *job)
23357ad20aa9SJames Smart {
233601e0e15cSJohannes Thumshirn 	struct fc_bsg_request *bsg_request = job->request;
233701e0e15cSJohannes Thumshirn 	struct fc_bsg_reply *bsg_reply = job->reply;
23387ad20aa9SJames Smart 	struct Scsi_Host *shost;
23397ad20aa9SJames Smart 	struct lpfc_vport *vport;
23407ad20aa9SJames Smart 	struct lpfc_hba *phba;
23417ad20aa9SJames Smart 	LPFC_MBOXQ_t *pmboxq;
23427ad20aa9SJames Smart 	struct sli4_link_diag *link_diag_test_cmd;
23437ad20aa9SJames Smart 	uint32_t req_len, alloc_len;
23447ad20aa9SJames Smart 	struct lpfc_mbx_run_link_diag_test *run_link_diag_test;
23457ad20aa9SJames Smart 	union lpfc_sli4_cfg_shdr *shdr;
23467ad20aa9SJames Smart 	uint32_t shdr_status, shdr_add_status;
23477ad20aa9SJames Smart 	struct diag_status *diag_status_reply;
2348e5fcb81dSDick Kennedy 	int mbxstatus, rc = -ENODEV, rc1 = 0;
23497ad20aa9SJames Smart 
2350cd21c605SJohannes Thumshirn 	shost = fc_bsg_to_shost(job);
2351e5fcb81dSDick Kennedy 	if (!shost)
23527ad20aa9SJames Smart 		goto job_error;
23537ad20aa9SJames Smart 
2354e5fcb81dSDick Kennedy 	vport = shost_priv(shost);
2355e5fcb81dSDick Kennedy 	if (!vport)
23567ad20aa9SJames Smart 		goto job_error;
2357e5fcb81dSDick Kennedy 
2358e5fcb81dSDick Kennedy 	phba = vport->phba;
2359e5fcb81dSDick Kennedy 	if (!phba)
2360e5fcb81dSDick Kennedy 		goto job_error;
2361e5fcb81dSDick Kennedy 
2362e5fcb81dSDick Kennedy 
2363e5fcb81dSDick Kennedy 	if (phba->sli_rev < LPFC_SLI_REV4)
2364e5fcb81dSDick Kennedy 		goto job_error;
2365e5fcb81dSDick Kennedy 
2366719162bdSJames Smart 	if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) <
2367e5fcb81dSDick Kennedy 	    LPFC_SLI_INTF_IF_TYPE_2)
23687ad20aa9SJames Smart 		goto job_error;
23697ad20aa9SJames Smart 
23707ad20aa9SJames Smart 	if (job->request_len < sizeof(struct fc_bsg_request) +
23717ad20aa9SJames Smart 	    sizeof(struct sli4_link_diag)) {
23727ad20aa9SJames Smart 		lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
23737ad20aa9SJames Smart 				"3013 Received LINK DIAG TEST request "
23747ad20aa9SJames Smart 				" size:%d below the minimum size:%d\n",
23757ad20aa9SJames Smart 				job->request_len,
23767ad20aa9SJames Smart 				(int)(sizeof(struct fc_bsg_request) +
23777ad20aa9SJames Smart 				sizeof(struct sli4_link_diag)));
23787ad20aa9SJames Smart 		rc = -EINVAL;
23797ad20aa9SJames Smart 		goto job_error;
23807ad20aa9SJames Smart 	}
23817ad20aa9SJames Smart 
238288a2cfbbSJames Smart 	rc = lpfc_bsg_diag_mode_enter(phba);
23837ad20aa9SJames Smart 	if (rc)
23847ad20aa9SJames Smart 		goto job_error;
23857ad20aa9SJames Smart 
23867ad20aa9SJames Smart 	link_diag_test_cmd = (struct sli4_link_diag *)
238701e0e15cSJohannes Thumshirn 			 bsg_request->rqst_data.h_vendor.vendor_cmd;
23887ad20aa9SJames Smart 
23897ad20aa9SJames Smart 	rc = lpfc_sli4_bsg_set_link_diag_state(phba, 1);
23907ad20aa9SJames Smart 
23917ad20aa9SJames Smart 	if (rc)
23927ad20aa9SJames Smart 		goto job_error;
23937ad20aa9SJames Smart 
23947ad20aa9SJames Smart 	pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
2395a1a553e3SJames Smart 	if (!pmboxq) {
2396a1a553e3SJames Smart 		rc = -ENOMEM;
23977ad20aa9SJames Smart 		goto link_diag_test_exit;
2398a1a553e3SJames Smart 	}
23997ad20aa9SJames Smart 
24007ad20aa9SJames Smart 	req_len = (sizeof(struct lpfc_mbx_set_link_diag_state) -
24017ad20aa9SJames Smart 		   sizeof(struct lpfc_sli4_cfg_mhdr));
24027ad20aa9SJames Smart 	alloc_len = lpfc_sli4_config(phba, pmboxq, LPFC_MBOX_SUBSYSTEM_FCOE,
24037ad20aa9SJames Smart 				     LPFC_MBOX_OPCODE_FCOE_LINK_DIAG_STATE,
24047ad20aa9SJames Smart 				     req_len, LPFC_SLI4_MBX_EMBED);
2405e5fcb81dSDick Kennedy 	if (alloc_len != req_len) {
2406e5fcb81dSDick Kennedy 		rc = -ENOMEM;
24077ad20aa9SJames Smart 		goto link_diag_test_exit;
2408e5fcb81dSDick Kennedy 	}
240944ed33e6SGustavo A. R. Silva 
24107ad20aa9SJames Smart 	run_link_diag_test = &pmboxq->u.mqe.un.link_diag_test;
24117ad20aa9SJames Smart 	bf_set(lpfc_mbx_run_diag_test_link_num, &run_link_diag_test->u.req,
24121b51197dSJames Smart 	       phba->sli4_hba.lnk_info.lnk_no);
24137ad20aa9SJames Smart 	bf_set(lpfc_mbx_run_diag_test_link_type, &run_link_diag_test->u.req,
24141b51197dSJames Smart 	       phba->sli4_hba.lnk_info.lnk_tp);
24157ad20aa9SJames Smart 	bf_set(lpfc_mbx_run_diag_test_test_id, &run_link_diag_test->u.req,
24167ad20aa9SJames Smart 	       link_diag_test_cmd->test_id);
24177ad20aa9SJames Smart 	bf_set(lpfc_mbx_run_diag_test_loops, &run_link_diag_test->u.req,
24187ad20aa9SJames Smart 	       link_diag_test_cmd->loops);
24197ad20aa9SJames Smart 	bf_set(lpfc_mbx_run_diag_test_test_ver, &run_link_diag_test->u.req,
24207ad20aa9SJames Smart 	       link_diag_test_cmd->test_version);
24217ad20aa9SJames Smart 	bf_set(lpfc_mbx_run_diag_test_err_act, &run_link_diag_test->u.req,
24227ad20aa9SJames Smart 	       link_diag_test_cmd->error_action);
24237ad20aa9SJames Smart 
24247ad20aa9SJames Smart 	mbxstatus = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL);
24257ad20aa9SJames Smart 
24267ad20aa9SJames Smart 	shdr = (union lpfc_sli4_cfg_shdr *)
24277ad20aa9SJames Smart 		&pmboxq->u.mqe.un.sli4_config.header.cfg_shdr;
24287ad20aa9SJames Smart 	shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
24297ad20aa9SJames Smart 	shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
24307ad20aa9SJames Smart 	if (shdr_status || shdr_add_status || mbxstatus) {
24317ad20aa9SJames Smart 		lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
24327ad20aa9SJames Smart 				"3010 Run link diag test mailbox failed with "
24337ad20aa9SJames Smart 				"mbx_status x%x status x%x, add_status x%x\n",
24347ad20aa9SJames Smart 				mbxstatus, shdr_status, shdr_add_status);
24357ad20aa9SJames Smart 	}
24367ad20aa9SJames Smart 
24377ad20aa9SJames Smart 	diag_status_reply = (struct diag_status *)
243801e0e15cSJohannes Thumshirn 			    bsg_reply->reply_data.vendor_reply.vendor_rsp;
24397ad20aa9SJames Smart 
2440feb3cc57SDick Kennedy 	if (job->reply_len < sizeof(*bsg_reply) + sizeof(*diag_status_reply)) {
24417ad20aa9SJames Smart 		lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
24427ad20aa9SJames Smart 				"3012 Received Run link diag test reply "
24437ad20aa9SJames Smart 				"below minimum size (%d): reply_len:%d\n",
2444feb3cc57SDick Kennedy 				(int)(sizeof(*bsg_reply) +
2445feb3cc57SDick Kennedy 				sizeof(*diag_status_reply)),
24467ad20aa9SJames Smart 				job->reply_len);
24477ad20aa9SJames Smart 		rc = -EINVAL;
24487ad20aa9SJames Smart 		goto job_error;
24497ad20aa9SJames Smart 	}
24507ad20aa9SJames Smart 
24517ad20aa9SJames Smart 	diag_status_reply->mbox_status = mbxstatus;
24527ad20aa9SJames Smart 	diag_status_reply->shdr_status = shdr_status;
24537ad20aa9SJames Smart 	diag_status_reply->shdr_add_status = shdr_add_status;
24547ad20aa9SJames Smart 
24557ad20aa9SJames Smart link_diag_test_exit:
2456e5fcb81dSDick Kennedy 	rc1 = lpfc_sli4_bsg_set_link_diag_state(phba, 0);
24577ad20aa9SJames Smart 
24587ad20aa9SJames Smart 	if (pmboxq)
24597ad20aa9SJames Smart 		mempool_free(pmboxq, phba->mbox_mem_pool);
24607ad20aa9SJames Smart 
24617ad20aa9SJames Smart 	lpfc_bsg_diag_mode_exit(phba);
24627ad20aa9SJames Smart 
24637ad20aa9SJames Smart job_error:
24647ad20aa9SJames Smart 	/* make error code available to userspace */
2465e5fcb81dSDick Kennedy 	if (rc1 && !rc)
2466e5fcb81dSDick Kennedy 		rc = rc1;
246701e0e15cSJohannes Thumshirn 	bsg_reply->result = rc;
24687ad20aa9SJames Smart 	/* complete the job back to userspace if no error */
24697ad20aa9SJames Smart 	if (rc == 0)
247006548160SJohannes Thumshirn 		bsg_job_done(job, bsg_reply->result,
24711abaede7SJohannes Thumshirn 			       bsg_reply->reply_payload_rcv_len);
24727ad20aa9SJames Smart 	return rc;
24737ad20aa9SJames Smart }
24747ad20aa9SJames Smart 
24757ad20aa9SJames Smart /**
24763b5dd52aSJames Smart  * lpfcdiag_loop_self_reg - obtains a remote port login id
24773b5dd52aSJames Smart  * @phba: Pointer to HBA context object
24783b5dd52aSJames Smart  * @rpi: Pointer to a remote port login id
24793b5dd52aSJames Smart  *
24803b5dd52aSJames Smart  * This function obtains a remote port login id so the diag loopback test
24813b5dd52aSJames Smart  * can send and receive its own unsolicited CT command.
24823b5dd52aSJames Smart  **/
24833b5dd52aSJames Smart static int lpfcdiag_loop_self_reg(struct lpfc_hba *phba, uint16_t *rpi)
24843b5dd52aSJames Smart {
24853b5dd52aSJames Smart 	LPFC_MBOXQ_t *mbox;
24863b5dd52aSJames Smart 	struct lpfc_dmabuf *dmabuff;
24873b5dd52aSJames Smart 	int status;
24883b5dd52aSJames Smart 
24893b5dd52aSJames Smart 	mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
24903b5dd52aSJames Smart 	if (!mbox)
2491d439d286SJames Smart 		return -ENOMEM;
24923b5dd52aSJames Smart 
24931b51197dSJames Smart 	if (phba->sli_rev < LPFC_SLI_REV4)
24943b5dd52aSJames Smart 		status = lpfc_reg_rpi(phba, 0, phba->pport->fc_myDID,
24951b51197dSJames Smart 				(uint8_t *)&phba->pport->fc_sparam,
24961b51197dSJames Smart 				mbox, *rpi);
24971b51197dSJames Smart 	else {
24981b51197dSJames Smart 		*rpi = lpfc_sli4_alloc_rpi(phba);
24999d3d340dSJames Smart 		if (*rpi == LPFC_RPI_ALLOC_ERROR) {
25009d3d340dSJames Smart 			mempool_free(mbox, phba->mbox_mem_pool);
25019d3d340dSJames Smart 			return -EBUSY;
25029d3d340dSJames Smart 		}
25031b51197dSJames Smart 		status = lpfc_reg_rpi(phba, phba->pport->vpi,
25041b51197dSJames Smart 				phba->pport->fc_myDID,
25051b51197dSJames Smart 				(uint8_t *)&phba->pport->fc_sparam,
25061b51197dSJames Smart 				mbox, *rpi);
25071b51197dSJames Smart 	}
25081b51197dSJames Smart 
25093b5dd52aSJames Smart 	if (status) {
25103b5dd52aSJames Smart 		mempool_free(mbox, phba->mbox_mem_pool);
25114042629eSJames Smart 		if (phba->sli_rev == LPFC_SLI_REV4)
25124042629eSJames Smart 			lpfc_sli4_free_rpi(phba, *rpi);
2513d439d286SJames Smart 		return -ENOMEM;
25143b5dd52aSJames Smart 	}
25153b5dd52aSJames Smart 
2516115d137aSJustin Tee 	dmabuff = mbox->ctx_buf;
25173e1f0718SJames Smart 	mbox->ctx_buf = NULL;
25183e1f0718SJames Smart 	mbox->ctx_ndlp = NULL;
25193b5dd52aSJames Smart 	status = lpfc_sli_issue_mbox_wait(phba, mbox, LPFC_MBOX_TMO);
25203b5dd52aSJames Smart 
25213b5dd52aSJames Smart 	if ((status != MBX_SUCCESS) || (mbox->u.mb.mbxStatus)) {
25223b5dd52aSJames Smart 		lpfc_mbuf_free(phba, dmabuff->virt, dmabuff->phys);
25233b5dd52aSJames Smart 		kfree(dmabuff);
25243b5dd52aSJames Smart 		if (status != MBX_TIMEOUT)
25253b5dd52aSJames Smart 			mempool_free(mbox, phba->mbox_mem_pool);
25264042629eSJames Smart 		if (phba->sli_rev == LPFC_SLI_REV4)
25274042629eSJames Smart 			lpfc_sli4_free_rpi(phba, *rpi);
2528d439d286SJames Smart 		return -ENODEV;
25293b5dd52aSJames Smart 	}
25303b5dd52aSJames Smart 
25311b51197dSJames Smart 	if (phba->sli_rev < LPFC_SLI_REV4)
25323b5dd52aSJames Smart 		*rpi = mbox->u.mb.un.varWords[0];
25333b5dd52aSJames Smart 
25343b5dd52aSJames Smart 	lpfc_mbuf_free(phba, dmabuff->virt, dmabuff->phys);
25353b5dd52aSJames Smart 	kfree(dmabuff);
25363b5dd52aSJames Smart 	mempool_free(mbox, phba->mbox_mem_pool);
25373b5dd52aSJames Smart 	return 0;
25383b5dd52aSJames Smart }
25393b5dd52aSJames Smart 
25403b5dd52aSJames Smart /**
25413b5dd52aSJames Smart  * lpfcdiag_loop_self_unreg - unregs from the rpi
25423b5dd52aSJames Smart  * @phba: Pointer to HBA context object
25433b5dd52aSJames Smart  * @rpi: Remote port login id
25443b5dd52aSJames Smart  *
25453b5dd52aSJames Smart  * This function unregisters the rpi obtained in lpfcdiag_loop_self_reg
25463b5dd52aSJames Smart  **/
25473b5dd52aSJames Smart static int lpfcdiag_loop_self_unreg(struct lpfc_hba *phba, uint16_t rpi)
25483b5dd52aSJames Smart {
25493b5dd52aSJames Smart 	LPFC_MBOXQ_t *mbox;
25503b5dd52aSJames Smart 	int status;
25513b5dd52aSJames Smart 
25523b5dd52aSJames Smart 	/* Allocate mboxq structure */
25533b5dd52aSJames Smart 	mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
25543b5dd52aSJames Smart 	if (mbox == NULL)
2555d439d286SJames Smart 		return -ENOMEM;
25563b5dd52aSJames Smart 
25571b51197dSJames Smart 	if (phba->sli_rev < LPFC_SLI_REV4)
25583b5dd52aSJames Smart 		lpfc_unreg_login(phba, 0, rpi, mbox);
25591b51197dSJames Smart 	else
25601b51197dSJames Smart 		lpfc_unreg_login(phba, phba->pport->vpi,
25611b51197dSJames Smart 				 phba->sli4_hba.rpi_ids[rpi], mbox);
25621b51197dSJames Smart 
25633b5dd52aSJames Smart 	status = lpfc_sli_issue_mbox_wait(phba, mbox, LPFC_MBOX_TMO);
25643b5dd52aSJames Smart 
25653b5dd52aSJames Smart 	if ((status != MBX_SUCCESS) || (mbox->u.mb.mbxStatus)) {
25663b5dd52aSJames Smart 		if (status != MBX_TIMEOUT)
25673b5dd52aSJames Smart 			mempool_free(mbox, phba->mbox_mem_pool);
2568d439d286SJames Smart 		return -EIO;
25693b5dd52aSJames Smart 	}
25703b5dd52aSJames Smart 	mempool_free(mbox, phba->mbox_mem_pool);
25714042629eSJames Smart 	if (phba->sli_rev == LPFC_SLI_REV4)
25724042629eSJames Smart 		lpfc_sli4_free_rpi(phba, rpi);
25733b5dd52aSJames Smart 	return 0;
25743b5dd52aSJames Smart }
25753b5dd52aSJames Smart 
25763b5dd52aSJames Smart /**
25773b5dd52aSJames Smart  * lpfcdiag_loop_get_xri - obtains the transmit and receive ids
25783b5dd52aSJames Smart  * @phba: Pointer to HBA context object
25793b5dd52aSJames Smart  * @rpi: Remote port login id
25803b5dd52aSJames Smart  * @txxri: Pointer to transmit exchange id
25813b5dd52aSJames Smart  * @rxxri: Pointer to response exchabge id
25823b5dd52aSJames Smart  *
25833b5dd52aSJames Smart  * This function obtains the transmit and receive ids required to send
25843b5dd52aSJames Smart  * an unsolicited ct command with a payload. A special lpfc FsType and CmdRsp
2585638eec06SColin Ian King  * flags are used to the unsolicited response handler is able to process
25863b5dd52aSJames Smart  * the ct command sent on the same port.
25873b5dd52aSJames Smart  **/
25883b5dd52aSJames Smart static int lpfcdiag_loop_get_xri(struct lpfc_hba *phba, uint16_t rpi,
25893b5dd52aSJames Smart 			 uint16_t *txxri, uint16_t * rxxri)
25903b5dd52aSJames Smart {
25913b5dd52aSJames Smart 	struct lpfc_bsg_event *evt;
25923b5dd52aSJames Smart 	struct lpfc_iocbq *cmdiocbq, *rspiocbq;
25933b5dd52aSJames Smart 	struct lpfc_dmabuf *dmabuf;
25943b5dd52aSJames Smart 	struct ulp_bde64 *bpl = NULL;
25953b5dd52aSJames Smart 	struct lpfc_sli_ct_request *ctreq = NULL;
25963b5dd52aSJames Smart 	int ret_val = 0;
2597d439d286SJames Smart 	int time_left;
25985a0916b4SJames Smart 	int iocb_stat = IOCB_SUCCESS;
25993b5dd52aSJames Smart 	unsigned long flags;
260061910d6aSJames Smart 	u32 status;
26013b5dd52aSJames Smart 
26023b5dd52aSJames Smart 	*txxri = 0;
26033b5dd52aSJames Smart 	*rxxri = 0;
26043b5dd52aSJames Smart 	evt = lpfc_bsg_event_new(FC_REG_CT_EVENT, current->pid,
26053b5dd52aSJames Smart 				SLI_CT_ELX_LOOPBACK);
26063b5dd52aSJames Smart 	if (!evt)
2607d439d286SJames Smart 		return -ENOMEM;
26083b5dd52aSJames Smart 
26093b5dd52aSJames Smart 	spin_lock_irqsave(&phba->ct_ev_lock, flags);
26103b5dd52aSJames Smart 	list_add(&evt->node, &phba->ct_ev_waiters);
26113b5dd52aSJames Smart 	lpfc_bsg_event_ref(evt);
26123b5dd52aSJames Smart 	spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
26133b5dd52aSJames Smart 
26143b5dd52aSJames Smart 	cmdiocbq = lpfc_sli_get_iocbq(phba);
26153b5dd52aSJames Smart 	rspiocbq = lpfc_sli_get_iocbq(phba);
26163b5dd52aSJames Smart 
26173b5dd52aSJames Smart 	dmabuf = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
26183b5dd52aSJames Smart 	if (dmabuf) {
26193b5dd52aSJames Smart 		dmabuf->virt = lpfc_mbuf_alloc(phba, 0, &dmabuf->phys);
2620c7495937SJames Smart 		if (dmabuf->virt) {
26213b5dd52aSJames Smart 			INIT_LIST_HEAD(&dmabuf->list);
26223b5dd52aSJames Smart 			bpl = (struct ulp_bde64 *) dmabuf->virt;
26233b5dd52aSJames Smart 			memset(bpl, 0, sizeof(*bpl));
26243b5dd52aSJames Smart 			ctreq = (struct lpfc_sli_ct_request *)(bpl + 1);
26253b5dd52aSJames Smart 			bpl->addrHigh =
2626c7495937SJames Smart 				le32_to_cpu(putPaddrHigh(dmabuf->phys +
2627c7495937SJames Smart 					sizeof(*bpl)));
26283b5dd52aSJames Smart 			bpl->addrLow =
2629c7495937SJames Smart 				le32_to_cpu(putPaddrLow(dmabuf->phys +
2630c7495937SJames Smart 					sizeof(*bpl)));
26313b5dd52aSJames Smart 			bpl->tus.f.bdeFlags = 0;
26323b5dd52aSJames Smart 			bpl->tus.f.bdeSize = ELX_LOOPBACK_HEADER_SZ;
26333b5dd52aSJames Smart 			bpl->tus.w = le32_to_cpu(bpl->tus.w);
26343b5dd52aSJames Smart 		}
2635c7495937SJames Smart 	}
26363b5dd52aSJames Smart 
26373b5dd52aSJames Smart 	if (cmdiocbq == NULL || rspiocbq == NULL ||
2638c7495937SJames Smart 	    dmabuf == NULL || bpl == NULL || ctreq == NULL ||
2639c7495937SJames Smart 		dmabuf->virt == NULL) {
2640d439d286SJames Smart 		ret_val = -ENOMEM;
26413b5dd52aSJames Smart 		goto err_get_xri_exit;
26423b5dd52aSJames Smart 	}
26433b5dd52aSJames Smart 
26443b5dd52aSJames Smart 	memset(ctreq, 0, ELX_LOOPBACK_HEADER_SZ);
26453b5dd52aSJames Smart 
26463b5dd52aSJames Smart 	ctreq->RevisionId.bits.Revision = SLI_CT_REVISION;
26473b5dd52aSJames Smart 	ctreq->RevisionId.bits.InId = 0;
26483b5dd52aSJames Smart 	ctreq->FsType = SLI_CT_ELX_LOOPBACK;
26493b5dd52aSJames Smart 	ctreq->FsSubType = 0;
26503b5dd52aSJames Smart 	ctreq->CommandResponse.bits.CmdRsp = ELX_LOOPBACK_XRI_SETUP;
26513b5dd52aSJames Smart 	ctreq->CommandResponse.bits.Size = 0;
26523b5dd52aSJames Smart 
2653d51cf5bdSJames Smart 	cmdiocbq->bpl_dmabuf = dmabuf;
2654a680a929SJames Smart 	cmdiocbq->cmd_flag |= LPFC_IO_LIBDFC;
26553b5dd52aSJames Smart 	cmdiocbq->vport = phba->pport;
2656a680a929SJames Smart 	cmdiocbq->cmd_cmpl = NULL;
26573b5dd52aSJames Smart 
265861910d6aSJames Smart 	lpfc_sli_prep_xmit_seq64(phba, cmdiocbq, dmabuf, rpi, 0, 1,
265961910d6aSJames Smart 				 FC_RCTL_DD_SOL_CTL, 0, CMD_XMIT_SEQUENCE64_CR);
266061910d6aSJames Smart 
2661d439d286SJames Smart 	iocb_stat = lpfc_sli_issue_iocb_wait(phba, LPFC_ELS_RING, cmdiocbq,
266261910d6aSJames Smart 					     rspiocbq, (phba->fc_ratov * 2)
26633b5dd52aSJames Smart 					     + LPFC_DRVR_TIMEOUT);
266461910d6aSJames Smart 
266561910d6aSJames Smart 	status = get_job_ulpstatus(phba, rspiocbq);
266661910d6aSJames Smart 	if (iocb_stat != IOCB_SUCCESS || status != IOCB_SUCCESS) {
2667d439d286SJames Smart 		ret_val = -EIO;
26683b5dd52aSJames Smart 		goto err_get_xri_exit;
2669d439d286SJames Smart 	}
267061910d6aSJames Smart 	*txxri = get_job_ulpcontext(phba, rspiocbq);
26713b5dd52aSJames Smart 
26723b5dd52aSJames Smart 	evt->waiting = 1;
26733b5dd52aSJames Smart 	evt->wait_time_stamp = jiffies;
2674d439d286SJames Smart 	time_left = wait_event_interruptible_timeout(
26753b5dd52aSJames Smart 		evt->wq, !list_empty(&evt->events_to_see),
2676256ec0d0SJames Smart 		msecs_to_jiffies(1000 *
2677256ec0d0SJames Smart 			((phba->fc_ratov * 2) + LPFC_DRVR_TIMEOUT)));
26783b5dd52aSJames Smart 	if (list_empty(&evt->events_to_see))
2679d439d286SJames Smart 		ret_val = (time_left) ? -EINTR : -ETIMEDOUT;
26803b5dd52aSJames Smart 	else {
26813b5dd52aSJames Smart 		spin_lock_irqsave(&phba->ct_ev_lock, flags);
26823b5dd52aSJames Smart 		list_move(evt->events_to_see.prev, &evt->events_to_get);
26833b5dd52aSJames Smart 		spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
26843b5dd52aSJames Smart 		*rxxri = (list_entry(evt->events_to_get.prev,
26853b5dd52aSJames Smart 				     typeof(struct event_data),
26863b5dd52aSJames Smart 				     node))->immed_dat;
26873b5dd52aSJames Smart 	}
26883b5dd52aSJames Smart 	evt->waiting = 0;
26893b5dd52aSJames Smart 
26903b5dd52aSJames Smart err_get_xri_exit:
26913b5dd52aSJames Smart 	spin_lock_irqsave(&phba->ct_ev_lock, flags);
26923b5dd52aSJames Smart 	lpfc_bsg_event_unref(evt); /* release ref */
26933b5dd52aSJames Smart 	lpfc_bsg_event_unref(evt); /* delete */
26943b5dd52aSJames Smart 	spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
26953b5dd52aSJames Smart 
26963b5dd52aSJames Smart 	if (dmabuf) {
26973b5dd52aSJames Smart 		if (dmabuf->virt)
26983b5dd52aSJames Smart 			lpfc_mbuf_free(phba, dmabuf->virt, dmabuf->phys);
26993b5dd52aSJames Smart 		kfree(dmabuf);
27003b5dd52aSJames Smart 	}
27013b5dd52aSJames Smart 
2702d439d286SJames Smart 	if (cmdiocbq && (iocb_stat != IOCB_TIMEDOUT))
27033b5dd52aSJames Smart 		lpfc_sli_release_iocbq(phba, cmdiocbq);
27043b5dd52aSJames Smart 	if (rspiocbq)
27053b5dd52aSJames Smart 		lpfc_sli_release_iocbq(phba, rspiocbq);
27063b5dd52aSJames Smart 	return ret_val;
27073b5dd52aSJames Smart }
27083b5dd52aSJames Smart 
27093b5dd52aSJames Smart /**
27107ad20aa9SJames Smart  * lpfc_bsg_dma_page_alloc - allocate a bsg mbox page sized dma buffers
27117ad20aa9SJames Smart  * @phba: Pointer to HBA context object
27127ad20aa9SJames Smart  *
27132ea259eeSJames Smart  * This function allocates BSG_MBOX_SIZE (4KB) page size dma buffer and
27149e03aa2fSJoe Perches  * returns the pointer to the buffer.
27157ad20aa9SJames Smart  **/
27167ad20aa9SJames Smart static struct lpfc_dmabuf *
27177ad20aa9SJames Smart lpfc_bsg_dma_page_alloc(struct lpfc_hba *phba)
27187ad20aa9SJames Smart {
27197ad20aa9SJames Smart 	struct lpfc_dmabuf *dmabuf;
27207ad20aa9SJames Smart 	struct pci_dev *pcidev = phba->pcidev;
27217ad20aa9SJames Smart 
27227ad20aa9SJames Smart 	/* allocate dma buffer struct */
27237ad20aa9SJames Smart 	dmabuf = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
27247ad20aa9SJames Smart 	if (!dmabuf)
27257ad20aa9SJames Smart 		return NULL;
27267ad20aa9SJames Smart 
27277ad20aa9SJames Smart 	INIT_LIST_HEAD(&dmabuf->list);
27287ad20aa9SJames Smart 
27297ad20aa9SJames Smart 	/* now, allocate dma buffer */
2730750afb08SLuis Chamberlain 	dmabuf->virt = dma_alloc_coherent(&pcidev->dev, BSG_MBOX_SIZE,
27317ad20aa9SJames Smart 					  &(dmabuf->phys), GFP_KERNEL);
27327ad20aa9SJames Smart 
27337ad20aa9SJames Smart 	if (!dmabuf->virt) {
27347ad20aa9SJames Smart 		kfree(dmabuf);
27357ad20aa9SJames Smart 		return NULL;
27367ad20aa9SJames Smart 	}
27377ad20aa9SJames Smart 
27387ad20aa9SJames Smart 	return dmabuf;
27397ad20aa9SJames Smart }
27407ad20aa9SJames Smart 
27417ad20aa9SJames Smart /**
27427ad20aa9SJames Smart  * lpfc_bsg_dma_page_free - free a bsg mbox page sized dma buffer
27437ad20aa9SJames Smart  * @phba: Pointer to HBA context object.
27447ad20aa9SJames Smart  * @dmabuf: Pointer to the bsg mbox page sized dma buffer descriptor.
27457ad20aa9SJames Smart  *
27467ad20aa9SJames Smart  * This routine just simply frees a dma buffer and its associated buffer
27477ad20aa9SJames Smart  * descriptor referred by @dmabuf.
27487ad20aa9SJames Smart  **/
27497ad20aa9SJames Smart static void
27507ad20aa9SJames Smart lpfc_bsg_dma_page_free(struct lpfc_hba *phba, struct lpfc_dmabuf *dmabuf)
27517ad20aa9SJames Smart {
27527ad20aa9SJames Smart 	struct pci_dev *pcidev = phba->pcidev;
27537ad20aa9SJames Smart 
27547ad20aa9SJames Smart 	if (!dmabuf)
27557ad20aa9SJames Smart 		return;
27567ad20aa9SJames Smart 
27577ad20aa9SJames Smart 	if (dmabuf->virt)
27587ad20aa9SJames Smart 		dma_free_coherent(&pcidev->dev, BSG_MBOX_SIZE,
27597ad20aa9SJames Smart 				  dmabuf->virt, dmabuf->phys);
27607ad20aa9SJames Smart 	kfree(dmabuf);
27617ad20aa9SJames Smart 	return;
27627ad20aa9SJames Smart }
27637ad20aa9SJames Smart 
27647ad20aa9SJames Smart /**
27657ad20aa9SJames Smart  * lpfc_bsg_dma_page_list_free - free a list of bsg mbox page sized dma buffers
27667ad20aa9SJames Smart  * @phba: Pointer to HBA context object.
27677ad20aa9SJames Smart  * @dmabuf_list: Pointer to a list of bsg mbox page sized dma buffer descs.
27687ad20aa9SJames Smart  *
27697ad20aa9SJames Smart  * This routine just simply frees all dma buffers and their associated buffer
27707ad20aa9SJames Smart  * descriptors referred by @dmabuf_list.
27717ad20aa9SJames Smart  **/
27727ad20aa9SJames Smart static void
27737ad20aa9SJames Smart lpfc_bsg_dma_page_list_free(struct lpfc_hba *phba,
27747ad20aa9SJames Smart 			    struct list_head *dmabuf_list)
27757ad20aa9SJames Smart {
27767ad20aa9SJames Smart 	struct lpfc_dmabuf *dmabuf, *next_dmabuf;
27777ad20aa9SJames Smart 
27787ad20aa9SJames Smart 	if (list_empty(dmabuf_list))
27797ad20aa9SJames Smart 		return;
27807ad20aa9SJames Smart 
27817ad20aa9SJames Smart 	list_for_each_entry_safe(dmabuf, next_dmabuf, dmabuf_list, list) {
27827ad20aa9SJames Smart 		list_del_init(&dmabuf->list);
27837ad20aa9SJames Smart 		lpfc_bsg_dma_page_free(phba, dmabuf);
27847ad20aa9SJames Smart 	}
27857ad20aa9SJames Smart 	return;
27867ad20aa9SJames Smart }
27877ad20aa9SJames Smart 
27887ad20aa9SJames Smart /**
27893b5dd52aSJames Smart  * diag_cmd_data_alloc - fills in a bde struct with dma buffers
27903b5dd52aSJames Smart  * @phba: Pointer to HBA context object
27913b5dd52aSJames Smart  * @bpl: Pointer to 64 bit bde structure
27923b5dd52aSJames Smart  * @size: Number of bytes to process
27933b5dd52aSJames Smart  * @nocopydata: Flag to copy user data into the allocated buffer
27943b5dd52aSJames Smart  *
27953b5dd52aSJames Smart  * This function allocates page size buffers and populates an lpfc_dmabufext.
27963b5dd52aSJames Smart  * If allowed the user data pointed to with indataptr is copied into the kernel
27973b5dd52aSJames Smart  * memory. The chained list of page size buffers is returned.
27983b5dd52aSJames Smart  **/
27993b5dd52aSJames Smart static struct lpfc_dmabufext *
28003b5dd52aSJames Smart diag_cmd_data_alloc(struct lpfc_hba *phba,
28013b5dd52aSJames Smart 		   struct ulp_bde64 *bpl, uint32_t size,
28023b5dd52aSJames Smart 		   int nocopydata)
28033b5dd52aSJames Smart {
28043b5dd52aSJames Smart 	struct lpfc_dmabufext *mlist = NULL;
28053b5dd52aSJames Smart 	struct lpfc_dmabufext *dmp;
28063b5dd52aSJames Smart 	int cnt, offset = 0, i = 0;
28073b5dd52aSJames Smart 	struct pci_dev *pcidev;
28083b5dd52aSJames Smart 
28093b5dd52aSJames Smart 	pcidev = phba->pcidev;
28103b5dd52aSJames Smart 
28113b5dd52aSJames Smart 	while (size) {
28123b5dd52aSJames Smart 		/* We get chunks of 4K */
28133b5dd52aSJames Smart 		if (size > BUF_SZ_4K)
28143b5dd52aSJames Smart 			cnt = BUF_SZ_4K;
28153b5dd52aSJames Smart 		else
28163b5dd52aSJames Smart 			cnt = size;
28173b5dd52aSJames Smart 
28183b5dd52aSJames Smart 		/* allocate struct lpfc_dmabufext buffer header */
28193b5dd52aSJames Smart 		dmp = kmalloc(sizeof(struct lpfc_dmabufext), GFP_KERNEL);
28203b5dd52aSJames Smart 		if (!dmp)
28213b5dd52aSJames Smart 			goto out;
28223b5dd52aSJames Smart 
28233b5dd52aSJames Smart 		INIT_LIST_HEAD(&dmp->dma.list);
28243b5dd52aSJames Smart 
28253b5dd52aSJames Smart 		/* Queue it to a linked list */
28263b5dd52aSJames Smart 		if (mlist)
28273b5dd52aSJames Smart 			list_add_tail(&dmp->dma.list, &mlist->dma.list);
28283b5dd52aSJames Smart 		else
28293b5dd52aSJames Smart 			mlist = dmp;
28303b5dd52aSJames Smart 
28313b5dd52aSJames Smart 		/* allocate buffer */
28323b5dd52aSJames Smart 		dmp->dma.virt = dma_alloc_coherent(&pcidev->dev,
28333b5dd52aSJames Smart 						   cnt,
28343b5dd52aSJames Smart 						   &(dmp->dma.phys),
28353b5dd52aSJames Smart 						   GFP_KERNEL);
28363b5dd52aSJames Smart 
28373b5dd52aSJames Smart 		if (!dmp->dma.virt)
28383b5dd52aSJames Smart 			goto out;
28393b5dd52aSJames Smart 
28403b5dd52aSJames Smart 		dmp->size = cnt;
28413b5dd52aSJames Smart 
28423b5dd52aSJames Smart 		if (nocopydata) {
28433b5dd52aSJames Smart 			bpl->tus.f.bdeFlags = 0;
28443b5dd52aSJames Smart 		} else {
28453b5dd52aSJames Smart 			memset((uint8_t *)dmp->dma.virt, 0, cnt);
28463b5dd52aSJames Smart 			bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
28473b5dd52aSJames Smart 		}
28483b5dd52aSJames Smart 
28493b5dd52aSJames Smart 		/* build buffer ptr list for IOCB */
28503b5dd52aSJames Smart 		bpl->addrLow = le32_to_cpu(putPaddrLow(dmp->dma.phys));
28513b5dd52aSJames Smart 		bpl->addrHigh = le32_to_cpu(putPaddrHigh(dmp->dma.phys));
28523b5dd52aSJames Smart 		bpl->tus.f.bdeSize = (ushort) cnt;
28533b5dd52aSJames Smart 		bpl->tus.w = le32_to_cpu(bpl->tus.w);
28543b5dd52aSJames Smart 		bpl++;
28553b5dd52aSJames Smart 
28563b5dd52aSJames Smart 		i++;
28573b5dd52aSJames Smart 		offset += cnt;
28583b5dd52aSJames Smart 		size -= cnt;
28593b5dd52aSJames Smart 	}
28603b5dd52aSJames Smart 
2861a2fc4aefSJames Smart 	if (mlist) {
28623b5dd52aSJames Smart 		mlist->flag = i;
28633b5dd52aSJames Smart 		return mlist;
2864a2fc4aefSJames Smart 	}
28653b5dd52aSJames Smart out:
28663b5dd52aSJames Smart 	diag_cmd_data_free(phba, mlist);
28673b5dd52aSJames Smart 	return NULL;
28683b5dd52aSJames Smart }
28693b5dd52aSJames Smart 
28703b5dd52aSJames Smart /**
287161910d6aSJames Smart  * lpfcdiag_sli3_loop_post_rxbufs - post the receive buffers for an unsol CT cmd
28723b5dd52aSJames Smart  * @phba: Pointer to HBA context object
28733b5dd52aSJames Smart  * @rxxri: Receive exchange id
28743b5dd52aSJames Smart  * @len: Number of data bytes
28753b5dd52aSJames Smart  *
287625985edcSLucas De Marchi  * This function allocates and posts a data buffer of sufficient size to receive
2877638eec06SColin Ian King  * an unsolicited CT command.
28783b5dd52aSJames Smart  **/
287961910d6aSJames Smart static int lpfcdiag_sli3_loop_post_rxbufs(struct lpfc_hba *phba, uint16_t rxxri,
28803b5dd52aSJames Smart 					  size_t len)
28813b5dd52aSJames Smart {
2882895427bdSJames Smart 	struct lpfc_sli_ring *pring;
28833b5dd52aSJames Smart 	struct lpfc_iocbq *cmdiocbq;
28843b5dd52aSJames Smart 	IOCB_t *cmd = NULL;
28853b5dd52aSJames Smart 	struct list_head head, *curr, *next;
28863b5dd52aSJames Smart 	struct lpfc_dmabuf *rxbmp;
28873b5dd52aSJames Smart 	struct lpfc_dmabuf *dmp;
28883b5dd52aSJames Smart 	struct lpfc_dmabuf *mp[2] = {NULL, NULL};
28893b5dd52aSJames Smart 	struct ulp_bde64 *rxbpl = NULL;
28903b5dd52aSJames Smart 	uint32_t num_bde;
28913b5dd52aSJames Smart 	struct lpfc_dmabufext *rxbuffer = NULL;
28923b5dd52aSJames Smart 	int ret_val = 0;
2893d439d286SJames Smart 	int iocb_stat;
28943b5dd52aSJames Smart 	int i = 0;
28953b5dd52aSJames Smart 
2896895427bdSJames Smart 	pring = lpfc_phba_elsring(phba);
2897895427bdSJames Smart 
28983b5dd52aSJames Smart 	cmdiocbq = lpfc_sli_get_iocbq(phba);
28993b5dd52aSJames Smart 	rxbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
29003b5dd52aSJames Smart 	if (rxbmp != NULL) {
29013b5dd52aSJames Smart 		rxbmp->virt = lpfc_mbuf_alloc(phba, 0, &rxbmp->phys);
2902c7495937SJames Smart 		if (rxbmp->virt) {
29033b5dd52aSJames Smart 			INIT_LIST_HEAD(&rxbmp->list);
29043b5dd52aSJames Smart 			rxbpl = (struct ulp_bde64 *) rxbmp->virt;
29053b5dd52aSJames Smart 			rxbuffer = diag_cmd_data_alloc(phba, rxbpl, len, 0);
29063b5dd52aSJames Smart 		}
2907c7495937SJames Smart 	}
29083b5dd52aSJames Smart 
29091234a6d5SDick Kennedy 	if (!cmdiocbq || !rxbmp || !rxbpl || !rxbuffer || !pring) {
2910d439d286SJames Smart 		ret_val = -ENOMEM;
29113b5dd52aSJames Smart 		goto err_post_rxbufs_exit;
29123b5dd52aSJames Smart 	}
29133b5dd52aSJames Smart 
29143b5dd52aSJames Smart 	/* Queue buffers for the receive exchange */
29153b5dd52aSJames Smart 	num_bde = (uint32_t)rxbuffer->flag;
29163b5dd52aSJames Smart 	dmp = &rxbuffer->dma;
29173b5dd52aSJames Smart 	cmd = &cmdiocbq->iocb;
29183b5dd52aSJames Smart 	i = 0;
29193b5dd52aSJames Smart 
29203b5dd52aSJames Smart 	INIT_LIST_HEAD(&head);
29213b5dd52aSJames Smart 	list_add_tail(&head, &dmp->list);
29223b5dd52aSJames Smart 	list_for_each_safe(curr, next, &head) {
29233b5dd52aSJames Smart 		mp[i] = list_entry(curr, struct lpfc_dmabuf, list);
29243b5dd52aSJames Smart 		list_del(curr);
29253b5dd52aSJames Smart 
29263b5dd52aSJames Smart 		if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) {
29273b5dd52aSJames Smart 			mp[i]->buffer_tag = lpfc_sli_get_buffer_tag(phba);
29283b5dd52aSJames Smart 			cmd->un.quexri64cx.buff.bde.addrHigh =
29293b5dd52aSJames Smart 				putPaddrHigh(mp[i]->phys);
29303b5dd52aSJames Smart 			cmd->un.quexri64cx.buff.bde.addrLow =
29313b5dd52aSJames Smart 				putPaddrLow(mp[i]->phys);
29323b5dd52aSJames Smart 			cmd->un.quexri64cx.buff.bde.tus.f.bdeSize =
29333b5dd52aSJames Smart 				((struct lpfc_dmabufext *)mp[i])->size;
29343b5dd52aSJames Smart 			cmd->un.quexri64cx.buff.buffer_tag = mp[i]->buffer_tag;
29353b5dd52aSJames Smart 			cmd->ulpCommand = CMD_QUE_XRI64_CX;
29363b5dd52aSJames Smart 			cmd->ulpPU = 0;
29373b5dd52aSJames Smart 			cmd->ulpLe = 1;
29383b5dd52aSJames Smart 			cmd->ulpBdeCount = 1;
29393b5dd52aSJames Smart 			cmd->unsli3.que_xri64cx_ext_words.ebde_count = 0;
29403b5dd52aSJames Smart 
29413b5dd52aSJames Smart 		} else {
29423b5dd52aSJames Smart 			cmd->un.cont64[i].addrHigh = putPaddrHigh(mp[i]->phys);
29433b5dd52aSJames Smart 			cmd->un.cont64[i].addrLow = putPaddrLow(mp[i]->phys);
29443b5dd52aSJames Smart 			cmd->un.cont64[i].tus.f.bdeSize =
29453b5dd52aSJames Smart 				((struct lpfc_dmabufext *)mp[i])->size;
29463b5dd52aSJames Smart 			cmd->ulpBdeCount = ++i;
29473b5dd52aSJames Smart 
29483b5dd52aSJames Smart 			if ((--num_bde > 0) && (i < 2))
29493b5dd52aSJames Smart 				continue;
29503b5dd52aSJames Smart 
29513b5dd52aSJames Smart 			cmd->ulpCommand = CMD_QUE_XRI_BUF64_CX;
29523b5dd52aSJames Smart 			cmd->ulpLe = 1;
29533b5dd52aSJames Smart 		}
29543b5dd52aSJames Smart 
29553b5dd52aSJames Smart 		cmd->ulpClass = CLASS3;
29563b5dd52aSJames Smart 		cmd->ulpContext = rxxri;
29573b5dd52aSJames Smart 
2958d439d286SJames Smart 		iocb_stat = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq,
2959d439d286SJames Smart 						0);
2960d439d286SJames Smart 		if (iocb_stat == IOCB_ERROR) {
29613b5dd52aSJames Smart 			diag_cmd_data_free(phba,
29623b5dd52aSJames Smart 				(struct lpfc_dmabufext *)mp[0]);
29633b5dd52aSJames Smart 			if (mp[1])
29643b5dd52aSJames Smart 				diag_cmd_data_free(phba,
29653b5dd52aSJames Smart 					  (struct lpfc_dmabufext *)mp[1]);
29663b5dd52aSJames Smart 			dmp = list_entry(next, struct lpfc_dmabuf, list);
2967d439d286SJames Smart 			ret_val = -EIO;
29683b5dd52aSJames Smart 			goto err_post_rxbufs_exit;
29693b5dd52aSJames Smart 		}
29703b5dd52aSJames Smart 
29713b5dd52aSJames Smart 		lpfc_sli_ringpostbuf_put(phba, pring, mp[0]);
29723b5dd52aSJames Smart 		if (mp[1]) {
29733b5dd52aSJames Smart 			lpfc_sli_ringpostbuf_put(phba, pring, mp[1]);
29743b5dd52aSJames Smart 			mp[1] = NULL;
29753b5dd52aSJames Smart 		}
29763b5dd52aSJames Smart 
29773b5dd52aSJames Smart 		/* The iocb was freed by lpfc_sli_issue_iocb */
29783b5dd52aSJames Smart 		cmdiocbq = lpfc_sli_get_iocbq(phba);
29793b5dd52aSJames Smart 		if (!cmdiocbq) {
29803b5dd52aSJames Smart 			dmp = list_entry(next, struct lpfc_dmabuf, list);
2981d439d286SJames Smart 			ret_val = -EIO;
29823b5dd52aSJames Smart 			goto err_post_rxbufs_exit;
29833b5dd52aSJames Smart 		}
29843b5dd52aSJames Smart 		cmd = &cmdiocbq->iocb;
29853b5dd52aSJames Smart 		i = 0;
29863b5dd52aSJames Smart 	}
29873b5dd52aSJames Smart 	list_del(&head);
29883b5dd52aSJames Smart 
29893b5dd52aSJames Smart err_post_rxbufs_exit:
29903b5dd52aSJames Smart 
29913b5dd52aSJames Smart 	if (rxbmp) {
29923b5dd52aSJames Smart 		if (rxbmp->virt)
29933b5dd52aSJames Smart 			lpfc_mbuf_free(phba, rxbmp->virt, rxbmp->phys);
29943b5dd52aSJames Smart 		kfree(rxbmp);
29953b5dd52aSJames Smart 	}
29963b5dd52aSJames Smart 
29973b5dd52aSJames Smart 	if (cmdiocbq)
29983b5dd52aSJames Smart 		lpfc_sli_release_iocbq(phba, cmdiocbq);
29993b5dd52aSJames Smart 	return ret_val;
30003b5dd52aSJames Smart }
30013b5dd52aSJames Smart 
30023b5dd52aSJames Smart /**
30037ad20aa9SJames Smart  * lpfc_bsg_diag_loopback_run - run loopback on a port by issue ct cmd to itself
30043b5dd52aSJames Smart  * @job: LPFC_BSG_VENDOR_DIAG_TEST fc_bsg_job
30053b5dd52aSJames Smart  *
30063b5dd52aSJames Smart  * This function receives a user data buffer to be transmitted and received on
30073b5dd52aSJames Smart  * the same port, the link must be up and in loopback mode prior
30083b5dd52aSJames Smart  * to being called.
30093b5dd52aSJames Smart  * 1. A kernel buffer is allocated to copy the user data into.
30103b5dd52aSJames Smart  * 2. The port registers with "itself".
30113b5dd52aSJames Smart  * 3. The transmit and receive exchange ids are obtained.
30123b5dd52aSJames Smart  * 4. The receive exchange id is posted.
30133b5dd52aSJames Smart  * 5. A new els loopback event is created.
30143b5dd52aSJames Smart  * 6. The command and response iocbs are allocated.
30153b5dd52aSJames Smart  * 7. The cmd iocb FsType is set to elx loopback and the CmdRsp to looppback.
30163b5dd52aSJames Smart  *
30173b5dd52aSJames Smart  * This function is meant to be called n times while the port is in loopback
30183b5dd52aSJames Smart  * so it is the apps responsibility to issue a reset to take the port out
30193b5dd52aSJames Smart  * of loopback mode.
30203b5dd52aSJames Smart  **/
30213b5dd52aSJames Smart static int
302275cc8cfcSJohannes Thumshirn lpfc_bsg_diag_loopback_run(struct bsg_job *job)
30233b5dd52aSJames Smart {
3024cd21c605SJohannes Thumshirn 	struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
302501e0e15cSJohannes Thumshirn 	struct fc_bsg_reply *bsg_reply = job->reply;
30263b5dd52aSJames Smart 	struct lpfc_hba *phba = vport->phba;
30273b5dd52aSJames Smart 	struct lpfc_bsg_event *evt;
30283b5dd52aSJames Smart 	struct event_data *evdat;
30293b5dd52aSJames Smart 	struct lpfc_sli *psli = &phba->sli;
30303b5dd52aSJames Smart 	uint32_t size;
30313b5dd52aSJames Smart 	uint32_t full_size;
30323b5dd52aSJames Smart 	size_t segment_len = 0, segment_offset = 0, current_offset = 0;
30334042629eSJames Smart 	uint16_t rpi = 0;
30341b51197dSJames Smart 	struct lpfc_iocbq *cmdiocbq, *rspiocbq = NULL;
303561910d6aSJames Smart 	union lpfc_wqe128 *cmdwqe, *rspwqe;
30363b5dd52aSJames Smart 	struct lpfc_sli_ct_request *ctreq;
30373b5dd52aSJames Smart 	struct lpfc_dmabuf *txbmp;
30383b5dd52aSJames Smart 	struct ulp_bde64 *txbpl = NULL;
30393b5dd52aSJames Smart 	struct lpfc_dmabufext *txbuffer = NULL;
30403b5dd52aSJames Smart 	struct list_head head;
30413b5dd52aSJames Smart 	struct lpfc_dmabuf  *curr;
30421b51197dSJames Smart 	uint16_t txxri = 0, rxxri;
30433b5dd52aSJames Smart 	uint32_t num_bde;
30443b5dd52aSJames Smart 	uint8_t *ptr = NULL, *rx_databuf = NULL;
30453b5dd52aSJames Smart 	int rc = 0;
3046d439d286SJames Smart 	int time_left;
30475a0916b4SJames Smart 	int iocb_stat = IOCB_SUCCESS;
30483b5dd52aSJames Smart 	unsigned long flags;
30493b5dd52aSJames Smart 	void *dataout = NULL;
30503b5dd52aSJames Smart 	uint32_t total_mem;
30513b5dd52aSJames Smart 
30523b5dd52aSJames Smart 	/* in case no data is returned return just the return code */
305301e0e15cSJohannes Thumshirn 	bsg_reply->reply_payload_rcv_len = 0;
30543b5dd52aSJames Smart 
30553b5dd52aSJames Smart 	if (job->request_len <
30563b5dd52aSJames Smart 	    sizeof(struct fc_bsg_request) + sizeof(struct diag_mode_test)) {
30573b5dd52aSJames Smart 		lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
30583b5dd52aSJames Smart 				"2739 Received DIAG TEST request below minimum "
30593b5dd52aSJames Smart 				"size\n");
30603b5dd52aSJames Smart 		rc = -EINVAL;
30613b5dd52aSJames Smart 		goto loopback_test_exit;
30623b5dd52aSJames Smart 	}
30633b5dd52aSJames Smart 
30643b5dd52aSJames Smart 	if (job->request_payload.payload_len !=
30653b5dd52aSJames Smart 		job->reply_payload.payload_len) {
30663b5dd52aSJames Smart 		rc = -EINVAL;
30673b5dd52aSJames Smart 		goto loopback_test_exit;
30683b5dd52aSJames Smart 	}
30693b5dd52aSJames Smart 
30703b5dd52aSJames Smart 	if ((phba->link_state == LPFC_HBA_ERROR) ||
30713b5dd52aSJames Smart 	    (psli->sli_flag & LPFC_BLOCK_MGMT_IO) ||
30723b5dd52aSJames Smart 	    (!(psli->sli_flag & LPFC_SLI_ACTIVE))) {
30733b5dd52aSJames Smart 		rc = -EACCES;
30743b5dd52aSJames Smart 		goto loopback_test_exit;
30753b5dd52aSJames Smart 	}
30763b5dd52aSJames Smart 
30773b5dd52aSJames Smart 	if (!lpfc_is_link_up(phba) || !(phba->link_flag & LS_LOOPBACK_MODE)) {
30783b5dd52aSJames Smart 		rc = -EACCES;
30793b5dd52aSJames Smart 		goto loopback_test_exit;
30803b5dd52aSJames Smart 	}
30813b5dd52aSJames Smart 
30823b5dd52aSJames Smart 	size = job->request_payload.payload_len;
30833b5dd52aSJames Smart 	full_size = size + ELX_LOOPBACK_HEADER_SZ; /* plus the header */
30843b5dd52aSJames Smart 
30853b5dd52aSJames Smart 	if ((size == 0) || (size > 80 * BUF_SZ_4K)) {
30863b5dd52aSJames Smart 		rc = -ERANGE;
30873b5dd52aSJames Smart 		goto loopback_test_exit;
30883b5dd52aSJames Smart 	}
30893b5dd52aSJames Smart 
309063e801ceSJames Smart 	if (full_size >= BUF_SZ_4K) {
30913b5dd52aSJames Smart 		/*
30923b5dd52aSJames Smart 		 * Allocate memory for ioctl data. If buffer is bigger than 64k,
30933b5dd52aSJames Smart 		 * then we allocate 64k and re-use that buffer over and over to
30943b5dd52aSJames Smart 		 * xfer the whole block. This is because Linux kernel has a
30953b5dd52aSJames Smart 		 * problem allocating more than 120k of kernel space memory. Saw
30963b5dd52aSJames Smart 		 * problem with GET_FCPTARGETMAPPING...
30973b5dd52aSJames Smart 		 */
30983b5dd52aSJames Smart 		if (size <= (64 * 1024))
309963e801ceSJames Smart 			total_mem = full_size;
31003b5dd52aSJames Smart 		else
31013b5dd52aSJames Smart 			total_mem = 64 * 1024;
31023b5dd52aSJames Smart 	} else
31033b5dd52aSJames Smart 		/* Allocate memory for ioctl data */
31043b5dd52aSJames Smart 		total_mem = BUF_SZ_4K;
31053b5dd52aSJames Smart 
31063b5dd52aSJames Smart 	dataout = kmalloc(total_mem, GFP_KERNEL);
31073b5dd52aSJames Smart 	if (dataout == NULL) {
31083b5dd52aSJames Smart 		rc = -ENOMEM;
31093b5dd52aSJames Smart 		goto loopback_test_exit;
31103b5dd52aSJames Smart 	}
31113b5dd52aSJames Smart 
31123b5dd52aSJames Smart 	ptr = dataout;
31133b5dd52aSJames Smart 	ptr += ELX_LOOPBACK_HEADER_SZ;
31143b5dd52aSJames Smart 	sg_copy_to_buffer(job->request_payload.sg_list,
31153b5dd52aSJames Smart 				job->request_payload.sg_cnt,
31163b5dd52aSJames Smart 				ptr, size);
31173b5dd52aSJames Smart 	rc = lpfcdiag_loop_self_reg(phba, &rpi);
3118d439d286SJames Smart 	if (rc)
31193b5dd52aSJames Smart 		goto loopback_test_exit;
31203b5dd52aSJames Smart 
31211b51197dSJames Smart 	if (phba->sli_rev < LPFC_SLI_REV4) {
31223b5dd52aSJames Smart 		rc = lpfcdiag_loop_get_xri(phba, rpi, &txxri, &rxxri);
31233b5dd52aSJames Smart 		if (rc) {
31243b5dd52aSJames Smart 			lpfcdiag_loop_self_unreg(phba, rpi);
31253b5dd52aSJames Smart 			goto loopback_test_exit;
31263b5dd52aSJames Smart 		}
31273b5dd52aSJames Smart 
312861910d6aSJames Smart 		rc = lpfcdiag_sli3_loop_post_rxbufs(phba, rxxri, full_size);
31293b5dd52aSJames Smart 		if (rc) {
31303b5dd52aSJames Smart 			lpfcdiag_loop_self_unreg(phba, rpi);
31313b5dd52aSJames Smart 			goto loopback_test_exit;
31323b5dd52aSJames Smart 		}
31331b51197dSJames Smart 	}
31343b5dd52aSJames Smart 	evt = lpfc_bsg_event_new(FC_REG_CT_EVENT, current->pid,
31353b5dd52aSJames Smart 				SLI_CT_ELX_LOOPBACK);
31363b5dd52aSJames Smart 	if (!evt) {
31373b5dd52aSJames Smart 		lpfcdiag_loop_self_unreg(phba, rpi);
31383b5dd52aSJames Smart 		rc = -ENOMEM;
31393b5dd52aSJames Smart 		goto loopback_test_exit;
31403b5dd52aSJames Smart 	}
31413b5dd52aSJames Smart 
31423b5dd52aSJames Smart 	spin_lock_irqsave(&phba->ct_ev_lock, flags);
31433b5dd52aSJames Smart 	list_add(&evt->node, &phba->ct_ev_waiters);
31443b5dd52aSJames Smart 	lpfc_bsg_event_ref(evt);
31453b5dd52aSJames Smart 	spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
31463b5dd52aSJames Smart 
31473b5dd52aSJames Smart 	cmdiocbq = lpfc_sli_get_iocbq(phba);
31481b51197dSJames Smart 	if (phba->sli_rev < LPFC_SLI_REV4)
31493b5dd52aSJames Smart 		rspiocbq = lpfc_sli_get_iocbq(phba);
31503b5dd52aSJames Smart 	txbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
31513b5dd52aSJames Smart 
31523b5dd52aSJames Smart 	if (txbmp) {
31533b5dd52aSJames Smart 		txbmp->virt = lpfc_mbuf_alloc(phba, 0, &txbmp->phys);
3154c7495937SJames Smart 		if (txbmp->virt) {
31553b5dd52aSJames Smart 			INIT_LIST_HEAD(&txbmp->list);
31563b5dd52aSJames Smart 			txbpl = (struct ulp_bde64 *) txbmp->virt;
31573b5dd52aSJames Smart 			txbuffer = diag_cmd_data_alloc(phba,
31583b5dd52aSJames Smart 							txbpl, full_size, 0);
31593b5dd52aSJames Smart 		}
3160c7495937SJames Smart 	}
31613b5dd52aSJames Smart 
31621b51197dSJames Smart 	if (!cmdiocbq || !txbmp || !txbpl || !txbuffer || !txbmp->virt) {
31631b51197dSJames Smart 		rc = -ENOMEM;
31641b51197dSJames Smart 		goto err_loopback_test_exit;
31651b51197dSJames Smart 	}
31661b51197dSJames Smart 	if ((phba->sli_rev < LPFC_SLI_REV4) && !rspiocbq) {
31673b5dd52aSJames Smart 		rc = -ENOMEM;
31683b5dd52aSJames Smart 		goto err_loopback_test_exit;
31693b5dd52aSJames Smart 	}
31703b5dd52aSJames Smart 
317161910d6aSJames Smart 	cmdwqe = &cmdiocbq->wqe;
317216cc2ba7SMuhammad Usama Anjum 	memset(cmdwqe, 0, sizeof(*cmdwqe));
317361910d6aSJames Smart 	if (phba->sli_rev < LPFC_SLI_REV4) {
317461910d6aSJames Smart 		rspwqe = &rspiocbq->wqe;
317516cc2ba7SMuhammad Usama Anjum 		memset(rspwqe, 0, sizeof(*rspwqe));
317661910d6aSJames Smart 	}
31773b5dd52aSJames Smart 
31783b5dd52aSJames Smart 	INIT_LIST_HEAD(&head);
31793b5dd52aSJames Smart 	list_add_tail(&head, &txbuffer->dma.list);
31803b5dd52aSJames Smart 	list_for_each_entry(curr, &head, list) {
31813b5dd52aSJames Smart 		segment_len = ((struct lpfc_dmabufext *)curr)->size;
31823b5dd52aSJames Smart 		if (current_offset == 0) {
31833b5dd52aSJames Smart 			ctreq = curr->virt;
31843b5dd52aSJames Smart 			memset(ctreq, 0, ELX_LOOPBACK_HEADER_SZ);
31853b5dd52aSJames Smart 			ctreq->RevisionId.bits.Revision = SLI_CT_REVISION;
31863b5dd52aSJames Smart 			ctreq->RevisionId.bits.InId = 0;
31873b5dd52aSJames Smart 			ctreq->FsType = SLI_CT_ELX_LOOPBACK;
31883b5dd52aSJames Smart 			ctreq->FsSubType = 0;
31899cefd6e7SJustin Tee 			ctreq->CommandResponse.bits.CmdRsp = cpu_to_be16(ELX_LOOPBACK_DATA);
31909cefd6e7SJustin Tee 			ctreq->CommandResponse.bits.Size   = cpu_to_be16(size);
31913b5dd52aSJames Smart 			segment_offset = ELX_LOOPBACK_HEADER_SZ;
31923b5dd52aSJames Smart 		} else
31933b5dd52aSJames Smart 			segment_offset = 0;
31943b5dd52aSJames Smart 
31953b5dd52aSJames Smart 		BUG_ON(segment_offset >= segment_len);
31963b5dd52aSJames Smart 		memcpy(curr->virt + segment_offset,
31973b5dd52aSJames Smart 			ptr + current_offset,
31983b5dd52aSJames Smart 			segment_len - segment_offset);
31993b5dd52aSJames Smart 
32003b5dd52aSJames Smart 		current_offset += segment_len - segment_offset;
32013b5dd52aSJames Smart 		BUG_ON(current_offset > size);
32023b5dd52aSJames Smart 	}
32033b5dd52aSJames Smart 	list_del(&head);
32043b5dd52aSJames Smart 
32053b5dd52aSJames Smart 	/* Build the XMIT_SEQUENCE iocb */
32063b5dd52aSJames Smart 	num_bde = (uint32_t)txbuffer->flag;
32073b5dd52aSJames Smart 
320861910d6aSJames Smart 	cmdiocbq->num_bdes = num_bde;
3209a680a929SJames Smart 	cmdiocbq->cmd_flag |= LPFC_IO_LIBDFC;
3210a680a929SJames Smart 	cmdiocbq->cmd_flag |= LPFC_IO_LOOPBACK;
32113b5dd52aSJames Smart 	cmdiocbq->vport = phba->pport;
3212a680a929SJames Smart 	cmdiocbq->cmd_cmpl = NULL;
3213d51cf5bdSJames Smart 	cmdiocbq->bpl_dmabuf = txbmp;
321461910d6aSJames Smart 
321561910d6aSJames Smart 	if (phba->sli_rev < LPFC_SLI_REV4) {
321661910d6aSJames Smart 		lpfc_sli_prep_xmit_seq64(phba, cmdiocbq, txbmp, 0, txxri,
321761910d6aSJames Smart 					 num_bde, FC_RCTL_DD_UNSOL_CTL, 1,
321861910d6aSJames Smart 					 CMD_XMIT_SEQUENCE64_CX);
321961910d6aSJames Smart 
322061910d6aSJames Smart 	} else {
322161910d6aSJames Smart 		lpfc_sli_prep_xmit_seq64(phba, cmdiocbq, txbmp,
322261910d6aSJames Smart 					 phba->sli4_hba.rpi_ids[rpi], 0xffff,
322361910d6aSJames Smart 					 full_size, FC_RCTL_DD_UNSOL_CTL, 1,
322461910d6aSJames Smart 					 CMD_XMIT_SEQUENCE64_WQE);
322561910d6aSJames Smart 		cmdiocbq->sli4_xritag = NO_XRI;
322661910d6aSJames Smart 	}
322756134142SJames Smart 
3228d439d286SJames Smart 	iocb_stat = lpfc_sli_issue_iocb_wait(phba, LPFC_ELS_RING, cmdiocbq,
3229d439d286SJames Smart 					     rspiocbq, (phba->fc_ratov * 2) +
3230d439d286SJames Smart 					     LPFC_DRVR_TIMEOUT);
323161910d6aSJames Smart 	if (iocb_stat != IOCB_SUCCESS ||
323261910d6aSJames Smart 	    (phba->sli_rev < LPFC_SLI_REV4 &&
323361910d6aSJames Smart 	     (get_job_ulpstatus(phba, rspiocbq) != IOSTAT_SUCCESS))) {
32341b51197dSJames Smart 		lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
32351b51197dSJames Smart 				"3126 Failed loopback test issue iocb: "
32361b51197dSJames Smart 				"iocb_stat:x%x\n", iocb_stat);
32373b5dd52aSJames Smart 		rc = -EIO;
32383b5dd52aSJames Smart 		goto err_loopback_test_exit;
32393b5dd52aSJames Smart 	}
32403b5dd52aSJames Smart 
32413b5dd52aSJames Smart 	evt->waiting = 1;
3242d439d286SJames Smart 	time_left = wait_event_interruptible_timeout(
32433b5dd52aSJames Smart 		evt->wq, !list_empty(&evt->events_to_see),
3244256ec0d0SJames Smart 		msecs_to_jiffies(1000 *
3245256ec0d0SJames Smart 			((phba->fc_ratov * 2) + LPFC_DRVR_TIMEOUT)));
32463b5dd52aSJames Smart 	evt->waiting = 0;
32471b51197dSJames Smart 	if (list_empty(&evt->events_to_see)) {
3248d439d286SJames Smart 		rc = (time_left) ? -EINTR : -ETIMEDOUT;
32491b51197dSJames Smart 		lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
32501b51197dSJames Smart 				"3125 Not receiving unsolicited event, "
32511b51197dSJames Smart 				"rc:x%x\n", rc);
32521b51197dSJames Smart 	} else {
32533b5dd52aSJames Smart 		spin_lock_irqsave(&phba->ct_ev_lock, flags);
32543b5dd52aSJames Smart 		list_move(evt->events_to_see.prev, &evt->events_to_get);
32553b5dd52aSJames Smart 		evdat = list_entry(evt->events_to_get.prev,
32563b5dd52aSJames Smart 				   typeof(*evdat), node);
32573b5dd52aSJames Smart 		spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
32583b5dd52aSJames Smart 		rx_databuf = evdat->data;
32593b5dd52aSJames Smart 		if (evdat->len != full_size) {
32603b5dd52aSJames Smart 			lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
32613b5dd52aSJames Smart 				"1603 Loopback test did not receive expected "
32623b5dd52aSJames Smart 				"data length. actual length 0x%x expected "
32633b5dd52aSJames Smart 				"length 0x%x\n",
32643b5dd52aSJames Smart 				evdat->len, full_size);
32653b5dd52aSJames Smart 			rc = -EIO;
32663b5dd52aSJames Smart 		} else if (rx_databuf == NULL)
32673b5dd52aSJames Smart 			rc = -EIO;
32683b5dd52aSJames Smart 		else {
32693b5dd52aSJames Smart 			rc = IOCB_SUCCESS;
32703b5dd52aSJames Smart 			/* skip over elx loopback header */
32713b5dd52aSJames Smart 			rx_databuf += ELX_LOOPBACK_HEADER_SZ;
327201e0e15cSJohannes Thumshirn 			bsg_reply->reply_payload_rcv_len =
32733b5dd52aSJames Smart 				sg_copy_from_buffer(job->reply_payload.sg_list,
32743b5dd52aSJames Smart 						    job->reply_payload.sg_cnt,
32753b5dd52aSJames Smart 						    rx_databuf, size);
327601e0e15cSJohannes Thumshirn 			bsg_reply->reply_payload_rcv_len = size;
32773b5dd52aSJames Smart 		}
32783b5dd52aSJames Smart 	}
32793b5dd52aSJames Smart 
32803b5dd52aSJames Smart err_loopback_test_exit:
32813b5dd52aSJames Smart 	lpfcdiag_loop_self_unreg(phba, rpi);
32823b5dd52aSJames Smart 
32833b5dd52aSJames Smart 	spin_lock_irqsave(&phba->ct_ev_lock, flags);
32843b5dd52aSJames Smart 	lpfc_bsg_event_unref(evt); /* release ref */
32853b5dd52aSJames Smart 	lpfc_bsg_event_unref(evt); /* delete */
32863b5dd52aSJames Smart 	spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
32873b5dd52aSJames Smart 
32885a0916b4SJames Smart 	if ((cmdiocbq != NULL) && (iocb_stat != IOCB_TIMEDOUT))
32893b5dd52aSJames Smart 		lpfc_sli_release_iocbq(phba, cmdiocbq);
32903b5dd52aSJames Smart 
32913b5dd52aSJames Smart 	if (rspiocbq != NULL)
32923b5dd52aSJames Smart 		lpfc_sli_release_iocbq(phba, rspiocbq);
32933b5dd52aSJames Smart 
32943b5dd52aSJames Smart 	if (txbmp != NULL) {
32953b5dd52aSJames Smart 		if (txbpl != NULL) {
32963b5dd52aSJames Smart 			if (txbuffer != NULL)
32973b5dd52aSJames Smart 				diag_cmd_data_free(phba, txbuffer);
32983b5dd52aSJames Smart 			lpfc_mbuf_free(phba, txbmp->virt, txbmp->phys);
32993b5dd52aSJames Smart 		}
33003b5dd52aSJames Smart 		kfree(txbmp);
33013b5dd52aSJames Smart 	}
33023b5dd52aSJames Smart 
33033b5dd52aSJames Smart loopback_test_exit:
33043b5dd52aSJames Smart 	kfree(dataout);
33053b5dd52aSJames Smart 	/* make error code available to userspace */
330601e0e15cSJohannes Thumshirn 	bsg_reply->result = rc;
33073b5dd52aSJames Smart 	job->dd_data = NULL;
33083b5dd52aSJames Smart 	/* complete the job back to userspace if no error */
33091b51197dSJames Smart 	if (rc == IOCB_SUCCESS)
331006548160SJohannes Thumshirn 		bsg_job_done(job, bsg_reply->result,
33111abaede7SJohannes Thumshirn 			       bsg_reply->reply_payload_rcv_len);
33123b5dd52aSJames Smart 	return rc;
33133b5dd52aSJames Smart }
33143b5dd52aSJames Smart 
33153b5dd52aSJames Smart /**
33163b5dd52aSJames Smart  * lpfc_bsg_get_dfc_rev - process a GET_DFC_REV bsg vendor command
33173b5dd52aSJames Smart  * @job: GET_DFC_REV fc_bsg_job
33183b5dd52aSJames Smart  **/
33193b5dd52aSJames Smart static int
332075cc8cfcSJohannes Thumshirn lpfc_bsg_get_dfc_rev(struct bsg_job *job)
33213b5dd52aSJames Smart {
3322cd21c605SJohannes Thumshirn 	struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
332301e0e15cSJohannes Thumshirn 	struct fc_bsg_reply *bsg_reply = job->reply;
33243b5dd52aSJames Smart 	struct lpfc_hba *phba = vport->phba;
33253b5dd52aSJames Smart 	struct get_mgmt_rev_reply *event_reply;
33263b5dd52aSJames Smart 	int rc = 0;
33273b5dd52aSJames Smart 
33283b5dd52aSJames Smart 	if (job->request_len <
33293b5dd52aSJames Smart 	    sizeof(struct fc_bsg_request) + sizeof(struct get_mgmt_rev)) {
33303b5dd52aSJames Smart 		lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
33313b5dd52aSJames Smart 				"2740 Received GET_DFC_REV request below "
33323b5dd52aSJames Smart 				"minimum size\n");
33333b5dd52aSJames Smart 		rc = -EINVAL;
33343b5dd52aSJames Smart 		goto job_error;
33353b5dd52aSJames Smart 	}
33363b5dd52aSJames Smart 
33373b5dd52aSJames Smart 	event_reply = (struct get_mgmt_rev_reply *)
333801e0e15cSJohannes Thumshirn 		bsg_reply->reply_data.vendor_reply.vendor_rsp;
33393b5dd52aSJames Smart 
3340feb3cc57SDick Kennedy 	if (job->reply_len < sizeof(*bsg_reply) + sizeof(*event_reply)) {
33413b5dd52aSJames Smart 		lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
33423b5dd52aSJames Smart 				"2741 Received GET_DFC_REV reply below "
33433b5dd52aSJames Smart 				"minimum size\n");
33443b5dd52aSJames Smart 		rc = -EINVAL;
33453b5dd52aSJames Smart 		goto job_error;
33463b5dd52aSJames Smart 	}
33473b5dd52aSJames Smart 
33483b5dd52aSJames Smart 	event_reply->info.a_Major = MANAGEMENT_MAJOR_REV;
33493b5dd52aSJames Smart 	event_reply->info.a_Minor = MANAGEMENT_MINOR_REV;
33503b5dd52aSJames Smart job_error:
335101e0e15cSJohannes Thumshirn 	bsg_reply->result = rc;
33523b5dd52aSJames Smart 	if (rc == 0)
335306548160SJohannes Thumshirn 		bsg_job_done(job, bsg_reply->result,
33541abaede7SJohannes Thumshirn 			       bsg_reply->reply_payload_rcv_len);
33553b5dd52aSJames Smart 	return rc;
33563b5dd52aSJames Smart }
33573b5dd52aSJames Smart 
33583b5dd52aSJames Smart /**
33597ad20aa9SJames Smart  * lpfc_bsg_issue_mbox_cmpl - lpfc_bsg_issue_mbox mbox completion handler
33603b5dd52aSJames Smart  * @phba: Pointer to HBA context object.
33613b5dd52aSJames Smart  * @pmboxq: Pointer to mailbox command.
33623b5dd52aSJames Smart  *
33633b5dd52aSJames Smart  * This is completion handler function for mailbox commands issued from
33643b5dd52aSJames Smart  * lpfc_bsg_issue_mbox function. This function is called by the
33653b5dd52aSJames Smart  * mailbox event handler function with no lock held. This function
3366d51cf5bdSJames Smart  * will wake up thread waiting on the wait queue pointed by dd_data
33673b5dd52aSJames Smart  * of the mailbox.
33683b5dd52aSJames Smart  **/
33699ab9b134SRashika Kheria static void
33707ad20aa9SJames Smart lpfc_bsg_issue_mbox_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
33713b5dd52aSJames Smart {
33723b5dd52aSJames Smart 	struct bsg_job_data *dd_data;
337301e0e15cSJohannes Thumshirn 	struct fc_bsg_reply *bsg_reply;
337475cc8cfcSJohannes Thumshirn 	struct bsg_job *job;
33753b5dd52aSJames Smart 	uint32_t size;
33763b5dd52aSJames Smart 	unsigned long flags;
33777ad20aa9SJames Smart 	uint8_t *pmb, *pmb_buf;
33783b5dd52aSJames Smart 
337985d77f91SJustin Tee 	dd_data = pmboxq->ctx_u.dd_data;
33803b5dd52aSJames Smart 
33817ad20aa9SJames Smart 	/*
33827ad20aa9SJames Smart 	 * The outgoing buffer is readily referred from the dma buffer,
33837ad20aa9SJames Smart 	 * just need to get header part from mailboxq structure.
33847a470277SJames Smart 	 */
33857ad20aa9SJames Smart 	pmb = (uint8_t *)&pmboxq->u.mb;
33867ad20aa9SJames Smart 	pmb_buf = (uint8_t *)dd_data->context_un.mbox.mb;
33877ad20aa9SJames Smart 	memcpy(pmb_buf, pmb, sizeof(MAILBOX_t));
3388515e0aa2SJames Smart 
3389a33c4f7bSJames Smart 	/* Determine if job has been aborted */
3390a33c4f7bSJames Smart 
3391a33c4f7bSJames Smart 	spin_lock_irqsave(&phba->ct_ev_lock, flags);
3392a33c4f7bSJames Smart 	job = dd_data->set_job;
3393a33c4f7bSJames Smart 	if (job) {
3394a33c4f7bSJames Smart 		/* Prevent timeout handling from trying to abort job  */
3395a33c4f7bSJames Smart 		job->dd_data = NULL;
3396a33c4f7bSJames Smart 	}
3397a33c4f7bSJames Smart 	spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
3398a33c4f7bSJames Smart 
3399a33c4f7bSJames Smart 	/* Copy the mailbox data to the job if it is still active */
3400a33c4f7bSJames Smart 
34015a6f133eSJames Smart 	if (job) {
340201e0e15cSJohannes Thumshirn 		bsg_reply = job->reply;
34037a470277SJames Smart 		size = job->reply_payload.payload_len;
340401e0e15cSJohannes Thumshirn 		bsg_reply->reply_payload_rcv_len =
34053b5dd52aSJames Smart 			sg_copy_from_buffer(job->reply_payload.sg_list,
34063b5dd52aSJames Smart 					    job->reply_payload.sg_cnt,
34077ad20aa9SJames Smart 					    pmb_buf, size);
3408b6e3b9c6SJames Smart 	}
34097a470277SJames Smart 
3410a33c4f7bSJames Smart 	dd_data->set_job = NULL;
34113b5dd52aSJames Smart 	mempool_free(dd_data->context_un.mbox.pmboxq, phba->mbox_mem_pool);
34127ad20aa9SJames Smart 	lpfc_bsg_dma_page_free(phba, dd_data->context_un.mbox.dmabuffers);
34133b5dd52aSJames Smart 	kfree(dd_data);
34147ad20aa9SJames Smart 
3415a33c4f7bSJames Smart 	/* Complete the job if the job is still active */
3416a33c4f7bSJames Smart 
34177ad20aa9SJames Smart 	if (job) {
341801e0e15cSJohannes Thumshirn 		bsg_reply->result = 0;
341906548160SJohannes Thumshirn 		bsg_job_done(job, bsg_reply->result,
34201abaede7SJohannes Thumshirn 			       bsg_reply->reply_payload_rcv_len);
34217ad20aa9SJames Smart 	}
34223b5dd52aSJames Smart 	return;
34233b5dd52aSJames Smart }
34243b5dd52aSJames Smart 
34253b5dd52aSJames Smart /**
34263b5dd52aSJames Smart  * lpfc_bsg_check_cmd_access - test for a supported mailbox command
34273b5dd52aSJames Smart  * @phba: Pointer to HBA context object.
34283b5dd52aSJames Smart  * @mb: Pointer to a mailbox object.
34293b5dd52aSJames Smart  * @vport: Pointer to a vport object.
34303b5dd52aSJames Smart  *
34313b5dd52aSJames Smart  * Some commands require the port to be offline, some may not be called from
34323b5dd52aSJames Smart  * the application.
34333b5dd52aSJames Smart  **/
34343b5dd52aSJames Smart static int lpfc_bsg_check_cmd_access(struct lpfc_hba *phba,
34353b5dd52aSJames Smart 	MAILBOX_t *mb, struct lpfc_vport *vport)
34363b5dd52aSJames Smart {
34373b5dd52aSJames Smart 	/* return negative error values for bsg job */
34383b5dd52aSJames Smart 	switch (mb->mbxCommand) {
34393b5dd52aSJames Smart 	/* Offline only */
34403b5dd52aSJames Smart 	case MBX_INIT_LINK:
34413b5dd52aSJames Smart 	case MBX_DOWN_LINK:
34423b5dd52aSJames Smart 	case MBX_CONFIG_LINK:
34433b5dd52aSJames Smart 	case MBX_CONFIG_RING:
34443b5dd52aSJames Smart 	case MBX_RESET_RING:
34453b5dd52aSJames Smart 	case MBX_UNREG_LOGIN:
34463b5dd52aSJames Smart 	case MBX_CLEAR_LA:
34473b5dd52aSJames Smart 	case MBX_DUMP_CONTEXT:
34483b5dd52aSJames Smart 	case MBX_RUN_DIAGS:
34493b5dd52aSJames Smart 	case MBX_RESTART:
34503b5dd52aSJames Smart 	case MBX_SET_MASK:
3451a645b8c1SJustin Tee 		if (!test_bit(FC_OFFLINE_MODE, &vport->fc_flag)) {
34523b5dd52aSJames Smart 			lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
34533b5dd52aSJames Smart 				"2743 Command 0x%x is illegal in on-line "
34543b5dd52aSJames Smart 				"state\n",
34553b5dd52aSJames Smart 				mb->mbxCommand);
34563b5dd52aSJames Smart 			return -EPERM;
34573b5dd52aSJames Smart 		}
3458e9a7c711SGustavo A. R. Silva 		break;
34593b5dd52aSJames Smart 	case MBX_WRITE_NV:
34603b5dd52aSJames Smart 	case MBX_WRITE_VPARMS:
34613b5dd52aSJames Smart 	case MBX_LOAD_SM:
34623b5dd52aSJames Smart 	case MBX_READ_NV:
34633b5dd52aSJames Smart 	case MBX_READ_CONFIG:
34643b5dd52aSJames Smart 	case MBX_READ_RCONFIG:
34653b5dd52aSJames Smart 	case MBX_READ_STATUS:
34663b5dd52aSJames Smart 	case MBX_READ_XRI:
34673b5dd52aSJames Smart 	case MBX_READ_REV:
34683b5dd52aSJames Smart 	case MBX_READ_LNK_STAT:
34693b5dd52aSJames Smart 	case MBX_DUMP_MEMORY:
34703b5dd52aSJames Smart 	case MBX_DOWN_LOAD:
34713b5dd52aSJames Smart 	case MBX_UPDATE_CFG:
34723b5dd52aSJames Smart 	case MBX_KILL_BOARD:
347306f35551SJames Smart 	case MBX_READ_TOPOLOGY:
34743b5dd52aSJames Smart 	case MBX_LOAD_AREA:
34753b5dd52aSJames Smart 	case MBX_LOAD_EXP_ROM:
34763b5dd52aSJames Smart 	case MBX_BEACON:
34773b5dd52aSJames Smart 	case MBX_DEL_LD_ENTRY:
34783b5dd52aSJames Smart 	case MBX_SET_DEBUG:
34793b5dd52aSJames Smart 	case MBX_WRITE_WWN:
34803b5dd52aSJames Smart 	case MBX_SLI4_CONFIG:
3481c7495937SJames Smart 	case MBX_READ_EVENT_LOG:
34823b5dd52aSJames Smart 	case MBX_READ_EVENT_LOG_STATUS:
34833b5dd52aSJames Smart 	case MBX_WRITE_EVENT_LOG:
34843b5dd52aSJames Smart 	case MBX_PORT_CAPABILITIES:
34853b5dd52aSJames Smart 	case MBX_PORT_IOV_CONTROL:
34867a470277SJames Smart 	case MBX_RUN_BIU_DIAG64:
34873b5dd52aSJames Smart 		break;
34883b5dd52aSJames Smart 	case MBX_SET_VARIABLE:
3489e2aed29fSJames Smart 		lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
3490e2aed29fSJames Smart 			"1226 mbox: set_variable 0x%x, 0x%x\n",
3491e2aed29fSJames Smart 			mb->un.varWords[0],
3492e2aed29fSJames Smart 			mb->un.varWords[1]);
3493e2aed29fSJames Smart 		break;
34943b5dd52aSJames Smart 	case MBX_READ_SPARM64:
34953b5dd52aSJames Smart 	case MBX_REG_LOGIN:
34963b5dd52aSJames Smart 	case MBX_REG_LOGIN64:
34973b5dd52aSJames Smart 	case MBX_CONFIG_PORT:
34983b5dd52aSJames Smart 	case MBX_RUN_BIU_DIAG:
34993b5dd52aSJames Smart 	default:
35003b5dd52aSJames Smart 		lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
35013b5dd52aSJames Smart 			"2742 Unknown Command 0x%x\n",
35023b5dd52aSJames Smart 			mb->mbxCommand);
35033b5dd52aSJames Smart 		return -EPERM;
35043b5dd52aSJames Smart 	}
35053b5dd52aSJames Smart 
35063b5dd52aSJames Smart 	return 0; /* ok */
35073b5dd52aSJames Smart }
35083b5dd52aSJames Smart 
35093b5dd52aSJames Smart /**
35103145d2d6SLee Jones  * lpfc_bsg_mbox_ext_session_reset - clean up context of multi-buffer mbox session
35117ad20aa9SJames Smart  * @phba: Pointer to HBA context object.
35127ad20aa9SJames Smart  *
35137ad20aa9SJames Smart  * This is routine clean up and reset BSG handling of multi-buffer mbox
35147ad20aa9SJames Smart  * command session.
35157ad20aa9SJames Smart  **/
35167ad20aa9SJames Smart static void
35177ad20aa9SJames Smart lpfc_bsg_mbox_ext_session_reset(struct lpfc_hba *phba)
35187ad20aa9SJames Smart {
35197ad20aa9SJames Smart 	if (phba->mbox_ext_buf_ctx.state == LPFC_BSG_MBOX_IDLE)
35207ad20aa9SJames Smart 		return;
35217ad20aa9SJames Smart 
35227ad20aa9SJames Smart 	/* free all memory, including dma buffers */
35237ad20aa9SJames Smart 	lpfc_bsg_dma_page_list_free(phba,
35247ad20aa9SJames Smart 				    &phba->mbox_ext_buf_ctx.ext_dmabuf_list);
35257ad20aa9SJames Smart 	lpfc_bsg_dma_page_free(phba, phba->mbox_ext_buf_ctx.mbx_dmabuf);
35267ad20aa9SJames Smart 	/* multi-buffer write mailbox command pass-through complete */
35277ad20aa9SJames Smart 	memset((char *)&phba->mbox_ext_buf_ctx, 0,
35287ad20aa9SJames Smart 	       sizeof(struct lpfc_mbox_ext_buf_ctx));
35297ad20aa9SJames Smart 	INIT_LIST_HEAD(&phba->mbox_ext_buf_ctx.ext_dmabuf_list);
35307ad20aa9SJames Smart 
35317ad20aa9SJames Smart 	return;
35327ad20aa9SJames Smart }
35337ad20aa9SJames Smart 
35347ad20aa9SJames Smart /**
35357ad20aa9SJames Smart  * lpfc_bsg_issue_mbox_ext_handle_job - job handler for multi-buffer mbox cmpl
35367ad20aa9SJames Smart  * @phba: Pointer to HBA context object.
35377ad20aa9SJames Smart  * @pmboxq: Pointer to mailbox command.
35387ad20aa9SJames Smart  *
35397ad20aa9SJames Smart  * This is routine handles BSG job for mailbox commands completions with
35407ad20aa9SJames Smart  * multiple external buffers.
35417ad20aa9SJames Smart  **/
354275cc8cfcSJohannes Thumshirn static struct bsg_job *
35437ad20aa9SJames Smart lpfc_bsg_issue_mbox_ext_handle_job(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
35447ad20aa9SJames Smart {
35457ad20aa9SJames Smart 	struct bsg_job_data *dd_data;
354675cc8cfcSJohannes Thumshirn 	struct bsg_job *job;
354701e0e15cSJohannes Thumshirn 	struct fc_bsg_reply *bsg_reply;
35487ad20aa9SJames Smart 	uint8_t *pmb, *pmb_buf;
35497ad20aa9SJames Smart 	unsigned long flags;
35507ad20aa9SJames Smart 	uint32_t size;
35517ad20aa9SJames Smart 	int rc = 0;
3552026abb87SJames Smart 	struct lpfc_dmabuf *dmabuf;
3553026abb87SJames Smart 	struct lpfc_sli_config_mbox *sli_cfg_mbx;
3554026abb87SJames Smart 	uint8_t *pmbx;
35557ad20aa9SJames Smart 
355685d77f91SJustin Tee 	dd_data = pmboxq->ctx_u.dd_data;
3557a33c4f7bSJames Smart 
3558a33c4f7bSJames Smart 	/* Determine if job has been aborted */
3559a33c4f7bSJames Smart 	spin_lock_irqsave(&phba->ct_ev_lock, flags);
3560a33c4f7bSJames Smart 	job = dd_data->set_job;
3561a33c4f7bSJames Smart 	if (job) {
356201e0e15cSJohannes Thumshirn 		bsg_reply = job->reply;
3563a33c4f7bSJames Smart 		/* Prevent timeout handling from trying to abort job  */
3564a33c4f7bSJames Smart 		job->dd_data = NULL;
35657ad20aa9SJames Smart 	}
3566a33c4f7bSJames Smart 	spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
35677ad20aa9SJames Smart 
35687ad20aa9SJames Smart 	/*
35697ad20aa9SJames Smart 	 * The outgoing buffer is readily referred from the dma buffer,
35707ad20aa9SJames Smart 	 * just need to get header part from mailboxq structure.
35717ad20aa9SJames Smart 	 */
3572a33c4f7bSJames Smart 
35737ad20aa9SJames Smart 	pmb = (uint8_t *)&pmboxq->u.mb;
35747ad20aa9SJames Smart 	pmb_buf = (uint8_t *)dd_data->context_un.mbox.mb;
3575026abb87SJames Smart 	/* Copy the byte swapped response mailbox back to the user */
35767ad20aa9SJames Smart 	memcpy(pmb_buf, pmb, sizeof(MAILBOX_t));
3577026abb87SJames Smart 	/* if there is any non-embedded extended data copy that too */
3578026abb87SJames Smart 	dmabuf = phba->mbox_ext_buf_ctx.mbx_dmabuf;
3579026abb87SJames Smart 	sli_cfg_mbx = (struct lpfc_sli_config_mbox *)dmabuf->virt;
3580026abb87SJames Smart 	if (!bsg_bf_get(lpfc_mbox_hdr_emb,
3581026abb87SJames Smart 	    &sli_cfg_mbx->un.sli_config_emb0_subsys.sli_config_hdr)) {
3582026abb87SJames Smart 		pmbx = (uint8_t *)dmabuf->virt;
3583026abb87SJames Smart 		/* byte swap the extended data following the mailbox command */
3584026abb87SJames Smart 		lpfc_sli_pcimem_bcopy(&pmbx[sizeof(MAILBOX_t)],
3585026abb87SJames Smart 			&pmbx[sizeof(MAILBOX_t)],
3586026abb87SJames Smart 			sli_cfg_mbx->un.sli_config_emb0_subsys.mse[0].buf_len);
3587026abb87SJames Smart 	}
35887ad20aa9SJames Smart 
3589a33c4f7bSJames Smart 	/* Complete the job if the job is still active */
3590a33c4f7bSJames Smart 
35917ad20aa9SJames Smart 	if (job) {
35927ad20aa9SJames Smart 		size = job->reply_payload.payload_len;
359301e0e15cSJohannes Thumshirn 		bsg_reply->reply_payload_rcv_len =
35947ad20aa9SJames Smart 			sg_copy_from_buffer(job->reply_payload.sg_list,
35957ad20aa9SJames Smart 					    job->reply_payload.sg_cnt,
35967ad20aa9SJames Smart 					    pmb_buf, size);
3597a33c4f7bSJames Smart 
35987ad20aa9SJames Smart 		/* result for successful */
359901e0e15cSJohannes Thumshirn 		bsg_reply->result = 0;
3600a33c4f7bSJames Smart 
36017ad20aa9SJames Smart 		lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
36027afc0ce9SColin Ian King 				"2937 SLI_CONFIG ext-buffer mailbox command "
36037ad20aa9SJames Smart 				"(x%x/x%x) complete bsg job done, bsize:%d\n",
36047ad20aa9SJames Smart 				phba->mbox_ext_buf_ctx.nembType,
36057ad20aa9SJames Smart 				phba->mbox_ext_buf_ctx.mboxType, size);
3606b76f2dc9SJames Smart 		lpfc_idiag_mbxacc_dump_bsg_mbox(phba,
3607b76f2dc9SJames Smart 					phba->mbox_ext_buf_ctx.nembType,
3608b76f2dc9SJames Smart 					phba->mbox_ext_buf_ctx.mboxType,
3609b76f2dc9SJames Smart 					dma_ebuf, sta_pos_addr,
3610b76f2dc9SJames Smart 					phba->mbox_ext_buf_ctx.mbx_dmabuf, 0);
3611a33c4f7bSJames Smart 	} else {
36127ad20aa9SJames Smart 		lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
36137afc0ce9SColin Ian King 				"2938 SLI_CONFIG ext-buffer mailbox "
36147ad20aa9SJames Smart 				"command (x%x/x%x) failure, rc:x%x\n",
36157ad20aa9SJames Smart 				phba->mbox_ext_buf_ctx.nembType,
36167ad20aa9SJames Smart 				phba->mbox_ext_buf_ctx.mboxType, rc);
3617a33c4f7bSJames Smart 	}
3618a33c4f7bSJames Smart 
3619a33c4f7bSJames Smart 
36207ad20aa9SJames Smart 	/* state change */
36217ad20aa9SJames Smart 	phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_DONE;
36227ad20aa9SJames Smart 	kfree(dd_data);
36237ad20aa9SJames Smart 	return job;
36247ad20aa9SJames Smart }
36257ad20aa9SJames Smart 
36267ad20aa9SJames Smart /**
36277ad20aa9SJames Smart  * lpfc_bsg_issue_read_mbox_ext_cmpl - compl handler for multi-buffer read mbox
36287ad20aa9SJames Smart  * @phba: Pointer to HBA context object.
36297ad20aa9SJames Smart  * @pmboxq: Pointer to mailbox command.
36307ad20aa9SJames Smart  *
36317ad20aa9SJames Smart  * This is completion handler function for mailbox read commands with multiple
36327ad20aa9SJames Smart  * external buffers.
36337ad20aa9SJames Smart  **/
36347ad20aa9SJames Smart static void
36357ad20aa9SJames Smart lpfc_bsg_issue_read_mbox_ext_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
36367ad20aa9SJames Smart {
363775cc8cfcSJohannes Thumshirn 	struct bsg_job *job;
36381abaede7SJohannes Thumshirn 	struct fc_bsg_reply *bsg_reply;
36397ad20aa9SJames Smart 
3640a33c4f7bSJames Smart 	job = lpfc_bsg_issue_mbox_ext_handle_job(phba, pmboxq);
3641a33c4f7bSJames Smart 
36427ad20aa9SJames Smart 	/* handle the BSG job with mailbox command */
3643a33c4f7bSJames Smart 	if (!job)
36447ad20aa9SJames Smart 		pmboxq->u.mb.mbxStatus = MBXERR_ERROR;
36457ad20aa9SJames Smart 
36467ad20aa9SJames Smart 	lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
36477afc0ce9SColin Ian King 			"2939 SLI_CONFIG ext-buffer rd mailbox command "
36487ad20aa9SJames Smart 			"complete, ctxState:x%x, mbxStatus:x%x\n",
36497ad20aa9SJames Smart 			phba->mbox_ext_buf_ctx.state, pmboxq->u.mb.mbxStatus);
36507ad20aa9SJames Smart 
36517ad20aa9SJames Smart 	if (pmboxq->u.mb.mbxStatus || phba->mbox_ext_buf_ctx.numBuf == 1)
36527ad20aa9SJames Smart 		lpfc_bsg_mbox_ext_session_reset(phba);
36537ad20aa9SJames Smart 
36547ad20aa9SJames Smart 	/* free base driver mailbox structure memory */
36557ad20aa9SJames Smart 	mempool_free(pmboxq, phba->mbox_mem_pool);
36567ad20aa9SJames Smart 
3657a33c4f7bSJames Smart 	/* if the job is still active, call job done */
36581abaede7SJohannes Thumshirn 	if (job) {
36591abaede7SJohannes Thumshirn 		bsg_reply = job->reply;
366006548160SJohannes Thumshirn 		bsg_job_done(job, bsg_reply->result,
36611abaede7SJohannes Thumshirn 			       bsg_reply->reply_payload_rcv_len);
36621abaede7SJohannes Thumshirn 	}
36637ad20aa9SJames Smart 	return;
36647ad20aa9SJames Smart }
36657ad20aa9SJames Smart 
36667ad20aa9SJames Smart /**
36677ad20aa9SJames Smart  * lpfc_bsg_issue_write_mbox_ext_cmpl - cmpl handler for multi-buffer write mbox
36687ad20aa9SJames Smart  * @phba: Pointer to HBA context object.
36697ad20aa9SJames Smart  * @pmboxq: Pointer to mailbox command.
36707ad20aa9SJames Smart  *
36717ad20aa9SJames Smart  * This is completion handler function for mailbox write commands with multiple
36727ad20aa9SJames Smart  * external buffers.
36737ad20aa9SJames Smart  **/
36747ad20aa9SJames Smart static void
36757ad20aa9SJames Smart lpfc_bsg_issue_write_mbox_ext_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
36767ad20aa9SJames Smart {
367775cc8cfcSJohannes Thumshirn 	struct bsg_job *job;
36781abaede7SJohannes Thumshirn 	struct fc_bsg_reply *bsg_reply;
36797ad20aa9SJames Smart 
3680a33c4f7bSJames Smart 	job = lpfc_bsg_issue_mbox_ext_handle_job(phba, pmboxq);
3681a33c4f7bSJames Smart 
36827ad20aa9SJames Smart 	/* handle the BSG job with the mailbox command */
3683a33c4f7bSJames Smart 	if (!job)
36847ad20aa9SJames Smart 		pmboxq->u.mb.mbxStatus = MBXERR_ERROR;
36857ad20aa9SJames Smart 
36867ad20aa9SJames Smart 	lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
36877afc0ce9SColin Ian King 			"2940 SLI_CONFIG ext-buffer wr mailbox command "
36887ad20aa9SJames Smart 			"complete, ctxState:x%x, mbxStatus:x%x\n",
36897ad20aa9SJames Smart 			phba->mbox_ext_buf_ctx.state, pmboxq->u.mb.mbxStatus);
36907ad20aa9SJames Smart 
36917ad20aa9SJames Smart 	/* free all memory, including dma buffers */
36927ad20aa9SJames Smart 	mempool_free(pmboxq, phba->mbox_mem_pool);
36937ad20aa9SJames Smart 	lpfc_bsg_mbox_ext_session_reset(phba);
36947ad20aa9SJames Smart 
3695a33c4f7bSJames Smart 	/* if the job is still active, call job done */
36961abaede7SJohannes Thumshirn 	if (job) {
36971abaede7SJohannes Thumshirn 		bsg_reply = job->reply;
369806548160SJohannes Thumshirn 		bsg_job_done(job, bsg_reply->result,
36991abaede7SJohannes Thumshirn 			       bsg_reply->reply_payload_rcv_len);
37001abaede7SJohannes Thumshirn 	}
37017ad20aa9SJames Smart 
37027ad20aa9SJames Smart 	return;
37037ad20aa9SJames Smart }
37047ad20aa9SJames Smart 
37057ad20aa9SJames Smart static void
37067ad20aa9SJames Smart lpfc_bsg_sli_cfg_dma_desc_setup(struct lpfc_hba *phba, enum nemb_type nemb_tp,
37077ad20aa9SJames Smart 				uint32_t index, struct lpfc_dmabuf *mbx_dmabuf,
37087ad20aa9SJames Smart 				struct lpfc_dmabuf *ext_dmabuf)
37097ad20aa9SJames Smart {
37107ad20aa9SJames Smart 	struct lpfc_sli_config_mbox *sli_cfg_mbx;
37117ad20aa9SJames Smart 
37127ad20aa9SJames Smart 	/* pointer to the start of mailbox command */
37137ad20aa9SJames Smart 	sli_cfg_mbx = (struct lpfc_sli_config_mbox *)mbx_dmabuf->virt;
37147ad20aa9SJames Smart 
37157ad20aa9SJames Smart 	if (nemb_tp == nemb_mse) {
37167ad20aa9SJames Smart 		if (index == 0) {
37177ad20aa9SJames Smart 			sli_cfg_mbx->un.sli_config_emb0_subsys.
37187ad20aa9SJames Smart 				mse[index].pa_hi =
37197ad20aa9SJames Smart 				putPaddrHigh(mbx_dmabuf->phys +
37207ad20aa9SJames Smart 					     sizeof(MAILBOX_t));
37217ad20aa9SJames Smart 			sli_cfg_mbx->un.sli_config_emb0_subsys.
37227ad20aa9SJames Smart 				mse[index].pa_lo =
37237ad20aa9SJames Smart 				putPaddrLow(mbx_dmabuf->phys +
37247ad20aa9SJames Smart 					    sizeof(MAILBOX_t));
37257ad20aa9SJames Smart 			lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
37267ad20aa9SJames Smart 					"2943 SLI_CONFIG(mse)[%d], "
37277ad20aa9SJames Smart 					"bufLen:%d, addrHi:x%x, addrLo:x%x\n",
37287ad20aa9SJames Smart 					index,
37297ad20aa9SJames Smart 					sli_cfg_mbx->un.sli_config_emb0_subsys.
37307ad20aa9SJames Smart 					mse[index].buf_len,
37317ad20aa9SJames Smart 					sli_cfg_mbx->un.sli_config_emb0_subsys.
37327ad20aa9SJames Smart 					mse[index].pa_hi,
37337ad20aa9SJames Smart 					sli_cfg_mbx->un.sli_config_emb0_subsys.
37347ad20aa9SJames Smart 					mse[index].pa_lo);
37357ad20aa9SJames Smart 		} else {
37367ad20aa9SJames Smart 			sli_cfg_mbx->un.sli_config_emb0_subsys.
37377ad20aa9SJames Smart 				mse[index].pa_hi =
37387ad20aa9SJames Smart 				putPaddrHigh(ext_dmabuf->phys);
37397ad20aa9SJames Smart 			sli_cfg_mbx->un.sli_config_emb0_subsys.
37407ad20aa9SJames Smart 				mse[index].pa_lo =
37417ad20aa9SJames Smart 				putPaddrLow(ext_dmabuf->phys);
37427ad20aa9SJames Smart 			lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
37437ad20aa9SJames Smart 					"2944 SLI_CONFIG(mse)[%d], "
37447ad20aa9SJames Smart 					"bufLen:%d, addrHi:x%x, addrLo:x%x\n",
37457ad20aa9SJames Smart 					index,
37467ad20aa9SJames Smart 					sli_cfg_mbx->un.sli_config_emb0_subsys.
37477ad20aa9SJames Smart 					mse[index].buf_len,
37487ad20aa9SJames Smart 					sli_cfg_mbx->un.sli_config_emb0_subsys.
37497ad20aa9SJames Smart 					mse[index].pa_hi,
37507ad20aa9SJames Smart 					sli_cfg_mbx->un.sli_config_emb0_subsys.
37517ad20aa9SJames Smart 					mse[index].pa_lo);
37527ad20aa9SJames Smart 		}
37537ad20aa9SJames Smart 	} else {
37547ad20aa9SJames Smart 		if (index == 0) {
37557ad20aa9SJames Smart 			sli_cfg_mbx->un.sli_config_emb1_subsys.
37567ad20aa9SJames Smart 				hbd[index].pa_hi =
37577ad20aa9SJames Smart 				putPaddrHigh(mbx_dmabuf->phys +
37587ad20aa9SJames Smart 					     sizeof(MAILBOX_t));
37597ad20aa9SJames Smart 			sli_cfg_mbx->un.sli_config_emb1_subsys.
37607ad20aa9SJames Smart 				hbd[index].pa_lo =
37617ad20aa9SJames Smart 				putPaddrLow(mbx_dmabuf->phys +
37627ad20aa9SJames Smart 					    sizeof(MAILBOX_t));
37637ad20aa9SJames Smart 			lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
37647ad20aa9SJames Smart 					"3007 SLI_CONFIG(hbd)[%d], "
37657ad20aa9SJames Smart 					"bufLen:%d, addrHi:x%x, addrLo:x%x\n",
37667ad20aa9SJames Smart 				index,
37677ad20aa9SJames Smart 				bsg_bf_get(lpfc_mbox_sli_config_ecmn_hbd_len,
37687ad20aa9SJames Smart 				&sli_cfg_mbx->un.
37697ad20aa9SJames Smart 				sli_config_emb1_subsys.hbd[index]),
37707ad20aa9SJames Smart 				sli_cfg_mbx->un.sli_config_emb1_subsys.
37717ad20aa9SJames Smart 				hbd[index].pa_hi,
37727ad20aa9SJames Smart 				sli_cfg_mbx->un.sli_config_emb1_subsys.
37737ad20aa9SJames Smart 				hbd[index].pa_lo);
37747ad20aa9SJames Smart 
37757ad20aa9SJames Smart 		} else {
37767ad20aa9SJames Smart 			sli_cfg_mbx->un.sli_config_emb1_subsys.
37777ad20aa9SJames Smart 				hbd[index].pa_hi =
37787ad20aa9SJames Smart 				putPaddrHigh(ext_dmabuf->phys);
37797ad20aa9SJames Smart 			sli_cfg_mbx->un.sli_config_emb1_subsys.
37807ad20aa9SJames Smart 				hbd[index].pa_lo =
37817ad20aa9SJames Smart 				putPaddrLow(ext_dmabuf->phys);
37827ad20aa9SJames Smart 			lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
37837ad20aa9SJames Smart 					"3008 SLI_CONFIG(hbd)[%d], "
37847ad20aa9SJames Smart 					"bufLen:%d, addrHi:x%x, addrLo:x%x\n",
37857ad20aa9SJames Smart 				index,
37867ad20aa9SJames Smart 				bsg_bf_get(lpfc_mbox_sli_config_ecmn_hbd_len,
37877ad20aa9SJames Smart 				&sli_cfg_mbx->un.
37887ad20aa9SJames Smart 				sli_config_emb1_subsys.hbd[index]),
37897ad20aa9SJames Smart 				sli_cfg_mbx->un.sli_config_emb1_subsys.
37907ad20aa9SJames Smart 				hbd[index].pa_hi,
37917ad20aa9SJames Smart 				sli_cfg_mbx->un.sli_config_emb1_subsys.
37927ad20aa9SJames Smart 				hbd[index].pa_lo);
37937ad20aa9SJames Smart 		}
37947ad20aa9SJames Smart 	}
37957ad20aa9SJames Smart 	return;
37967ad20aa9SJames Smart }
37977ad20aa9SJames Smart 
37987ad20aa9SJames Smart /**
37993145d2d6SLee Jones  * lpfc_bsg_sli_cfg_read_cmd_ext - sli_config non-embedded mailbox cmd read
38007ad20aa9SJames Smart  * @phba: Pointer to HBA context object.
3801ea085dabSLee Jones  * @job: Pointer to the job object.
38027ad20aa9SJames Smart  * @nemb_tp: Enumerate of non-embedded mailbox command type.
3803ea085dabSLee Jones  * @dmabuf: Pointer to a DMA buffer descriptor.
38047ad20aa9SJames Smart  *
38057ad20aa9SJames Smart  * This routine performs SLI_CONFIG (0x9B) read mailbox command operation with
38063bfab8a0SJames Smart  * non-embedded external buffers.
38077ad20aa9SJames Smart  **/
38087ad20aa9SJames Smart static int
380975cc8cfcSJohannes Thumshirn lpfc_bsg_sli_cfg_read_cmd_ext(struct lpfc_hba *phba, struct bsg_job *job,
38107ad20aa9SJames Smart 			      enum nemb_type nemb_tp,
38117ad20aa9SJames Smart 			      struct lpfc_dmabuf *dmabuf)
38127ad20aa9SJames Smart {
381301e0e15cSJohannes Thumshirn 	struct fc_bsg_request *bsg_request = job->request;
38147ad20aa9SJames Smart 	struct lpfc_sli_config_mbox *sli_cfg_mbx;
38157ad20aa9SJames Smart 	struct dfc_mbox_req *mbox_req;
38167ad20aa9SJames Smart 	struct lpfc_dmabuf *curr_dmabuf, *next_dmabuf;
38177ad20aa9SJames Smart 	uint32_t ext_buf_cnt, ext_buf_index;
38187ad20aa9SJames Smart 	struct lpfc_dmabuf *ext_dmabuf = NULL;
38197ad20aa9SJames Smart 	struct bsg_job_data *dd_data = NULL;
38207ad20aa9SJames Smart 	LPFC_MBOXQ_t *pmboxq = NULL;
38217ad20aa9SJames Smart 	MAILBOX_t *pmb;
38227ad20aa9SJames Smart 	uint8_t *pmbx;
38237ad20aa9SJames Smart 	int rc, i;
38247ad20aa9SJames Smart 
38257ad20aa9SJames Smart 	mbox_req =
382601e0e15cSJohannes Thumshirn 	   (struct dfc_mbox_req *)bsg_request->rqst_data.h_vendor.vendor_cmd;
38277ad20aa9SJames Smart 
38287ad20aa9SJames Smart 	/* pointer to the start of mailbox command */
38297ad20aa9SJames Smart 	sli_cfg_mbx = (struct lpfc_sli_config_mbox *)dmabuf->virt;
38307ad20aa9SJames Smart 
38317ad20aa9SJames Smart 	if (nemb_tp == nemb_mse) {
38327ad20aa9SJames Smart 		ext_buf_cnt = bsg_bf_get(lpfc_mbox_hdr_mse_cnt,
38337ad20aa9SJames Smart 			&sli_cfg_mbx->un.sli_config_emb0_subsys.sli_config_hdr);
38347ad20aa9SJames Smart 		if (ext_buf_cnt > LPFC_MBX_SLI_CONFIG_MAX_MSE) {
38357ad20aa9SJames Smart 			lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
38367ad20aa9SJames Smart 					"2945 Handled SLI_CONFIG(mse) rd, "
38377ad20aa9SJames Smart 					"ext_buf_cnt(%d) out of range(%d)\n",
38387ad20aa9SJames Smart 					ext_buf_cnt,
38397ad20aa9SJames Smart 					LPFC_MBX_SLI_CONFIG_MAX_MSE);
38407ad20aa9SJames Smart 			rc = -ERANGE;
38417ad20aa9SJames Smart 			goto job_error;
38427ad20aa9SJames Smart 		}
38437ad20aa9SJames Smart 		lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
38447ad20aa9SJames Smart 				"2941 Handled SLI_CONFIG(mse) rd, "
38457ad20aa9SJames Smart 				"ext_buf_cnt:%d\n", ext_buf_cnt);
38467ad20aa9SJames Smart 	} else {
38477ad20aa9SJames Smart 		/* sanity check on interface type for support */
384827d6ac0aSJames Smart 		if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) <
38497ad20aa9SJames Smart 		    LPFC_SLI_INTF_IF_TYPE_2) {
38507ad20aa9SJames Smart 			rc = -ENODEV;
38517ad20aa9SJames Smart 			goto job_error;
38527ad20aa9SJames Smart 		}
38537ad20aa9SJames Smart 		/* nemb_tp == nemb_hbd */
38547ad20aa9SJames Smart 		ext_buf_cnt = sli_cfg_mbx->un.sli_config_emb1_subsys.hbd_count;
38557ad20aa9SJames Smart 		if (ext_buf_cnt > LPFC_MBX_SLI_CONFIG_MAX_HBD) {
38567ad20aa9SJames Smart 			lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
38577ad20aa9SJames Smart 					"2946 Handled SLI_CONFIG(hbd) rd, "
38587ad20aa9SJames Smart 					"ext_buf_cnt(%d) out of range(%d)\n",
38597ad20aa9SJames Smart 					ext_buf_cnt,
38607ad20aa9SJames Smart 					LPFC_MBX_SLI_CONFIG_MAX_HBD);
38617ad20aa9SJames Smart 			rc = -ERANGE;
38627ad20aa9SJames Smart 			goto job_error;
38637ad20aa9SJames Smart 		}
38647ad20aa9SJames Smart 		lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
38657ad20aa9SJames Smart 				"2942 Handled SLI_CONFIG(hbd) rd, "
38667ad20aa9SJames Smart 				"ext_buf_cnt:%d\n", ext_buf_cnt);
38677ad20aa9SJames Smart 	}
38687ad20aa9SJames Smart 
3869b76f2dc9SJames Smart 	/* before dma descriptor setup */
3870b76f2dc9SJames Smart 	lpfc_idiag_mbxacc_dump_bsg_mbox(phba, nemb_tp, mbox_rd, dma_mbox,
3871b76f2dc9SJames Smart 					sta_pre_addr, dmabuf, ext_buf_cnt);
3872b76f2dc9SJames Smart 
38737ad20aa9SJames Smart 	/* reject non-embedded mailbox command with none external buffer */
38747ad20aa9SJames Smart 	if (ext_buf_cnt == 0) {
38757ad20aa9SJames Smart 		rc = -EPERM;
38767ad20aa9SJames Smart 		goto job_error;
38777ad20aa9SJames Smart 	} else if (ext_buf_cnt > 1) {
38787ad20aa9SJames Smart 		/* additional external read buffers */
38797ad20aa9SJames Smart 		for (i = 1; i < ext_buf_cnt; i++) {
38807ad20aa9SJames Smart 			ext_dmabuf = lpfc_bsg_dma_page_alloc(phba);
38817ad20aa9SJames Smart 			if (!ext_dmabuf) {
38827ad20aa9SJames Smart 				rc = -ENOMEM;
38837ad20aa9SJames Smart 				goto job_error;
38847ad20aa9SJames Smart 			}
38857ad20aa9SJames Smart 			list_add_tail(&ext_dmabuf->list,
38867ad20aa9SJames Smart 				      &phba->mbox_ext_buf_ctx.ext_dmabuf_list);
38877ad20aa9SJames Smart 		}
38887ad20aa9SJames Smart 	}
38897ad20aa9SJames Smart 
38907ad20aa9SJames Smart 	/* bsg tracking structure */
38917ad20aa9SJames Smart 	dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
38927ad20aa9SJames Smart 	if (!dd_data) {
38937ad20aa9SJames Smart 		rc = -ENOMEM;
38947ad20aa9SJames Smart 		goto job_error;
38957ad20aa9SJames Smart 	}
38967ad20aa9SJames Smart 
38977ad20aa9SJames Smart 	/* mailbox command structure for base driver */
38987ad20aa9SJames Smart 	pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
38997ad20aa9SJames Smart 	if (!pmboxq) {
39007ad20aa9SJames Smart 		rc = -ENOMEM;
39017ad20aa9SJames Smart 		goto job_error;
39027ad20aa9SJames Smart 	}
39037ad20aa9SJames Smart 	memset(pmboxq, 0, sizeof(LPFC_MBOXQ_t));
39047ad20aa9SJames Smart 
39057ad20aa9SJames Smart 	/* for the first external buffer */
39067ad20aa9SJames Smart 	lpfc_bsg_sli_cfg_dma_desc_setup(phba, nemb_tp, 0, dmabuf, dmabuf);
39077ad20aa9SJames Smart 
39087ad20aa9SJames Smart 	/* for the rest of external buffer descriptors if any */
39097ad20aa9SJames Smart 	if (ext_buf_cnt > 1) {
39107ad20aa9SJames Smart 		ext_buf_index = 1;
39117ad20aa9SJames Smart 		list_for_each_entry_safe(curr_dmabuf, next_dmabuf,
39127ad20aa9SJames Smart 				&phba->mbox_ext_buf_ctx.ext_dmabuf_list, list) {
39137ad20aa9SJames Smart 			lpfc_bsg_sli_cfg_dma_desc_setup(phba, nemb_tp,
39147ad20aa9SJames Smart 						ext_buf_index, dmabuf,
39157ad20aa9SJames Smart 						curr_dmabuf);
39167ad20aa9SJames Smart 			ext_buf_index++;
39177ad20aa9SJames Smart 		}
39187ad20aa9SJames Smart 	}
39197ad20aa9SJames Smart 
3920b76f2dc9SJames Smart 	/* after dma descriptor setup */
3921b76f2dc9SJames Smart 	lpfc_idiag_mbxacc_dump_bsg_mbox(phba, nemb_tp, mbox_rd, dma_mbox,
3922b76f2dc9SJames Smart 					sta_pos_addr, dmabuf, ext_buf_cnt);
3923b76f2dc9SJames Smart 
39247ad20aa9SJames Smart 	/* construct base driver mbox command */
39257ad20aa9SJames Smart 	pmb = &pmboxq->u.mb;
39267ad20aa9SJames Smart 	pmbx = (uint8_t *)dmabuf->virt;
39277ad20aa9SJames Smart 	memcpy(pmb, pmbx, sizeof(*pmb));
39287ad20aa9SJames Smart 	pmb->mbxOwner = OWN_HOST;
39297ad20aa9SJames Smart 	pmboxq->vport = phba->pport;
39307ad20aa9SJames Smart 
39317ad20aa9SJames Smart 	/* multi-buffer handling context */
39327ad20aa9SJames Smart 	phba->mbox_ext_buf_ctx.nembType = nemb_tp;
39337ad20aa9SJames Smart 	phba->mbox_ext_buf_ctx.mboxType = mbox_rd;
39347ad20aa9SJames Smart 	phba->mbox_ext_buf_ctx.numBuf = ext_buf_cnt;
39357ad20aa9SJames Smart 	phba->mbox_ext_buf_ctx.mbxTag = mbox_req->extMboxTag;
39367ad20aa9SJames Smart 	phba->mbox_ext_buf_ctx.seqNum = mbox_req->extSeqNum;
39377ad20aa9SJames Smart 	phba->mbox_ext_buf_ctx.mbx_dmabuf = dmabuf;
39387ad20aa9SJames Smart 
39397ad20aa9SJames Smart 	/* callback for multi-buffer read mailbox command */
39407ad20aa9SJames Smart 	pmboxq->mbox_cmpl = lpfc_bsg_issue_read_mbox_ext_cmpl;
39417ad20aa9SJames Smart 
39427ad20aa9SJames Smart 	/* context fields to callback function */
394385d77f91SJustin Tee 	pmboxq->ctx_u.dd_data = dd_data;
39447ad20aa9SJames Smart 	dd_data->type = TYPE_MBOX;
3945a33c4f7bSJames Smart 	dd_data->set_job = job;
39467ad20aa9SJames Smart 	dd_data->context_un.mbox.pmboxq = pmboxq;
39477ad20aa9SJames Smart 	dd_data->context_un.mbox.mb = (MAILBOX_t *)pmbx;
39487ad20aa9SJames Smart 	job->dd_data = dd_data;
39497ad20aa9SJames Smart 
39507ad20aa9SJames Smart 	/* state change */
39517ad20aa9SJames Smart 	phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_PORT;
39527ad20aa9SJames Smart 
3953026abb87SJames Smart 	/*
3954026abb87SJames Smart 	 * Non-embedded mailbox subcommand data gets byte swapped here because
3955026abb87SJames Smart 	 * the lower level driver code only does the first 64 mailbox words.
3956026abb87SJames Smart 	 */
3957026abb87SJames Smart 	if ((!bsg_bf_get(lpfc_mbox_hdr_emb,
3958026abb87SJames Smart 	    &sli_cfg_mbx->un.sli_config_emb0_subsys.sli_config_hdr)) &&
3959026abb87SJames Smart 		(nemb_tp == nemb_mse))
3960026abb87SJames Smart 		lpfc_sli_pcimem_bcopy(&pmbx[sizeof(MAILBOX_t)],
3961026abb87SJames Smart 			&pmbx[sizeof(MAILBOX_t)],
3962026abb87SJames Smart 				sli_cfg_mbx->un.sli_config_emb0_subsys.
3963026abb87SJames Smart 					mse[0].buf_len);
3964026abb87SJames Smart 
39657ad20aa9SJames Smart 	rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT);
39667ad20aa9SJames Smart 	if ((rc == MBX_SUCCESS) || (rc == MBX_BUSY)) {
39677ad20aa9SJames Smart 		lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
39687ad20aa9SJames Smart 				"2947 Issued SLI_CONFIG ext-buffer "
39697afc0ce9SColin Ian King 				"mailbox command, rc:x%x\n", rc);
397088a2cfbbSJames Smart 		return SLI_CONFIG_HANDLED;
39717ad20aa9SJames Smart 	}
39727ad20aa9SJames Smart 	lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
39737ad20aa9SJames Smart 			"2948 Failed to issue SLI_CONFIG ext-buffer "
39747afc0ce9SColin Ian King 			"mailbox command, rc:x%x\n", rc);
39757ad20aa9SJames Smart 	rc = -EPIPE;
39767ad20aa9SJames Smart 
39777ad20aa9SJames Smart job_error:
39787ad20aa9SJames Smart 	if (pmboxq)
39797ad20aa9SJames Smart 		mempool_free(pmboxq, phba->mbox_mem_pool);
39807ad20aa9SJames Smart 	lpfc_bsg_dma_page_list_free(phba,
39817ad20aa9SJames Smart 				    &phba->mbox_ext_buf_ctx.ext_dmabuf_list);
39827ad20aa9SJames Smart 	kfree(dd_data);
39837ad20aa9SJames Smart 	phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_IDLE;
39847ad20aa9SJames Smart 	return rc;
39857ad20aa9SJames Smart }
39867ad20aa9SJames Smart 
39877ad20aa9SJames Smart /**
39887ad20aa9SJames Smart  * lpfc_bsg_sli_cfg_write_cmd_ext - sli_config non-embedded mailbox cmd write
39897ad20aa9SJames Smart  * @phba: Pointer to HBA context object.
3990ea085dabSLee Jones  * @job: Pointer to the job object.
3991ea085dabSLee Jones  * @nemb_tp: Enumerate of non-embedded mailbox command type.
3992ea085dabSLee Jones  * @dmabuf: Pointer to a DMA buffer descriptor.
39937ad20aa9SJames Smart  *
39947ad20aa9SJames Smart  * This routine performs SLI_CONFIG (0x9B) write mailbox command operation with
39953bfab8a0SJames Smart  * non-embedded external buffers.
39967ad20aa9SJames Smart  **/
39977ad20aa9SJames Smart static int
399875cc8cfcSJohannes Thumshirn lpfc_bsg_sli_cfg_write_cmd_ext(struct lpfc_hba *phba, struct bsg_job *job,
39997ad20aa9SJames Smart 			       enum nemb_type nemb_tp,
40007ad20aa9SJames Smart 			       struct lpfc_dmabuf *dmabuf)
40017ad20aa9SJames Smart {
400201e0e15cSJohannes Thumshirn 	struct fc_bsg_request *bsg_request = job->request;
400301e0e15cSJohannes Thumshirn 	struct fc_bsg_reply *bsg_reply = job->reply;
40047ad20aa9SJames Smart 	struct dfc_mbox_req *mbox_req;
40057ad20aa9SJames Smart 	struct lpfc_sli_config_mbox *sli_cfg_mbx;
40067ad20aa9SJames Smart 	uint32_t ext_buf_cnt;
40077ad20aa9SJames Smart 	struct bsg_job_data *dd_data = NULL;
40087ad20aa9SJames Smart 	LPFC_MBOXQ_t *pmboxq = NULL;
40097ad20aa9SJames Smart 	MAILBOX_t *pmb;
40107ad20aa9SJames Smart 	uint8_t *mbx;
401188a2cfbbSJames Smart 	int rc = SLI_CONFIG_NOT_HANDLED, i;
40127ad20aa9SJames Smart 
40137ad20aa9SJames Smart 	mbox_req =
401401e0e15cSJohannes Thumshirn 	   (struct dfc_mbox_req *)bsg_request->rqst_data.h_vendor.vendor_cmd;
40157ad20aa9SJames Smart 
40167ad20aa9SJames Smart 	/* pointer to the start of mailbox command */
40177ad20aa9SJames Smart 	sli_cfg_mbx = (struct lpfc_sli_config_mbox *)dmabuf->virt;
40187ad20aa9SJames Smart 
40197ad20aa9SJames Smart 	if (nemb_tp == nemb_mse) {
40207ad20aa9SJames Smart 		ext_buf_cnt = bsg_bf_get(lpfc_mbox_hdr_mse_cnt,
40217ad20aa9SJames Smart 			&sli_cfg_mbx->un.sli_config_emb0_subsys.sli_config_hdr);
40227ad20aa9SJames Smart 		if (ext_buf_cnt > LPFC_MBX_SLI_CONFIG_MAX_MSE) {
40237ad20aa9SJames Smart 			lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
4024026abb87SJames Smart 					"2953 Failed SLI_CONFIG(mse) wr, "
40257ad20aa9SJames Smart 					"ext_buf_cnt(%d) out of range(%d)\n",
40267ad20aa9SJames Smart 					ext_buf_cnt,
40277ad20aa9SJames Smart 					LPFC_MBX_SLI_CONFIG_MAX_MSE);
40287ad20aa9SJames Smart 			return -ERANGE;
40297ad20aa9SJames Smart 		}
40307ad20aa9SJames Smart 		lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
40317ad20aa9SJames Smart 				"2949 Handled SLI_CONFIG(mse) wr, "
40327ad20aa9SJames Smart 				"ext_buf_cnt:%d\n", ext_buf_cnt);
40337ad20aa9SJames Smart 	} else {
40347ad20aa9SJames Smart 		/* sanity check on interface type for support */
403527d6ac0aSJames Smart 		if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) <
40367ad20aa9SJames Smart 		    LPFC_SLI_INTF_IF_TYPE_2)
40377ad20aa9SJames Smart 			return -ENODEV;
40387ad20aa9SJames Smart 		/* nemb_tp == nemb_hbd */
40397ad20aa9SJames Smart 		ext_buf_cnt = sli_cfg_mbx->un.sli_config_emb1_subsys.hbd_count;
40407ad20aa9SJames Smart 		if (ext_buf_cnt > LPFC_MBX_SLI_CONFIG_MAX_HBD) {
40417ad20aa9SJames Smart 			lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
4042026abb87SJames Smart 					"2954 Failed SLI_CONFIG(hbd) wr, "
40437ad20aa9SJames Smart 					"ext_buf_cnt(%d) out of range(%d)\n",
40447ad20aa9SJames Smart 					ext_buf_cnt,
40457ad20aa9SJames Smart 					LPFC_MBX_SLI_CONFIG_MAX_HBD);
40467ad20aa9SJames Smart 			return -ERANGE;
40477ad20aa9SJames Smart 		}
40487ad20aa9SJames Smart 		lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
40497ad20aa9SJames Smart 				"2950 Handled SLI_CONFIG(hbd) wr, "
40507ad20aa9SJames Smart 				"ext_buf_cnt:%d\n", ext_buf_cnt);
40517ad20aa9SJames Smart 	}
40527ad20aa9SJames Smart 
4053b76f2dc9SJames Smart 	/* before dma buffer descriptor setup */
4054b76f2dc9SJames Smart 	lpfc_idiag_mbxacc_dump_bsg_mbox(phba, nemb_tp, mbox_wr, dma_mbox,
4055b76f2dc9SJames Smart 					sta_pre_addr, dmabuf, ext_buf_cnt);
4056b76f2dc9SJames Smart 
40577ad20aa9SJames Smart 	if (ext_buf_cnt == 0)
40587ad20aa9SJames Smart 		return -EPERM;
40597ad20aa9SJames Smart 
40607ad20aa9SJames Smart 	/* for the first external buffer */
40617ad20aa9SJames Smart 	lpfc_bsg_sli_cfg_dma_desc_setup(phba, nemb_tp, 0, dmabuf, dmabuf);
40627ad20aa9SJames Smart 
4063b76f2dc9SJames Smart 	/* after dma descriptor setup */
4064b76f2dc9SJames Smart 	lpfc_idiag_mbxacc_dump_bsg_mbox(phba, nemb_tp, mbox_wr, dma_mbox,
4065b76f2dc9SJames Smart 					sta_pos_addr, dmabuf, ext_buf_cnt);
4066b76f2dc9SJames Smart 
40677ad20aa9SJames Smart 	/* log for looking forward */
40687ad20aa9SJames Smart 	for (i = 1; i < ext_buf_cnt; i++) {
40697ad20aa9SJames Smart 		if (nemb_tp == nemb_mse)
40707ad20aa9SJames Smart 			lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
40717ad20aa9SJames Smart 				"2951 SLI_CONFIG(mse), buf[%d]-length:%d\n",
40727ad20aa9SJames Smart 				i, sli_cfg_mbx->un.sli_config_emb0_subsys.
40737ad20aa9SJames Smart 				mse[i].buf_len);
40747ad20aa9SJames Smart 		else
40757ad20aa9SJames Smart 			lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
40767ad20aa9SJames Smart 				"2952 SLI_CONFIG(hbd), buf[%d]-length:%d\n",
40777ad20aa9SJames Smart 				i, bsg_bf_get(lpfc_mbox_sli_config_ecmn_hbd_len,
40787ad20aa9SJames Smart 				&sli_cfg_mbx->un.sli_config_emb1_subsys.
40797ad20aa9SJames Smart 				hbd[i]));
40807ad20aa9SJames Smart 	}
40817ad20aa9SJames Smart 
40827ad20aa9SJames Smart 	/* multi-buffer handling context */
40837ad20aa9SJames Smart 	phba->mbox_ext_buf_ctx.nembType = nemb_tp;
40847ad20aa9SJames Smart 	phba->mbox_ext_buf_ctx.mboxType = mbox_wr;
40857ad20aa9SJames Smart 	phba->mbox_ext_buf_ctx.numBuf = ext_buf_cnt;
40867ad20aa9SJames Smart 	phba->mbox_ext_buf_ctx.mbxTag = mbox_req->extMboxTag;
40877ad20aa9SJames Smart 	phba->mbox_ext_buf_ctx.seqNum = mbox_req->extSeqNum;
40887ad20aa9SJames Smart 	phba->mbox_ext_buf_ctx.mbx_dmabuf = dmabuf;
40897ad20aa9SJames Smart 
40907ad20aa9SJames Smart 	if (ext_buf_cnt == 1) {
40917ad20aa9SJames Smart 		/* bsg tracking structure */
40927ad20aa9SJames Smart 		dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
40937ad20aa9SJames Smart 		if (!dd_data) {
40947ad20aa9SJames Smart 			rc = -ENOMEM;
40957ad20aa9SJames Smart 			goto job_error;
40967ad20aa9SJames Smart 		}
40977ad20aa9SJames Smart 
40987ad20aa9SJames Smart 		/* mailbox command structure for base driver */
40997ad20aa9SJames Smart 		pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
41007ad20aa9SJames Smart 		if (!pmboxq) {
41017ad20aa9SJames Smart 			rc = -ENOMEM;
41027ad20aa9SJames Smart 			goto job_error;
41037ad20aa9SJames Smart 		}
41047ad20aa9SJames Smart 		memset(pmboxq, 0, sizeof(LPFC_MBOXQ_t));
41057ad20aa9SJames Smart 		pmb = &pmboxq->u.mb;
41067ad20aa9SJames Smart 		mbx = (uint8_t *)dmabuf->virt;
41077ad20aa9SJames Smart 		memcpy(pmb, mbx, sizeof(*pmb));
41087ad20aa9SJames Smart 		pmb->mbxOwner = OWN_HOST;
41097ad20aa9SJames Smart 		pmboxq->vport = phba->pport;
41107ad20aa9SJames Smart 
41117ad20aa9SJames Smart 		/* callback for multi-buffer read mailbox command */
41127ad20aa9SJames Smart 		pmboxq->mbox_cmpl = lpfc_bsg_issue_write_mbox_ext_cmpl;
41137ad20aa9SJames Smart 
41147ad20aa9SJames Smart 		/* context fields to callback function */
411585d77f91SJustin Tee 		pmboxq->ctx_u.dd_data = dd_data;
41167ad20aa9SJames Smart 		dd_data->type = TYPE_MBOX;
4117a33c4f7bSJames Smart 		dd_data->set_job = job;
41187ad20aa9SJames Smart 		dd_data->context_un.mbox.pmboxq = pmboxq;
41197ad20aa9SJames Smart 		dd_data->context_un.mbox.mb = (MAILBOX_t *)mbx;
41207ad20aa9SJames Smart 		job->dd_data = dd_data;
41217ad20aa9SJames Smart 
41227ad20aa9SJames Smart 		/* state change */
41237ad20aa9SJames Smart 
4124a33c4f7bSJames Smart 		phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_PORT;
41257ad20aa9SJames Smart 		rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT);
41267ad20aa9SJames Smart 		if ((rc == MBX_SUCCESS) || (rc == MBX_BUSY)) {
41277ad20aa9SJames Smart 			lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
41287ad20aa9SJames Smart 					"2955 Issued SLI_CONFIG ext-buffer "
41297afc0ce9SColin Ian King 					"mailbox command, rc:x%x\n", rc);
413088a2cfbbSJames Smart 			return SLI_CONFIG_HANDLED;
41317ad20aa9SJames Smart 		}
41327ad20aa9SJames Smart 		lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
41337ad20aa9SJames Smart 				"2956 Failed to issue SLI_CONFIG ext-buffer "
41347afc0ce9SColin Ian King 				"mailbox command, rc:x%x\n", rc);
41357ad20aa9SJames Smart 		rc = -EPIPE;
4136026abb87SJames Smart 		goto job_error;
41377ad20aa9SJames Smart 	}
41387ad20aa9SJames Smart 
41393bfab8a0SJames Smart 	/* wait for additional external buffers */
4140a33c4f7bSJames Smart 
414101e0e15cSJohannes Thumshirn 	bsg_reply->result = 0;
414206548160SJohannes Thumshirn 	bsg_job_done(job, bsg_reply->result,
41431abaede7SJohannes Thumshirn 		       bsg_reply->reply_payload_rcv_len);
414488a2cfbbSJames Smart 	return SLI_CONFIG_HANDLED;
414588a2cfbbSJames Smart 
41467ad20aa9SJames Smart job_error:
41477ad20aa9SJames Smart 	if (pmboxq)
41487ad20aa9SJames Smart 		mempool_free(pmboxq, phba->mbox_mem_pool);
41497ad20aa9SJames Smart 	kfree(dd_data);
41507ad20aa9SJames Smart 
41517ad20aa9SJames Smart 	return rc;
41527ad20aa9SJames Smart }
41537ad20aa9SJames Smart 
41547ad20aa9SJames Smart /**
41557ad20aa9SJames Smart  * lpfc_bsg_handle_sli_cfg_mbox - handle sli-cfg mailbox cmd with ext buffer
41567ad20aa9SJames Smart  * @phba: Pointer to HBA context object.
4157ea085dabSLee Jones  * @job: Pointer to the job object.
4158ea085dabSLee Jones  * @dmabuf: Pointer to a DMA buffer descriptor.
41597ad20aa9SJames Smart  *
41607ad20aa9SJames Smart  * This routine handles SLI_CONFIG (0x9B) mailbox command with non-embedded
41613bfab8a0SJames Smart  * external buffers, including both 0x9B with non-embedded MSEs and 0x9B
41623bfab8a0SJames Smart  * with embedded subsystem 0x1 and opcodes with external HBDs.
41637ad20aa9SJames Smart  **/
41647ad20aa9SJames Smart static int
416575cc8cfcSJohannes Thumshirn lpfc_bsg_handle_sli_cfg_mbox(struct lpfc_hba *phba, struct bsg_job *job,
41667ad20aa9SJames Smart 			     struct lpfc_dmabuf *dmabuf)
41677ad20aa9SJames Smart {
41687ad20aa9SJames Smart 	struct lpfc_sli_config_mbox *sli_cfg_mbx;
41697ad20aa9SJames Smart 	uint32_t subsys;
41707ad20aa9SJames Smart 	uint32_t opcode;
41717ad20aa9SJames Smart 	int rc = SLI_CONFIG_NOT_HANDLED;
41727ad20aa9SJames Smart 
4173026abb87SJames Smart 	/* state change on new multi-buffer pass-through mailbox command */
41747ad20aa9SJames Smart 	phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_HOST;
41757ad20aa9SJames Smart 
41767ad20aa9SJames Smart 	sli_cfg_mbx = (struct lpfc_sli_config_mbox *)dmabuf->virt;
41777ad20aa9SJames Smart 
41787ad20aa9SJames Smart 	if (!bsg_bf_get(lpfc_mbox_hdr_emb,
41797ad20aa9SJames Smart 	    &sli_cfg_mbx->un.sli_config_emb0_subsys.sli_config_hdr)) {
41807ad20aa9SJames Smart 		subsys = bsg_bf_get(lpfc_emb0_subcmnd_subsys,
41817ad20aa9SJames Smart 				    &sli_cfg_mbx->un.sli_config_emb0_subsys);
41827ad20aa9SJames Smart 		opcode = bsg_bf_get(lpfc_emb0_subcmnd_opcode,
41837ad20aa9SJames Smart 				    &sli_cfg_mbx->un.sli_config_emb0_subsys);
41847ad20aa9SJames Smart 		if (subsys == SLI_CONFIG_SUBSYS_FCOE) {
41857ad20aa9SJames Smart 			switch (opcode) {
41867ad20aa9SJames Smart 			case FCOE_OPCODE_READ_FCF:
41872a2719d3SJames Smart 			case FCOE_OPCODE_GET_DPORT_RESULTS:
41887ad20aa9SJames Smart 				lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
41897ad20aa9SJames Smart 						"2957 Handled SLI_CONFIG "
41907ad20aa9SJames Smart 						"subsys_fcoe, opcode:x%x\n",
41917ad20aa9SJames Smart 						opcode);
41927ad20aa9SJames Smart 				rc = lpfc_bsg_sli_cfg_read_cmd_ext(phba, job,
41937ad20aa9SJames Smart 							nemb_mse, dmabuf);
41947ad20aa9SJames Smart 				break;
41957ad20aa9SJames Smart 			case FCOE_OPCODE_ADD_FCF:
41962a2719d3SJames Smart 			case FCOE_OPCODE_SET_DPORT_MODE:
41972a2719d3SJames Smart 			case LPFC_MBOX_OPCODE_FCOE_LINK_DIAG_STATE:
41987ad20aa9SJames Smart 				lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
41997ad20aa9SJames Smart 						"2958 Handled SLI_CONFIG "
42007ad20aa9SJames Smart 						"subsys_fcoe, opcode:x%x\n",
42017ad20aa9SJames Smart 						opcode);
42027ad20aa9SJames Smart 				rc = lpfc_bsg_sli_cfg_write_cmd_ext(phba, job,
42037ad20aa9SJames Smart 							nemb_mse, dmabuf);
42047ad20aa9SJames Smart 				break;
42057ad20aa9SJames Smart 			default:
42067ad20aa9SJames Smart 				lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
4207026abb87SJames Smart 						"2959 Reject SLI_CONFIG "
42087ad20aa9SJames Smart 						"subsys_fcoe, opcode:x%x\n",
42097ad20aa9SJames Smart 						opcode);
4210026abb87SJames Smart 				rc = -EPERM;
4211026abb87SJames Smart 				break;
4212026abb87SJames Smart 			}
4213026abb87SJames Smart 		} else if (subsys == SLI_CONFIG_SUBSYS_COMN) {
4214026abb87SJames Smart 			switch (opcode) {
4215026abb87SJames Smart 			case COMN_OPCODE_GET_CNTL_ADDL_ATTRIBUTES:
4216b99570ddSJames Smart 			case COMN_OPCODE_GET_CNTL_ATTRIBUTES:
4217c6377970SJames Smart 			case COMN_OPCODE_GET_PROFILE_CONFIG:
421845bc4427SDick Kennedy 			case COMN_OPCODE_SET_FEATURES:
4219026abb87SJames Smart 				lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
4220026abb87SJames Smart 						"3106 Handled SLI_CONFIG "
42216b5151fdSJames Smart 						"subsys_comn, opcode:x%x\n",
4222026abb87SJames Smart 						opcode);
4223026abb87SJames Smart 				rc = lpfc_bsg_sli_cfg_read_cmd_ext(phba, job,
4224026abb87SJames Smart 							nemb_mse, dmabuf);
4225026abb87SJames Smart 				break;
4226026abb87SJames Smart 			default:
4227026abb87SJames Smart 				lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
4228026abb87SJames Smart 						"3107 Reject SLI_CONFIG "
42296b5151fdSJames Smart 						"subsys_comn, opcode:x%x\n",
4230026abb87SJames Smart 						opcode);
4231026abb87SJames Smart 				rc = -EPERM;
42327ad20aa9SJames Smart 				break;
42337ad20aa9SJames Smart 			}
42347ad20aa9SJames Smart 		} else {
42357ad20aa9SJames Smart 			lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
4236026abb87SJames Smart 					"2977 Reject SLI_CONFIG "
42377ad20aa9SJames Smart 					"subsys:x%d, opcode:x%x\n",
42387ad20aa9SJames Smart 					subsys, opcode);
4239026abb87SJames Smart 			rc = -EPERM;
42407ad20aa9SJames Smart 		}
42417ad20aa9SJames Smart 	} else {
42427ad20aa9SJames Smart 		subsys = bsg_bf_get(lpfc_emb1_subcmnd_subsys,
42437ad20aa9SJames Smart 				    &sli_cfg_mbx->un.sli_config_emb1_subsys);
42447ad20aa9SJames Smart 		opcode = bsg_bf_get(lpfc_emb1_subcmnd_opcode,
42457ad20aa9SJames Smart 				    &sli_cfg_mbx->un.sli_config_emb1_subsys);
42467ad20aa9SJames Smart 		if (subsys == SLI_CONFIG_SUBSYS_COMN) {
42477ad20aa9SJames Smart 			switch (opcode) {
42487ad20aa9SJames Smart 			case COMN_OPCODE_READ_OBJECT:
42497ad20aa9SJames Smart 			case COMN_OPCODE_READ_OBJECT_LIST:
42507ad20aa9SJames Smart 				lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
42517ad20aa9SJames Smart 						"2960 Handled SLI_CONFIG "
42527ad20aa9SJames Smart 						"subsys_comn, opcode:x%x\n",
42537ad20aa9SJames Smart 						opcode);
42547ad20aa9SJames Smart 				rc = lpfc_bsg_sli_cfg_read_cmd_ext(phba, job,
42557ad20aa9SJames Smart 							nemb_hbd, dmabuf);
42567ad20aa9SJames Smart 				break;
42577ad20aa9SJames Smart 			case COMN_OPCODE_WRITE_OBJECT:
42587ad20aa9SJames Smart 				lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
42597ad20aa9SJames Smart 						"2961 Handled SLI_CONFIG "
42607ad20aa9SJames Smart 						"subsys_comn, opcode:x%x\n",
42617ad20aa9SJames Smart 						opcode);
42627ad20aa9SJames Smart 				rc = lpfc_bsg_sli_cfg_write_cmd_ext(phba, job,
42637ad20aa9SJames Smart 							nemb_hbd, dmabuf);
42647ad20aa9SJames Smart 				break;
42657ad20aa9SJames Smart 			default:
42667ad20aa9SJames Smart 				lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
42677ad20aa9SJames Smart 						"2962 Not handled SLI_CONFIG "
42687ad20aa9SJames Smart 						"subsys_comn, opcode:x%x\n",
42697ad20aa9SJames Smart 						opcode);
42707ad20aa9SJames Smart 				rc = SLI_CONFIG_NOT_HANDLED;
42717ad20aa9SJames Smart 				break;
42727ad20aa9SJames Smart 			}
42737ad20aa9SJames Smart 		} else {
42747ad20aa9SJames Smart 			lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
4275026abb87SJames Smart 					"2978 Not handled SLI_CONFIG "
42767ad20aa9SJames Smart 					"subsys:x%d, opcode:x%x\n",
42777ad20aa9SJames Smart 					subsys, opcode);
42787ad20aa9SJames Smart 			rc = SLI_CONFIG_NOT_HANDLED;
42797ad20aa9SJames Smart 		}
42807ad20aa9SJames Smart 	}
4281026abb87SJames Smart 
4282026abb87SJames Smart 	/* state reset on not handled new multi-buffer mailbox command */
4283026abb87SJames Smart 	if (rc != SLI_CONFIG_HANDLED)
4284026abb87SJames Smart 		phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_IDLE;
4285026abb87SJames Smart 
42867ad20aa9SJames Smart 	return rc;
42877ad20aa9SJames Smart }
42887ad20aa9SJames Smart 
42897ad20aa9SJames Smart /**
42903145d2d6SLee Jones  * lpfc_bsg_mbox_ext_abort - request to abort mbox command with ext buffers
42917ad20aa9SJames Smart  * @phba: Pointer to HBA context object.
42927ad20aa9SJames Smart  *
42937ad20aa9SJames Smart  * This routine is for requesting to abort a pass-through mailbox command with
42947ad20aa9SJames Smart  * multiple external buffers due to error condition.
42957ad20aa9SJames Smart  **/
42967ad20aa9SJames Smart static void
42977ad20aa9SJames Smart lpfc_bsg_mbox_ext_abort(struct lpfc_hba *phba)
42987ad20aa9SJames Smart {
42997ad20aa9SJames Smart 	if (phba->mbox_ext_buf_ctx.state == LPFC_BSG_MBOX_PORT)
43007ad20aa9SJames Smart 		phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_ABTS;
43017ad20aa9SJames Smart 	else
43027ad20aa9SJames Smart 		lpfc_bsg_mbox_ext_session_reset(phba);
43037ad20aa9SJames Smart 	return;
43047ad20aa9SJames Smart }
43057ad20aa9SJames Smart 
43067ad20aa9SJames Smart /**
43077ad20aa9SJames Smart  * lpfc_bsg_read_ebuf_get - get the next mailbox read external buffer
43087ad20aa9SJames Smart  * @phba: Pointer to HBA context object.
4309ea085dabSLee Jones  * @job: Pointer to the job object.
43107ad20aa9SJames Smart  *
43117ad20aa9SJames Smart  * This routine extracts the next mailbox read external buffer back to
43127ad20aa9SJames Smart  * user space through BSG.
43137ad20aa9SJames Smart  **/
43147ad20aa9SJames Smart static int
431575cc8cfcSJohannes Thumshirn lpfc_bsg_read_ebuf_get(struct lpfc_hba *phba, struct bsg_job *job)
43167ad20aa9SJames Smart {
431701e0e15cSJohannes Thumshirn 	struct fc_bsg_reply *bsg_reply = job->reply;
43187ad20aa9SJames Smart 	struct lpfc_sli_config_mbox *sli_cfg_mbx;
43197ad20aa9SJames Smart 	struct lpfc_dmabuf *dmabuf;
43207ad20aa9SJames Smart 	uint8_t *pbuf;
43217ad20aa9SJames Smart 	uint32_t size;
43227ad20aa9SJames Smart 	uint32_t index;
43237ad20aa9SJames Smart 
43247ad20aa9SJames Smart 	index = phba->mbox_ext_buf_ctx.seqNum;
43257ad20aa9SJames Smart 	phba->mbox_ext_buf_ctx.seqNum++;
43267ad20aa9SJames Smart 
43277ad20aa9SJames Smart 	sli_cfg_mbx = (struct lpfc_sli_config_mbox *)
43287ad20aa9SJames Smart 			phba->mbox_ext_buf_ctx.mbx_dmabuf->virt;
43297ad20aa9SJames Smart 
43307ad20aa9SJames Smart 	if (phba->mbox_ext_buf_ctx.nembType == nemb_mse) {
43317ad20aa9SJames Smart 		size = bsg_bf_get(lpfc_mbox_sli_config_mse_len,
43327ad20aa9SJames Smart 			&sli_cfg_mbx->un.sli_config_emb0_subsys.mse[index]);
43337ad20aa9SJames Smart 		lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
43347ad20aa9SJames Smart 				"2963 SLI_CONFIG (mse) ext-buffer rd get "
43357ad20aa9SJames Smart 				"buffer[%d], size:%d\n", index, size);
43367ad20aa9SJames Smart 	} else {
43377ad20aa9SJames Smart 		size = bsg_bf_get(lpfc_mbox_sli_config_ecmn_hbd_len,
43387ad20aa9SJames Smart 			&sli_cfg_mbx->un.sli_config_emb1_subsys.hbd[index]);
43397ad20aa9SJames Smart 		lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
43407ad20aa9SJames Smart 				"2964 SLI_CONFIG (hbd) ext-buffer rd get "
43417ad20aa9SJames Smart 				"buffer[%d], size:%d\n", index, size);
43427ad20aa9SJames Smart 	}
43437ad20aa9SJames Smart 	if (list_empty(&phba->mbox_ext_buf_ctx.ext_dmabuf_list))
43447ad20aa9SJames Smart 		return -EPIPE;
43457ad20aa9SJames Smart 	dmabuf = list_first_entry(&phba->mbox_ext_buf_ctx.ext_dmabuf_list,
43467ad20aa9SJames Smart 				  struct lpfc_dmabuf, list);
43477ad20aa9SJames Smart 	list_del_init(&dmabuf->list);
4348b76f2dc9SJames Smart 
4349b76f2dc9SJames Smart 	/* after dma buffer descriptor setup */
4350b76f2dc9SJames Smart 	lpfc_idiag_mbxacc_dump_bsg_mbox(phba, phba->mbox_ext_buf_ctx.nembType,
4351b76f2dc9SJames Smart 					mbox_rd, dma_ebuf, sta_pos_addr,
4352b76f2dc9SJames Smart 					dmabuf, index);
4353b76f2dc9SJames Smart 
43547ad20aa9SJames Smart 	pbuf = (uint8_t *)dmabuf->virt;
435501e0e15cSJohannes Thumshirn 	bsg_reply->reply_payload_rcv_len =
43567ad20aa9SJames Smart 		sg_copy_from_buffer(job->reply_payload.sg_list,
43577ad20aa9SJames Smart 				    job->reply_payload.sg_cnt,
43587ad20aa9SJames Smart 				    pbuf, size);
43597ad20aa9SJames Smart 
43607ad20aa9SJames Smart 	lpfc_bsg_dma_page_free(phba, dmabuf);
43617ad20aa9SJames Smart 
43627ad20aa9SJames Smart 	if (phba->mbox_ext_buf_ctx.seqNum == phba->mbox_ext_buf_ctx.numBuf) {
43637ad20aa9SJames Smart 		lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
43647ad20aa9SJames Smart 				"2965 SLI_CONFIG (hbd) ext-buffer rd mbox "
43657ad20aa9SJames Smart 				"command session done\n");
43667ad20aa9SJames Smart 		lpfc_bsg_mbox_ext_session_reset(phba);
43677ad20aa9SJames Smart 	}
43687ad20aa9SJames Smart 
436901e0e15cSJohannes Thumshirn 	bsg_reply->result = 0;
437006548160SJohannes Thumshirn 	bsg_job_done(job, bsg_reply->result,
43711abaede7SJohannes Thumshirn 		       bsg_reply->reply_payload_rcv_len);
43727ad20aa9SJames Smart 
43737ad20aa9SJames Smart 	return SLI_CONFIG_HANDLED;
43747ad20aa9SJames Smart }
43757ad20aa9SJames Smart 
43767ad20aa9SJames Smart /**
43777ad20aa9SJames Smart  * lpfc_bsg_write_ebuf_set - set the next mailbox write external buffer
43787ad20aa9SJames Smart  * @phba: Pointer to HBA context object.
4379ea085dabSLee Jones  * @job: Pointer to the job object.
43807ad20aa9SJames Smart  * @dmabuf: Pointer to a DMA buffer descriptor.
43817ad20aa9SJames Smart  *
43827ad20aa9SJames Smart  * This routine sets up the next mailbox read external buffer obtained
43837ad20aa9SJames Smart  * from user space through BSG.
43847ad20aa9SJames Smart  **/
43857ad20aa9SJames Smart static int
438675cc8cfcSJohannes Thumshirn lpfc_bsg_write_ebuf_set(struct lpfc_hba *phba, struct bsg_job *job,
43877ad20aa9SJames Smart 			struct lpfc_dmabuf *dmabuf)
43887ad20aa9SJames Smart {
438901e0e15cSJohannes Thumshirn 	struct fc_bsg_reply *bsg_reply = job->reply;
43907ad20aa9SJames Smart 	struct bsg_job_data *dd_data = NULL;
43917ad20aa9SJames Smart 	LPFC_MBOXQ_t *pmboxq = NULL;
43927ad20aa9SJames Smart 	MAILBOX_t *pmb;
43937ad20aa9SJames Smart 	enum nemb_type nemb_tp;
43947ad20aa9SJames Smart 	uint8_t *pbuf;
43957ad20aa9SJames Smart 	uint32_t size;
43967ad20aa9SJames Smart 	uint32_t index;
43977ad20aa9SJames Smart 	int rc;
43987ad20aa9SJames Smart 
43997ad20aa9SJames Smart 	index = phba->mbox_ext_buf_ctx.seqNum;
44007ad20aa9SJames Smart 	phba->mbox_ext_buf_ctx.seqNum++;
44017ad20aa9SJames Smart 	nemb_tp = phba->mbox_ext_buf_ctx.nembType;
44027ad20aa9SJames Smart 
44037ad20aa9SJames Smart 	pbuf = (uint8_t *)dmabuf->virt;
44047ad20aa9SJames Smart 	size = job->request_payload.payload_len;
44057ad20aa9SJames Smart 	sg_copy_to_buffer(job->request_payload.sg_list,
44067ad20aa9SJames Smart 			  job->request_payload.sg_cnt,
44077ad20aa9SJames Smart 			  pbuf, size);
44087ad20aa9SJames Smart 
44097ad20aa9SJames Smart 	if (phba->mbox_ext_buf_ctx.nembType == nemb_mse) {
44107ad20aa9SJames Smart 		lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
44117ad20aa9SJames Smart 				"2966 SLI_CONFIG (mse) ext-buffer wr set "
44127ad20aa9SJames Smart 				"buffer[%d], size:%d\n",
44137ad20aa9SJames Smart 				phba->mbox_ext_buf_ctx.seqNum, size);
44147ad20aa9SJames Smart 
44157ad20aa9SJames Smart 	} else {
44167ad20aa9SJames Smart 		lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
44177ad20aa9SJames Smart 				"2967 SLI_CONFIG (hbd) ext-buffer wr set "
44187ad20aa9SJames Smart 				"buffer[%d], size:%d\n",
44197ad20aa9SJames Smart 				phba->mbox_ext_buf_ctx.seqNum, size);
44207ad20aa9SJames Smart 
44217ad20aa9SJames Smart 	}
44227ad20aa9SJames Smart 
44237ad20aa9SJames Smart 	/* set up external buffer descriptor and add to external buffer list */
44247ad20aa9SJames Smart 	lpfc_bsg_sli_cfg_dma_desc_setup(phba, nemb_tp, index,
44257ad20aa9SJames Smart 					phba->mbox_ext_buf_ctx.mbx_dmabuf,
44267ad20aa9SJames Smart 					dmabuf);
44277ad20aa9SJames Smart 	list_add_tail(&dmabuf->list, &phba->mbox_ext_buf_ctx.ext_dmabuf_list);
44287ad20aa9SJames Smart 
4429b76f2dc9SJames Smart 	/* after write dma buffer */
4430b76f2dc9SJames Smart 	lpfc_idiag_mbxacc_dump_bsg_mbox(phba, phba->mbox_ext_buf_ctx.nembType,
4431b76f2dc9SJames Smart 					mbox_wr, dma_ebuf, sta_pos_addr,
4432b76f2dc9SJames Smart 					dmabuf, index);
4433b76f2dc9SJames Smart 
44347ad20aa9SJames Smart 	if (phba->mbox_ext_buf_ctx.seqNum == phba->mbox_ext_buf_ctx.numBuf) {
44357ad20aa9SJames Smart 		lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
44367ad20aa9SJames Smart 				"2968 SLI_CONFIG ext-buffer wr all %d "
44377ad20aa9SJames Smart 				"ebuffers received\n",
44387ad20aa9SJames Smart 				phba->mbox_ext_buf_ctx.numBuf);
44399a1b0b9aSBo Wu 
44409a1b0b9aSBo Wu 		dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
44419a1b0b9aSBo Wu 		if (!dd_data) {
44429a1b0b9aSBo Wu 			rc = -ENOMEM;
44439a1b0b9aSBo Wu 			goto job_error;
44449a1b0b9aSBo Wu 		}
44459a1b0b9aSBo Wu 
44467ad20aa9SJames Smart 		/* mailbox command structure for base driver */
44477ad20aa9SJames Smart 		pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
44487ad20aa9SJames Smart 		if (!pmboxq) {
44497ad20aa9SJames Smart 			rc = -ENOMEM;
44507ad20aa9SJames Smart 			goto job_error;
44517ad20aa9SJames Smart 		}
44527ad20aa9SJames Smart 		memset(pmboxq, 0, sizeof(LPFC_MBOXQ_t));
44537ad20aa9SJames Smart 		pbuf = (uint8_t *)phba->mbox_ext_buf_ctx.mbx_dmabuf->virt;
44547ad20aa9SJames Smart 		pmb = &pmboxq->u.mb;
44557ad20aa9SJames Smart 		memcpy(pmb, pbuf, sizeof(*pmb));
44567ad20aa9SJames Smart 		pmb->mbxOwner = OWN_HOST;
44577ad20aa9SJames Smart 		pmboxq->vport = phba->pport;
44587ad20aa9SJames Smart 
44597ad20aa9SJames Smart 		/* callback for multi-buffer write mailbox command */
44607ad20aa9SJames Smart 		pmboxq->mbox_cmpl = lpfc_bsg_issue_write_mbox_ext_cmpl;
44617ad20aa9SJames Smart 
44627ad20aa9SJames Smart 		/* context fields to callback function */
446385d77f91SJustin Tee 		pmboxq->ctx_u.dd_data = dd_data;
44647ad20aa9SJames Smart 		dd_data->type = TYPE_MBOX;
4465a33c4f7bSJames Smart 		dd_data->set_job = job;
44667ad20aa9SJames Smart 		dd_data->context_un.mbox.pmboxq = pmboxq;
44677ad20aa9SJames Smart 		dd_data->context_un.mbox.mb = (MAILBOX_t *)pbuf;
44687ad20aa9SJames Smart 		job->dd_data = dd_data;
44697ad20aa9SJames Smart 
44707ad20aa9SJames Smart 		/* state change */
44717ad20aa9SJames Smart 		phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_PORT;
44727ad20aa9SJames Smart 
44737ad20aa9SJames Smart 		rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT);
44747ad20aa9SJames Smart 		if ((rc == MBX_SUCCESS) || (rc == MBX_BUSY)) {
44757ad20aa9SJames Smart 			lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
44767ad20aa9SJames Smart 					"2969 Issued SLI_CONFIG ext-buffer "
44777afc0ce9SColin Ian King 					"mailbox command, rc:x%x\n", rc);
447888a2cfbbSJames Smart 			return SLI_CONFIG_HANDLED;
44797ad20aa9SJames Smart 		}
44807ad20aa9SJames Smart 		lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
44817ad20aa9SJames Smart 				"2970 Failed to issue SLI_CONFIG ext-buffer "
44827afc0ce9SColin Ian King 				"mailbox command, rc:x%x\n", rc);
44837ad20aa9SJames Smart 		rc = -EPIPE;
44847ad20aa9SJames Smart 		goto job_error;
44857ad20aa9SJames Smart 	}
44867ad20aa9SJames Smart 
44873bfab8a0SJames Smart 	/* wait for additional external buffers */
448801e0e15cSJohannes Thumshirn 	bsg_reply->result = 0;
448906548160SJohannes Thumshirn 	bsg_job_done(job, bsg_reply->result,
44901abaede7SJohannes Thumshirn 		       bsg_reply->reply_payload_rcv_len);
44917ad20aa9SJames Smart 	return SLI_CONFIG_HANDLED;
44927ad20aa9SJames Smart 
44937ad20aa9SJames Smart job_error:
44949a1b0b9aSBo Wu 	if (pmboxq)
44959a1b0b9aSBo Wu 		mempool_free(pmboxq, phba->mbox_mem_pool);
44967ad20aa9SJames Smart 	lpfc_bsg_dma_page_free(phba, dmabuf);
44977ad20aa9SJames Smart 	kfree(dd_data);
44987ad20aa9SJames Smart 
44997ad20aa9SJames Smart 	return rc;
45007ad20aa9SJames Smart }
45017ad20aa9SJames Smart 
45027ad20aa9SJames Smart /**
45037ad20aa9SJames Smart  * lpfc_bsg_handle_sli_cfg_ebuf - handle ext buffer with sli-cfg mailbox cmd
45047ad20aa9SJames Smart  * @phba: Pointer to HBA context object.
4505ea085dabSLee Jones  * @job: Pointer to the job object.
4506ea085dabSLee Jones  * @dmabuf: Pointer to a DMA buffer descriptor.
45077ad20aa9SJames Smart  *
45087ad20aa9SJames Smart  * This routine handles the external buffer with SLI_CONFIG (0x9B) mailbox
45097ad20aa9SJames Smart  * command with multiple non-embedded external buffers.
45107ad20aa9SJames Smart  **/
45117ad20aa9SJames Smart static int
451275cc8cfcSJohannes Thumshirn lpfc_bsg_handle_sli_cfg_ebuf(struct lpfc_hba *phba, struct bsg_job *job,
45137ad20aa9SJames Smart 			     struct lpfc_dmabuf *dmabuf)
45147ad20aa9SJames Smart {
45157ad20aa9SJames Smart 	int rc;
45167ad20aa9SJames Smart 
45177ad20aa9SJames Smart 	lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
45187ad20aa9SJames Smart 			"2971 SLI_CONFIG buffer (type:x%x)\n",
45197ad20aa9SJames Smart 			phba->mbox_ext_buf_ctx.mboxType);
45207ad20aa9SJames Smart 
45217ad20aa9SJames Smart 	if (phba->mbox_ext_buf_ctx.mboxType == mbox_rd) {
45227ad20aa9SJames Smart 		if (phba->mbox_ext_buf_ctx.state != LPFC_BSG_MBOX_DONE) {
45237ad20aa9SJames Smart 			lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
45247ad20aa9SJames Smart 					"2972 SLI_CONFIG rd buffer state "
45257ad20aa9SJames Smart 					"mismatch:x%x\n",
45267ad20aa9SJames Smart 					phba->mbox_ext_buf_ctx.state);
45277ad20aa9SJames Smart 			lpfc_bsg_mbox_ext_abort(phba);
45287ad20aa9SJames Smart 			return -EPIPE;
45297ad20aa9SJames Smart 		}
45307ad20aa9SJames Smart 		rc = lpfc_bsg_read_ebuf_get(phba, job);
45317ad20aa9SJames Smart 		if (rc == SLI_CONFIG_HANDLED)
45327ad20aa9SJames Smart 			lpfc_bsg_dma_page_free(phba, dmabuf);
45337ad20aa9SJames Smart 	} else { /* phba->mbox_ext_buf_ctx.mboxType == mbox_wr */
45347ad20aa9SJames Smart 		if (phba->mbox_ext_buf_ctx.state != LPFC_BSG_MBOX_HOST) {
45357ad20aa9SJames Smart 			lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
45367ad20aa9SJames Smart 					"2973 SLI_CONFIG wr buffer state "
45377ad20aa9SJames Smart 					"mismatch:x%x\n",
45387ad20aa9SJames Smart 					phba->mbox_ext_buf_ctx.state);
45397ad20aa9SJames Smart 			lpfc_bsg_mbox_ext_abort(phba);
45407ad20aa9SJames Smart 			return -EPIPE;
45417ad20aa9SJames Smart 		}
45427ad20aa9SJames Smart 		rc = lpfc_bsg_write_ebuf_set(phba, job, dmabuf);
45437ad20aa9SJames Smart 	}
45447ad20aa9SJames Smart 	return rc;
45457ad20aa9SJames Smart }
45467ad20aa9SJames Smart 
45477ad20aa9SJames Smart /**
45487ad20aa9SJames Smart  * lpfc_bsg_handle_sli_cfg_ext - handle sli-cfg mailbox with external buffer
45497ad20aa9SJames Smart  * @phba: Pointer to HBA context object.
4550ea085dabSLee Jones  * @job: Pointer to the job object.
4551ea085dabSLee Jones  * @dmabuf: Pointer to a DMA buffer descriptor.
45527ad20aa9SJames Smart  *
45533bfab8a0SJames Smart  * This routine checks and handles non-embedded multi-buffer SLI_CONFIG
45547ad20aa9SJames Smart  * (0x9B) mailbox commands and external buffers.
45557ad20aa9SJames Smart  **/
45567ad20aa9SJames Smart static int
455775cc8cfcSJohannes Thumshirn lpfc_bsg_handle_sli_cfg_ext(struct lpfc_hba *phba, struct bsg_job *job,
45587ad20aa9SJames Smart 			    struct lpfc_dmabuf *dmabuf)
45597ad20aa9SJames Smart {
456001e0e15cSJohannes Thumshirn 	struct fc_bsg_request *bsg_request = job->request;
45617ad20aa9SJames Smart 	struct dfc_mbox_req *mbox_req;
456288a2cfbbSJames Smart 	int rc = SLI_CONFIG_NOT_HANDLED;
45637ad20aa9SJames Smart 
45647ad20aa9SJames Smart 	mbox_req =
456501e0e15cSJohannes Thumshirn 	   (struct dfc_mbox_req *)bsg_request->rqst_data.h_vendor.vendor_cmd;
45667ad20aa9SJames Smart 
45677ad20aa9SJames Smart 	/* mbox command with/without single external buffer */
45687ad20aa9SJames Smart 	if (mbox_req->extMboxTag == 0 && mbox_req->extSeqNum == 0)
456988a2cfbbSJames Smart 		return rc;
45707ad20aa9SJames Smart 
45717ad20aa9SJames Smart 	/* mbox command and first external buffer */
45727ad20aa9SJames Smart 	if (phba->mbox_ext_buf_ctx.state == LPFC_BSG_MBOX_IDLE) {
45737ad20aa9SJames Smart 		if (mbox_req->extSeqNum == 1) {
45747ad20aa9SJames Smart 			lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
45757ad20aa9SJames Smart 					"2974 SLI_CONFIG mailbox: tag:%d, "
45767ad20aa9SJames Smart 					"seq:%d\n", mbox_req->extMboxTag,
45777ad20aa9SJames Smart 					mbox_req->extSeqNum);
45787ad20aa9SJames Smart 			rc = lpfc_bsg_handle_sli_cfg_mbox(phba, job, dmabuf);
45797ad20aa9SJames Smart 			return rc;
45807ad20aa9SJames Smart 		} else
45817ad20aa9SJames Smart 			goto sli_cfg_ext_error;
45827ad20aa9SJames Smart 	}
45837ad20aa9SJames Smart 
45847ad20aa9SJames Smart 	/*
45857ad20aa9SJames Smart 	 * handle additional external buffers
45867ad20aa9SJames Smart 	 */
45877ad20aa9SJames Smart 
45887ad20aa9SJames Smart 	/* check broken pipe conditions */
45897ad20aa9SJames Smart 	if (mbox_req->extMboxTag != phba->mbox_ext_buf_ctx.mbxTag)
45907ad20aa9SJames Smart 		goto sli_cfg_ext_error;
45917ad20aa9SJames Smart 	if (mbox_req->extSeqNum > phba->mbox_ext_buf_ctx.numBuf)
45927ad20aa9SJames Smart 		goto sli_cfg_ext_error;
45937ad20aa9SJames Smart 	if (mbox_req->extSeqNum != phba->mbox_ext_buf_ctx.seqNum + 1)
45947ad20aa9SJames Smart 		goto sli_cfg_ext_error;
45957ad20aa9SJames Smart 
45967ad20aa9SJames Smart 	lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
45977ad20aa9SJames Smart 			"2975 SLI_CONFIG mailbox external buffer: "
45987ad20aa9SJames Smart 			"extSta:x%x, tag:%d, seq:%d\n",
45997ad20aa9SJames Smart 			phba->mbox_ext_buf_ctx.state, mbox_req->extMboxTag,
46007ad20aa9SJames Smart 			mbox_req->extSeqNum);
46017ad20aa9SJames Smart 	rc = lpfc_bsg_handle_sli_cfg_ebuf(phba, job, dmabuf);
46027ad20aa9SJames Smart 	return rc;
46037ad20aa9SJames Smart 
46047ad20aa9SJames Smart sli_cfg_ext_error:
46057ad20aa9SJames Smart 	/* all other cases, broken pipe */
46067ad20aa9SJames Smart 	lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
46077ad20aa9SJames Smart 			"2976 SLI_CONFIG mailbox broken pipe: "
46087ad20aa9SJames Smart 			"ctxSta:x%x, ctxNumBuf:%d "
46097ad20aa9SJames Smart 			"ctxTag:%d, ctxSeq:%d, tag:%d, seq:%d\n",
46107ad20aa9SJames Smart 			phba->mbox_ext_buf_ctx.state,
46117ad20aa9SJames Smart 			phba->mbox_ext_buf_ctx.numBuf,
46127ad20aa9SJames Smart 			phba->mbox_ext_buf_ctx.mbxTag,
46137ad20aa9SJames Smart 			phba->mbox_ext_buf_ctx.seqNum,
46147ad20aa9SJames Smart 			mbox_req->extMboxTag, mbox_req->extSeqNum);
46157ad20aa9SJames Smart 
46167ad20aa9SJames Smart 	lpfc_bsg_mbox_ext_session_reset(phba);
46177ad20aa9SJames Smart 
46187ad20aa9SJames Smart 	return -EPIPE;
46197ad20aa9SJames Smart }
46207ad20aa9SJames Smart 
46217ad20aa9SJames Smart /**
46223b5dd52aSJames Smart  * lpfc_bsg_issue_mbox - issues a mailbox command on behalf of an app
46233b5dd52aSJames Smart  * @phba: Pointer to HBA context object.
4624ea085dabSLee Jones  * @job: Pointer to the job object.
46253b5dd52aSJames Smart  * @vport: Pointer to a vport object.
46263b5dd52aSJames Smart  *
46273b5dd52aSJames Smart  * Allocate a tracking object, mailbox command memory, get a mailbox
46283b5dd52aSJames Smart  * from the mailbox pool, copy the caller mailbox command.
46293b5dd52aSJames Smart  *
46303b5dd52aSJames Smart  * If offline and the sli is active we need to poll for the command (port is
46313bfab8a0SJames Smart  * being reset) and complete the job, otherwise issue the mailbox command and
46323b5dd52aSJames Smart  * let our completion handler finish the command.
46333b5dd52aSJames Smart  **/
4634a2fc4aefSJames Smart static int
463575cc8cfcSJohannes Thumshirn lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct bsg_job *job,
46363b5dd52aSJames Smart 	struct lpfc_vport *vport)
46373b5dd52aSJames Smart {
463801e0e15cSJohannes Thumshirn 	struct fc_bsg_request *bsg_request = job->request;
463901e0e15cSJohannes Thumshirn 	struct fc_bsg_reply *bsg_reply = job->reply;
46407a470277SJames Smart 	LPFC_MBOXQ_t *pmboxq = NULL; /* internal mailbox queue */
46417a470277SJames Smart 	MAILBOX_t *pmb; /* shortcut to the pmboxq mailbox */
46427a470277SJames Smart 	/* a 4k buffer to hold the mb and extended data from/to the bsg */
46437ad20aa9SJames Smart 	uint8_t *pmbx = NULL;
46447a470277SJames Smart 	struct bsg_job_data *dd_data = NULL; /* bsg data tracking structure */
46457ad20aa9SJames Smart 	struct lpfc_dmabuf *dmabuf = NULL;
46467ad20aa9SJames Smart 	struct dfc_mbox_req *mbox_req;
4647b6e3b9c6SJames Smart 	struct READ_EVENT_LOG_VAR *rdEventLog;
4648b6e3b9c6SJames Smart 	uint32_t transmit_length, receive_length, mode;
46497ad20aa9SJames Smart 	struct lpfc_mbx_sli4_config *sli4_config;
4650b6e3b9c6SJames Smart 	struct lpfc_mbx_nembed_cmd *nembed_sge;
4651b6e3b9c6SJames Smart 	struct ulp_bde64 *bde;
46527a470277SJames Smart 	uint8_t *ext = NULL;
46533b5dd52aSJames Smart 	int rc = 0;
46547a470277SJames Smart 	uint8_t *from;
46557ad20aa9SJames Smart 	uint32_t size;
46567ad20aa9SJames Smart 
46577a470277SJames Smart 	/* in case no data is transferred */
465801e0e15cSJohannes Thumshirn 	bsg_reply->reply_payload_rcv_len = 0;
46597a470277SJames Smart 
4660b6e3b9c6SJames Smart 	/* sanity check to protect driver */
4661b6e3b9c6SJames Smart 	if (job->reply_payload.payload_len > BSG_MBOX_SIZE ||
4662b6e3b9c6SJames Smart 	    job->request_payload.payload_len > BSG_MBOX_SIZE) {
4663b6e3b9c6SJames Smart 		rc = -ERANGE;
4664b6e3b9c6SJames Smart 		goto job_done;
4665b6e3b9c6SJames Smart 	}
4666b6e3b9c6SJames Smart 
46677ad20aa9SJames Smart 	/*
46687ad20aa9SJames Smart 	 * Don't allow mailbox commands to be sent when blocked or when in
46697ad20aa9SJames Smart 	 * the middle of discovery
46707ad20aa9SJames Smart 	 */
46717ad20aa9SJames Smart 	if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO) {
46727ad20aa9SJames Smart 		rc = -EAGAIN;
46737ad20aa9SJames Smart 		goto job_done;
46747ad20aa9SJames Smart 	}
46757ad20aa9SJames Smart 
46767ad20aa9SJames Smart 	mbox_req =
467701e0e15cSJohannes Thumshirn 	    (struct dfc_mbox_req *)bsg_request->rqst_data.h_vendor.vendor_cmd;
46787ad20aa9SJames Smart 
46797a470277SJames Smart 	/* check if requested extended data lengths are valid */
4680b6e3b9c6SJames Smart 	if ((mbox_req->inExtWLen > BSG_MBOX_SIZE/sizeof(uint32_t)) ||
4681b6e3b9c6SJames Smart 	    (mbox_req->outExtWLen > BSG_MBOX_SIZE/sizeof(uint32_t))) {
46827a470277SJames Smart 		rc = -ERANGE;
46837a470277SJames Smart 		goto job_done;
46847a470277SJames Smart 	}
46853b5dd52aSJames Smart 
46867ad20aa9SJames Smart 	dmabuf = lpfc_bsg_dma_page_alloc(phba);
46877ad20aa9SJames Smart 	if (!dmabuf || !dmabuf->virt) {
46887ad20aa9SJames Smart 		rc = -ENOMEM;
46897ad20aa9SJames Smart 		goto job_done;
46907ad20aa9SJames Smart 	}
46917ad20aa9SJames Smart 
46927ad20aa9SJames Smart 	/* Get the mailbox command or external buffer from BSG */
46937ad20aa9SJames Smart 	pmbx = (uint8_t *)dmabuf->virt;
46947ad20aa9SJames Smart 	size = job->request_payload.payload_len;
46957ad20aa9SJames Smart 	sg_copy_to_buffer(job->request_payload.sg_list,
46967ad20aa9SJames Smart 			  job->request_payload.sg_cnt, pmbx, size);
46977ad20aa9SJames Smart 
46987ad20aa9SJames Smart 	/* Handle possible SLI_CONFIG with non-embedded payloads */
46997ad20aa9SJames Smart 	if (phba->sli_rev == LPFC_SLI_REV4) {
47007ad20aa9SJames Smart 		rc = lpfc_bsg_handle_sli_cfg_ext(phba, job, dmabuf);
47017ad20aa9SJames Smart 		if (rc == SLI_CONFIG_HANDLED)
47027ad20aa9SJames Smart 			goto job_cont;
47037ad20aa9SJames Smart 		if (rc)
47047ad20aa9SJames Smart 			goto job_done;
47057ad20aa9SJames Smart 		/* SLI_CONFIG_NOT_HANDLED for other mailbox commands */
47067ad20aa9SJames Smart 	}
47077ad20aa9SJames Smart 
47087ad20aa9SJames Smart 	rc = lpfc_bsg_check_cmd_access(phba, (MAILBOX_t *)pmbx, vport);
47097ad20aa9SJames Smart 	if (rc != 0)
47107ad20aa9SJames Smart 		goto job_done; /* must be negative */
47117ad20aa9SJames Smart 
47123b5dd52aSJames Smart 	/* allocate our bsg tracking structure */
47133b5dd52aSJames Smart 	dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
47143b5dd52aSJames Smart 	if (!dd_data) {
47153b5dd52aSJames Smart 		lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
47163b5dd52aSJames Smart 				"2727 Failed allocation of dd_data\n");
47177a470277SJames Smart 		rc = -ENOMEM;
47187a470277SJames Smart 		goto job_done;
47193b5dd52aSJames Smart 	}
47203b5dd52aSJames Smart 
47213b5dd52aSJames Smart 	pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
47223b5dd52aSJames Smart 	if (!pmboxq) {
47237a470277SJames Smart 		rc = -ENOMEM;
47247a470277SJames Smart 		goto job_done;
47253b5dd52aSJames Smart 	}
47267a470277SJames Smart 	memset(pmboxq, 0, sizeof(LPFC_MBOXQ_t));
47273b5dd52aSJames Smart 
47283b5dd52aSJames Smart 	pmb = &pmboxq->u.mb;
47297ad20aa9SJames Smart 	memcpy(pmb, pmbx, sizeof(*pmb));
47303b5dd52aSJames Smart 	pmb->mbxOwner = OWN_HOST;
47313b5dd52aSJames Smart 	pmboxq->vport = vport;
47323b5dd52aSJames Smart 
4733c7495937SJames Smart 	/* If HBA encountered an error attention, allow only DUMP
4734c7495937SJames Smart 	 * or RESTART mailbox commands until the HBA is restarted.
4735c7495937SJames Smart 	 */
4736c7495937SJames Smart 	if (phba->pport->stopped &&
4737c7495937SJames Smart 	    pmb->mbxCommand != MBX_DUMP_MEMORY &&
4738c7495937SJames Smart 	    pmb->mbxCommand != MBX_RESTART &&
4739c7495937SJames Smart 	    pmb->mbxCommand != MBX_WRITE_VPARMS &&
4740c7495937SJames Smart 	    pmb->mbxCommand != MBX_WRITE_WWN)
4741c7495937SJames Smart 		lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX,
4742c7495937SJames Smart 				"2797 mbox: Issued mailbox cmd "
4743c7495937SJames Smart 				"0x%x while in stopped state.\n",
4744c7495937SJames Smart 				pmb->mbxCommand);
4745c7495937SJames Smart 
47467a470277SJames Smart 	/* extended mailbox commands will need an extended buffer */
4747c7495937SJames Smart 	if (mbox_req->inExtWLen || mbox_req->outExtWLen) {
47487ad20aa9SJames Smart 		from = pmbx;
47497ad20aa9SJames Smart 		ext = from + sizeof(MAILBOX_t);
4750115d137aSJustin Tee 		pmboxq->ext_buf = ext;
47517a470277SJames Smart 		pmboxq->in_ext_byte_len =
47527a470277SJames Smart 			mbox_req->inExtWLen * sizeof(uint32_t);
47537a470277SJames Smart 		pmboxq->out_ext_byte_len =
4754c7495937SJames Smart 			mbox_req->outExtWLen * sizeof(uint32_t);
47557a470277SJames Smart 		pmboxq->mbox_offset_word = mbox_req->mbOffset;
47567a470277SJames Smart 	}
47577a470277SJames Smart 
47587a470277SJames Smart 	/* biu diag will need a kernel buffer to transfer the data
47597a470277SJames Smart 	 * allocate our own buffer and setup the mailbox command to
47607a470277SJames Smart 	 * use ours
47617a470277SJames Smart 	 */
47627a470277SJames Smart 	if (pmb->mbxCommand == MBX_RUN_BIU_DIAG64) {
4763b6e3b9c6SJames Smart 		transmit_length = pmb->un.varWords[1];
4764b6e3b9c6SJames Smart 		receive_length = pmb->un.varWords[4];
4765c7495937SJames Smart 		/* transmit length cannot be greater than receive length or
4766c7495937SJames Smart 		 * mailbox extension size
4767c7495937SJames Smart 		 */
4768c7495937SJames Smart 		if ((transmit_length > receive_length) ||
476988a2cfbbSJames Smart 			(transmit_length > BSG_MBOX_SIZE - sizeof(MAILBOX_t))) {
4770c7495937SJames Smart 			rc = -ERANGE;
4771c7495937SJames Smart 			goto job_done;
4772c7495937SJames Smart 		}
47737a470277SJames Smart 		pmb->un.varBIUdiag.un.s2.xmit_bde64.addrHigh =
47747ad20aa9SJames Smart 			putPaddrHigh(dmabuf->phys + sizeof(MAILBOX_t));
47757a470277SJames Smart 		pmb->un.varBIUdiag.un.s2.xmit_bde64.addrLow =
47767ad20aa9SJames Smart 			putPaddrLow(dmabuf->phys + sizeof(MAILBOX_t));
47777a470277SJames Smart 
47787a470277SJames Smart 		pmb->un.varBIUdiag.un.s2.rcv_bde64.addrHigh =
47797ad20aa9SJames Smart 			putPaddrHigh(dmabuf->phys + sizeof(MAILBOX_t)
47807ad20aa9SJames Smart 			  + pmb->un.varBIUdiag.un.s2.xmit_bde64.tus.f.bdeSize);
47817a470277SJames Smart 		pmb->un.varBIUdiag.un.s2.rcv_bde64.addrLow =
47827ad20aa9SJames Smart 			putPaddrLow(dmabuf->phys + sizeof(MAILBOX_t)
47837ad20aa9SJames Smart 			  + pmb->un.varBIUdiag.un.s2.xmit_bde64.tus.f.bdeSize);
4784c7495937SJames Smart 	} else if (pmb->mbxCommand == MBX_READ_EVENT_LOG) {
4785b6e3b9c6SJames Smart 		rdEventLog = &pmb->un.varRdEventLog;
4786b6e3b9c6SJames Smart 		receive_length = rdEventLog->rcv_bde64.tus.f.bdeSize;
4787b6e3b9c6SJames Smart 		mode = bf_get(lpfc_event_log, rdEventLog);
4788c7495937SJames Smart 
4789c7495937SJames Smart 		/* receive length cannot be greater than mailbox
4790c7495937SJames Smart 		 * extension size
4791c7495937SJames Smart 		 */
479288a2cfbbSJames Smart 		if (receive_length > BSG_MBOX_SIZE - sizeof(MAILBOX_t)) {
4793c7495937SJames Smart 			rc = -ERANGE;
4794c7495937SJames Smart 			goto job_done;
4795c7495937SJames Smart 		}
4796c7495937SJames Smart 
4797c7495937SJames Smart 		/* mode zero uses a bde like biu diags command */
4798c7495937SJames Smart 		if (mode == 0) {
47997ad20aa9SJames Smart 			pmb->un.varWords[3] = putPaddrLow(dmabuf->phys
48007ad20aa9SJames Smart 							+ sizeof(MAILBOX_t));
48017ad20aa9SJames Smart 			pmb->un.varWords[4] = putPaddrHigh(dmabuf->phys
48027ad20aa9SJames Smart 							+ sizeof(MAILBOX_t));
4803c7495937SJames Smart 		}
4804c7495937SJames Smart 	} else if (phba->sli_rev == LPFC_SLI_REV4) {
48053ef6d24cSJames Smart 		/* Let type 4 (well known data) through because the data is
48063ef6d24cSJames Smart 		 * returned in varwords[4-8]
48073ef6d24cSJames Smart 		 * otherwise check the recieve length and fetch the buffer addr
48083ef6d24cSJames Smart 		 */
48093ef6d24cSJames Smart 		if ((pmb->mbxCommand == MBX_DUMP_MEMORY) &&
48103ef6d24cSJames Smart 			(pmb->un.varDmp.type != DMP_WELL_KNOWN)) {
4811c7495937SJames Smart 			/* rebuild the command for sli4 using our own buffers
4812c7495937SJames Smart 			* like we do for biu diags
4813c7495937SJames Smart 			*/
4814b6e3b9c6SJames Smart 			receive_length = pmb->un.varWords[2];
4815c7495937SJames Smart 			/* receive length cannot be greater than mailbox
4816c7495937SJames Smart 			 * extension size
4817c7495937SJames Smart 			 */
48187ad20aa9SJames Smart 			if (receive_length == 0) {
4819c7495937SJames Smart 				rc = -ERANGE;
4820c7495937SJames Smart 				goto job_done;
4821c7495937SJames Smart 			}
48227ad20aa9SJames Smart 			pmb->un.varWords[3] = putPaddrLow(dmabuf->phys
48237ad20aa9SJames Smart 						+ sizeof(MAILBOX_t));
48247ad20aa9SJames Smart 			pmb->un.varWords[4] = putPaddrHigh(dmabuf->phys
48257ad20aa9SJames Smart 						+ sizeof(MAILBOX_t));
4826c7495937SJames Smart 		} else if ((pmb->mbxCommand == MBX_UPDATE_CFG) &&
4827c7495937SJames Smart 			pmb->un.varUpdateCfg.co) {
4828b6e3b9c6SJames Smart 			bde = (struct ulp_bde64 *)&pmb->un.varWords[4];
4829c7495937SJames Smart 
4830c7495937SJames Smart 			/* bde size cannot be greater than mailbox ext size */
483188a2cfbbSJames Smart 			if (bde->tus.f.bdeSize >
483288a2cfbbSJames Smart 			    BSG_MBOX_SIZE - sizeof(MAILBOX_t)) {
4833c7495937SJames Smart 				rc = -ERANGE;
4834c7495937SJames Smart 				goto job_done;
4835c7495937SJames Smart 			}
48367ad20aa9SJames Smart 			bde->addrHigh = putPaddrHigh(dmabuf->phys
48377ad20aa9SJames Smart 						+ sizeof(MAILBOX_t));
48387ad20aa9SJames Smart 			bde->addrLow = putPaddrLow(dmabuf->phys
48397ad20aa9SJames Smart 						+ sizeof(MAILBOX_t));
4840515e0aa2SJames Smart 		} else if (pmb->mbxCommand == MBX_SLI4_CONFIG) {
48417ad20aa9SJames Smart 			/* Handling non-embedded SLI_CONFIG mailbox command */
48427ad20aa9SJames Smart 			sli4_config = &pmboxq->u.mqe.un.sli4_config;
48437ad20aa9SJames Smart 			if (!bf_get(lpfc_mbox_hdr_emb,
48447ad20aa9SJames Smart 			    &sli4_config->header.cfg_mhdr)) {
48457ad20aa9SJames Smart 				/* rebuild the command for sli4 using our
48467ad20aa9SJames Smart 				 * own buffers like we do for biu diags
4847515e0aa2SJames Smart 				 */
4848515e0aa2SJames Smart 				nembed_sge = (struct lpfc_mbx_nembed_cmd *)
4849515e0aa2SJames Smart 						&pmb->un.varWords[0];
4850515e0aa2SJames Smart 				receive_length = nembed_sge->sge[0].length;
4851515e0aa2SJames Smart 
48527ad20aa9SJames Smart 				/* receive length cannot be greater than
48537ad20aa9SJames Smart 				 * mailbox extension size
4854515e0aa2SJames Smart 				 */
4855515e0aa2SJames Smart 				if ((receive_length == 0) ||
485688a2cfbbSJames Smart 				    (receive_length >
485788a2cfbbSJames Smart 				     BSG_MBOX_SIZE - sizeof(MAILBOX_t))) {
4858515e0aa2SJames Smart 					rc = -ERANGE;
4859515e0aa2SJames Smart 					goto job_done;
4860515e0aa2SJames Smart 				}
4861515e0aa2SJames Smart 
48627ad20aa9SJames Smart 				nembed_sge->sge[0].pa_hi =
48637ad20aa9SJames Smart 						putPaddrHigh(dmabuf->phys
48647ad20aa9SJames Smart 						   + sizeof(MAILBOX_t));
48657ad20aa9SJames Smart 				nembed_sge->sge[0].pa_lo =
48667ad20aa9SJames Smart 						putPaddrLow(dmabuf->phys
48677ad20aa9SJames Smart 						   + sizeof(MAILBOX_t));
4868515e0aa2SJames Smart 			}
4869c7495937SJames Smart 		}
4870c7495937SJames Smart 	}
4871c7495937SJames Smart 
48727ad20aa9SJames Smart 	dd_data->context_un.mbox.dmabuffers = dmabuf;
48733b5dd52aSJames Smart 
48743b5dd52aSJames Smart 	/* setup wake call as IOCB callback */
48757ad20aa9SJames Smart 	pmboxq->mbox_cmpl = lpfc_bsg_issue_mbox_cmpl;
48767a470277SJames Smart 
48773b5dd52aSJames Smart 	/* setup context field to pass wait_queue pointer to wake function */
487885d77f91SJustin Tee 	pmboxq->ctx_u.dd_data = dd_data;
48793b5dd52aSJames Smart 	dd_data->type = TYPE_MBOX;
4880a33c4f7bSJames Smart 	dd_data->set_job = job;
48813b5dd52aSJames Smart 	dd_data->context_un.mbox.pmboxq = pmboxq;
48827ad20aa9SJames Smart 	dd_data->context_un.mbox.mb = (MAILBOX_t *)pmbx;
48837a470277SJames Smart 	dd_data->context_un.mbox.ext = ext;
48847a470277SJames Smart 	dd_data->context_un.mbox.mbOffset = mbox_req->mbOffset;
48857a470277SJames Smart 	dd_data->context_un.mbox.inExtWLen = mbox_req->inExtWLen;
4886c7495937SJames Smart 	dd_data->context_un.mbox.outExtWLen = mbox_req->outExtWLen;
48873b5dd52aSJames Smart 	job->dd_data = dd_data;
48887a470277SJames Smart 
4889a645b8c1SJustin Tee 	if (test_bit(FC_OFFLINE_MODE, &vport->fc_flag) ||
48907a470277SJames Smart 	    (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE))) {
48917a470277SJames Smart 		rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL);
48927a470277SJames Smart 		if (rc != MBX_SUCCESS) {
48937a470277SJames Smart 			rc = (rc == MBX_TIMEOUT) ? -ETIME : -ENODEV;
48947a470277SJames Smart 			goto job_done;
48953b5dd52aSJames Smart 		}
48963b5dd52aSJames Smart 
48977a470277SJames Smart 		/* job finished, copy the data */
48987ad20aa9SJames Smart 		memcpy(pmbx, pmb, sizeof(*pmb));
489901e0e15cSJohannes Thumshirn 		bsg_reply->reply_payload_rcv_len =
49007a470277SJames Smart 			sg_copy_from_buffer(job->reply_payload.sg_list,
49017a470277SJames Smart 					    job->reply_payload.sg_cnt,
49027ad20aa9SJames Smart 					    pmbx, size);
49037a470277SJames Smart 		/* not waiting mbox already done */
49047a470277SJames Smart 		rc = 0;
49057a470277SJames Smart 		goto job_done;
49067a470277SJames Smart 	}
49077a470277SJames Smart 
49087a470277SJames Smart 	rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT);
49097a470277SJames Smart 	if ((rc == MBX_SUCCESS) || (rc == MBX_BUSY))
49107a470277SJames Smart 		return 1; /* job started */
49117a470277SJames Smart 
49127a470277SJames Smart job_done:
49137a470277SJames Smart 	/* common exit for error or job completed inline */
49147a470277SJames Smart 	if (pmboxq)
49157a470277SJames Smart 		mempool_free(pmboxq, phba->mbox_mem_pool);
49167ad20aa9SJames Smart 	lpfc_bsg_dma_page_free(phba, dmabuf);
49177a470277SJames Smart 	kfree(dd_data);
49187a470277SJames Smart 
49197ad20aa9SJames Smart job_cont:
49207a470277SJames Smart 	return rc;
49213b5dd52aSJames Smart }
49223b5dd52aSJames Smart 
49233b5dd52aSJames Smart /**
49243b5dd52aSJames Smart  * lpfc_bsg_mbox_cmd - process an fc bsg LPFC_BSG_VENDOR_MBOX command
49253b5dd52aSJames Smart  * @job: MBOX fc_bsg_job for LPFC_BSG_VENDOR_MBOX.
49263b5dd52aSJames Smart  **/
49273b5dd52aSJames Smart static int
492875cc8cfcSJohannes Thumshirn lpfc_bsg_mbox_cmd(struct bsg_job *job)
49293b5dd52aSJames Smart {
4930cd21c605SJohannes Thumshirn 	struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
493101e0e15cSJohannes Thumshirn 	struct fc_bsg_request *bsg_request = job->request;
493201e0e15cSJohannes Thumshirn 	struct fc_bsg_reply *bsg_reply = job->reply;
49333b5dd52aSJames Smart 	struct lpfc_hba *phba = vport->phba;
49347ad20aa9SJames Smart 	struct dfc_mbox_req *mbox_req;
49353b5dd52aSJames Smart 	int rc = 0;
49363b5dd52aSJames Smart 
49377ad20aa9SJames Smart 	/* mix-and-match backward compatibility */
493801e0e15cSJohannes Thumshirn 	bsg_reply->reply_payload_rcv_len = 0;
49393b5dd52aSJames Smart 	if (job->request_len <
49403b5dd52aSJames Smart 	    sizeof(struct fc_bsg_request) + sizeof(struct dfc_mbox_req)) {
49417ad20aa9SJames Smart 		lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
49421051e9b3SMasanari Iida 				"2737 Mix-and-match backward compatibility "
49437ad20aa9SJames Smart 				"between MBOX_REQ old size:%d and "
49447ad20aa9SJames Smart 				"new request size:%d\n",
49457ad20aa9SJames Smart 				(int)(job->request_len -
49467ad20aa9SJames Smart 				      sizeof(struct fc_bsg_request)),
49477ad20aa9SJames Smart 				(int)sizeof(struct dfc_mbox_req));
49487ad20aa9SJames Smart 		mbox_req = (struct dfc_mbox_req *)
494901e0e15cSJohannes Thumshirn 				bsg_request->rqst_data.h_vendor.vendor_cmd;
49507ad20aa9SJames Smart 		mbox_req->extMboxTag = 0;
49517ad20aa9SJames Smart 		mbox_req->extSeqNum = 0;
49523b5dd52aSJames Smart 	}
49533b5dd52aSJames Smart 
49543b5dd52aSJames Smart 	rc = lpfc_bsg_issue_mbox(phba, job, vport);
49553b5dd52aSJames Smart 
49563b5dd52aSJames Smart 	if (rc == 0) {
49573b5dd52aSJames Smart 		/* job done */
495801e0e15cSJohannes Thumshirn 		bsg_reply->result = 0;
49593b5dd52aSJames Smart 		job->dd_data = NULL;
496006548160SJohannes Thumshirn 		bsg_job_done(job, bsg_reply->result,
49611abaede7SJohannes Thumshirn 			       bsg_reply->reply_payload_rcv_len);
49623b5dd52aSJames Smart 	} else if (rc == 1)
49633b5dd52aSJames Smart 		/* job submitted, will complete later*/
49643b5dd52aSJames Smart 		rc = 0; /* return zero, no error */
49653b5dd52aSJames Smart 	else {
49663b5dd52aSJames Smart 		/* some error occurred */
496701e0e15cSJohannes Thumshirn 		bsg_reply->result = rc;
49683b5dd52aSJames Smart 		job->dd_data = NULL;
49693b5dd52aSJames Smart 	}
49703b5dd52aSJames Smart 
49713b5dd52aSJames Smart 	return rc;
49723b5dd52aSJames Smart }
49733b5dd52aSJames Smart 
4974c691816eSJames Smart static int
497575cc8cfcSJohannes Thumshirn lpfc_forced_link_speed(struct bsg_job *job)
4976c691816eSJames Smart {
4977cd21c605SJohannes Thumshirn 	struct Scsi_Host *shost = fc_bsg_to_shost(job);
4978c691816eSJames Smart 	struct lpfc_vport *vport = shost_priv(shost);
4979c691816eSJames Smart 	struct lpfc_hba *phba = vport->phba;
498001e0e15cSJohannes Thumshirn 	struct fc_bsg_reply *bsg_reply = job->reply;
4981c691816eSJames Smart 	struct forced_link_speed_support_reply *forced_reply;
4982c691816eSJames Smart 	int rc = 0;
4983c691816eSJames Smart 
4984c691816eSJames Smart 	if (job->request_len <
4985c691816eSJames Smart 	    sizeof(struct fc_bsg_request) +
4986c691816eSJames Smart 	    sizeof(struct get_forced_link_speed_support)) {
4987c691816eSJames Smart 		lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
4988c691816eSJames Smart 				"0048 Received FORCED_LINK_SPEED request "
4989c691816eSJames Smart 				"below minimum size\n");
4990c691816eSJames Smart 		rc = -EINVAL;
4991c691816eSJames Smart 		goto job_error;
4992c691816eSJames Smart 	}
4993c691816eSJames Smart 
4994c691816eSJames Smart 	forced_reply = (struct forced_link_speed_support_reply *)
499501e0e15cSJohannes Thumshirn 		bsg_reply->reply_data.vendor_reply.vendor_rsp;
4996c691816eSJames Smart 
4997feb3cc57SDick Kennedy 	if (job->reply_len < sizeof(*bsg_reply) + sizeof(*forced_reply)) {
4998c691816eSJames Smart 		lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
4999c691816eSJames Smart 				"0049 Received FORCED_LINK_SPEED reply below "
5000c691816eSJames Smart 				"minimum size\n");
5001c691816eSJames Smart 		rc = -EINVAL;
5002c691816eSJames Smart 		goto job_error;
5003c691816eSJames Smart 	}
5004c691816eSJames Smart 
5005e780c942SJustin Tee 	forced_reply->supported = test_bit(HBA_FORCED_LINK_SPEED,
5006e780c942SJustin Tee 					   &phba->hba_flag)
5007c691816eSJames Smart 				   ? LPFC_FORCED_LINK_SPEED_SUPPORTED
5008c691816eSJames Smart 				   : LPFC_FORCED_LINK_SPEED_NOT_SUPPORTED;
5009c691816eSJames Smart job_error:
501001e0e15cSJohannes Thumshirn 	bsg_reply->result = rc;
5011c691816eSJames Smart 	if (rc == 0)
501206548160SJohannes Thumshirn 		bsg_job_done(job, bsg_reply->result,
50131abaede7SJohannes Thumshirn 			       bsg_reply->reply_payload_rcv_len);
5014c691816eSJames Smart 	return rc;
5015c691816eSJames Smart }
5016c691816eSJames Smart 
5017e2aed29fSJames Smart /**
5018d2cc9bcdSJames Smart  * lpfc_check_fwlog_support: Check FW log support on the adapter
5019d2cc9bcdSJames Smart  * @phba: Pointer to HBA context object.
5020d2cc9bcdSJames Smart  *
5021d2cc9bcdSJames Smart  * Check if FW Logging support by the adapter
5022d2cc9bcdSJames Smart  **/
5023d2cc9bcdSJames Smart int
5024d2cc9bcdSJames Smart lpfc_check_fwlog_support(struct lpfc_hba *phba)
5025d2cc9bcdSJames Smart {
5026d2cc9bcdSJames Smart 	struct lpfc_ras_fwlog *ras_fwlog = NULL;
5027d2cc9bcdSJames Smart 
5028d2cc9bcdSJames Smart 	ras_fwlog = &phba->ras_fwlog;
5029d2cc9bcdSJames Smart 
5030af0c94afSYANG LI 	if (!ras_fwlog->ras_hwsupport)
5031d2cc9bcdSJames Smart 		return -EACCES;
5032af0c94afSYANG LI 	else if (!ras_fwlog->ras_enabled)
5033d2cc9bcdSJames Smart 		return -EPERM;
5034d2cc9bcdSJames Smart 	else
5035d2cc9bcdSJames Smart 		return 0;
5036d2cc9bcdSJames Smart }
5037d2cc9bcdSJames Smart 
5038d2cc9bcdSJames Smart /**
5039d2cc9bcdSJames Smart  * lpfc_bsg_get_ras_config: Get RAS configuration settings
5040d2cc9bcdSJames Smart  * @job: fc_bsg_job to handle
5041d2cc9bcdSJames Smart  *
5042d2cc9bcdSJames Smart  * Get RAS configuration values set.
5043d2cc9bcdSJames Smart  **/
5044d2cc9bcdSJames Smart static int
5045d2cc9bcdSJames Smart lpfc_bsg_get_ras_config(struct bsg_job *job)
5046d2cc9bcdSJames Smart {
5047d2cc9bcdSJames Smart 	struct Scsi_Host *shost = fc_bsg_to_shost(job);
5048d2cc9bcdSJames Smart 	struct lpfc_vport *vport = shost_priv(shost);
5049d2cc9bcdSJames Smart 	struct fc_bsg_reply *bsg_reply = job->reply;
5050d2cc9bcdSJames Smart 	struct lpfc_hba *phba = vport->phba;
5051d2cc9bcdSJames Smart 	struct lpfc_bsg_get_ras_config_reply *ras_reply;
5052d2cc9bcdSJames Smart 	struct lpfc_ras_fwlog *ras_fwlog = &phba->ras_fwlog;
5053d2cc9bcdSJames Smart 	int rc = 0;
5054d2cc9bcdSJames Smart 
5055d2cc9bcdSJames Smart 	if (job->request_len <
5056d2cc9bcdSJames Smart 	    sizeof(struct fc_bsg_request) +
5057d2cc9bcdSJames Smart 	    sizeof(struct lpfc_bsg_ras_req)) {
5058d2cc9bcdSJames Smart 		lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
5059cb34990bSJames Smart 				"6192 FW_LOG request received "
5060d2cc9bcdSJames Smart 				"below minimum size\n");
5061d2cc9bcdSJames Smart 		rc = -EINVAL;
5062d2cc9bcdSJames Smart 		goto ras_job_error;
5063d2cc9bcdSJames Smart 	}
5064d2cc9bcdSJames Smart 
5065d2cc9bcdSJames Smart 	/* Check FW log status */
5066d2cc9bcdSJames Smart 	rc = lpfc_check_fwlog_support(phba);
5067cb34990bSJames Smart 	if (rc)
5068d2cc9bcdSJames Smart 		goto ras_job_error;
5069d2cc9bcdSJames Smart 
5070d2cc9bcdSJames Smart 	ras_reply = (struct lpfc_bsg_get_ras_config_reply *)
5071d2cc9bcdSJames Smart 		bsg_reply->reply_data.vendor_reply.vendor_rsp;
5072d2cc9bcdSJames Smart 
5073d2cc9bcdSJames Smart 	/* Current logging state */
5074f733a76eSJustin Tee 	spin_lock_irq(&phba->ras_fwlog_lock);
507595bfc6d8SJames Smart 	if (ras_fwlog->state == ACTIVE)
5076d2cc9bcdSJames Smart 		ras_reply->state = LPFC_RASLOG_STATE_RUNNING;
5077d2cc9bcdSJames Smart 	else
5078d2cc9bcdSJames Smart 		ras_reply->state = LPFC_RASLOG_STATE_STOPPED;
5079f733a76eSJustin Tee 	spin_unlock_irq(&phba->ras_fwlog_lock);
5080d2cc9bcdSJames Smart 
5081d2cc9bcdSJames Smart 	ras_reply->log_level = phba->ras_fwlog.fw_loglevel;
5082d2cc9bcdSJames Smart 	ras_reply->log_buff_sz = phba->cfg_ras_fwlog_buffsize;
5083d2cc9bcdSJames Smart 
5084d2cc9bcdSJames Smart ras_job_error:
5085d2cc9bcdSJames Smart 	/* make error code available to userspace */
5086d2cc9bcdSJames Smart 	bsg_reply->result = rc;
5087d2cc9bcdSJames Smart 
5088d2cc9bcdSJames Smart 	/* complete the job back to userspace */
50896db51abbSJames Smart 	if (!rc)
50906db51abbSJames Smart 		bsg_job_done(job, bsg_reply->result,
50916db51abbSJames Smart 			     bsg_reply->reply_payload_rcv_len);
5092d2cc9bcdSJames Smart 	return rc;
5093d2cc9bcdSJames Smart }
5094d2cc9bcdSJames Smart 
5095d2cc9bcdSJames Smart /**
5096d2cc9bcdSJames Smart  * lpfc_bsg_set_ras_config: Set FW logging parameters
5097d2cc9bcdSJames Smart  * @job: fc_bsg_job to handle
5098d2cc9bcdSJames Smart  *
5099d2cc9bcdSJames Smart  * Set log-level parameters for FW-logging in host memory
5100d2cc9bcdSJames Smart  **/
5101d2cc9bcdSJames Smart static int
5102d2cc9bcdSJames Smart lpfc_bsg_set_ras_config(struct bsg_job *job)
5103d2cc9bcdSJames Smart {
5104d2cc9bcdSJames Smart 	struct Scsi_Host *shost = fc_bsg_to_shost(job);
5105d2cc9bcdSJames Smart 	struct lpfc_vport *vport = shost_priv(shost);
5106d2cc9bcdSJames Smart 	struct lpfc_hba *phba = vport->phba;
5107d2cc9bcdSJames Smart 	struct lpfc_bsg_set_ras_config_req *ras_req;
5108d2cc9bcdSJames Smart 	struct fc_bsg_request *bsg_request = job->request;
5109d2cc9bcdSJames Smart 	struct lpfc_ras_fwlog *ras_fwlog = &phba->ras_fwlog;
5110d2cc9bcdSJames Smart 	struct fc_bsg_reply *bsg_reply = job->reply;
5111d2cc9bcdSJames Smart 	uint8_t action = 0, log_level = 0;
5112191e2f74SJames Smart 	int rc = 0, action_status = 0;
5113d2cc9bcdSJames Smart 
5114d2cc9bcdSJames Smart 	if (job->request_len <
5115d2cc9bcdSJames Smart 	    sizeof(struct fc_bsg_request) +
5116d2cc9bcdSJames Smart 	    sizeof(struct lpfc_bsg_set_ras_config_req)) {
5117d2cc9bcdSJames Smart 		lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
5118d2cc9bcdSJames Smart 				"6182 Received RAS_LOG request "
5119d2cc9bcdSJames Smart 				"below minimum size\n");
5120d2cc9bcdSJames Smart 		rc = -EINVAL;
5121d2cc9bcdSJames Smart 		goto ras_job_error;
5122d2cc9bcdSJames Smart 	}
5123d2cc9bcdSJames Smart 
5124d2cc9bcdSJames Smart 	/* Check FW log status */
5125d2cc9bcdSJames Smart 	rc = lpfc_check_fwlog_support(phba);
5126cb34990bSJames Smart 	if (rc)
5127d2cc9bcdSJames Smart 		goto ras_job_error;
5128d2cc9bcdSJames Smart 
5129d2cc9bcdSJames Smart 	ras_req = (struct lpfc_bsg_set_ras_config_req *)
5130d2cc9bcdSJames Smart 		bsg_request->rqst_data.h_vendor.vendor_cmd;
5131d2cc9bcdSJames Smart 	action = ras_req->action;
5132d2cc9bcdSJames Smart 	log_level = ras_req->log_level;
5133d2cc9bcdSJames Smart 
5134d2cc9bcdSJames Smart 	if (action == LPFC_RASACTION_STOP_LOGGING) {
5135d2cc9bcdSJames Smart 		/* Check if already disabled */
5136f733a76eSJustin Tee 		spin_lock_irq(&phba->ras_fwlog_lock);
513795bfc6d8SJames Smart 		if (ras_fwlog->state != ACTIVE) {
5138f733a76eSJustin Tee 			spin_unlock_irq(&phba->ras_fwlog_lock);
5139d2cc9bcdSJames Smart 			rc = -ESRCH;
5140d2cc9bcdSJames Smart 			goto ras_job_error;
5141d2cc9bcdSJames Smart 		}
5142f733a76eSJustin Tee 		spin_unlock_irq(&phba->ras_fwlog_lock);
5143d2cc9bcdSJames Smart 
5144d2cc9bcdSJames Smart 		/* Disable logging */
5145d2cc9bcdSJames Smart 		lpfc_ras_stop_fwlog(phba);
5146d2cc9bcdSJames Smart 	} else {
5147d2cc9bcdSJames Smart 		/*action = LPFC_RASACTION_START_LOGGING*/
5148191e2f74SJames Smart 
5149191e2f74SJames Smart 		/* Even though FW-logging is active re-initialize
5150191e2f74SJames Smart 		 * FW-logging with new log-level. Return status
5151191e2f74SJames Smart 		 * "Logging already Running" to caller.
5152191e2f74SJames Smart 		 **/
5153f733a76eSJustin Tee 		spin_lock_irq(&phba->ras_fwlog_lock);
515495bfc6d8SJames Smart 		if (ras_fwlog->state != INACTIVE)
5155191e2f74SJames Smart 			action_status = -EINPROGRESS;
5156f733a76eSJustin Tee 		spin_unlock_irq(&phba->ras_fwlog_lock);
5157d2cc9bcdSJames Smart 
5158d2cc9bcdSJames Smart 		/* Enable logging */
5159d2cc9bcdSJames Smart 		rc = lpfc_sli4_ras_fwlog_init(phba, log_level,
5160d2cc9bcdSJames Smart 					      LPFC_RAS_ENABLE_LOGGING);
5161191e2f74SJames Smart 		if (rc) {
5162d2cc9bcdSJames Smart 			rc = -EINVAL;
5163191e2f74SJames Smart 			goto ras_job_error;
5164191e2f74SJames Smart 		}
5165191e2f74SJames Smart 
5166191e2f74SJames Smart 		/* Check if FW-logging is re-initialized */
5167191e2f74SJames Smart 		if (action_status == -EINPROGRESS)
5168191e2f74SJames Smart 			rc = action_status;
5169d2cc9bcdSJames Smart 	}
5170d2cc9bcdSJames Smart ras_job_error:
5171d2cc9bcdSJames Smart 	/* make error code available to userspace */
5172d2cc9bcdSJames Smart 	bsg_reply->result = rc;
5173d2cc9bcdSJames Smart 
5174d2cc9bcdSJames Smart 	/* complete the job back to userspace */
51756db51abbSJames Smart 	if (!rc)
5176d2cc9bcdSJames Smart 		bsg_job_done(job, bsg_reply->result,
5177d2cc9bcdSJames Smart 			     bsg_reply->reply_payload_rcv_len);
5178d2cc9bcdSJames Smart 
5179d2cc9bcdSJames Smart 	return rc;
5180d2cc9bcdSJames Smart }
5181d2cc9bcdSJames Smart 
5182d2cc9bcdSJames Smart /**
5183d2cc9bcdSJames Smart  * lpfc_bsg_get_ras_lwpd: Get log write position data
5184d2cc9bcdSJames Smart  * @job: fc_bsg_job to handle
5185d2cc9bcdSJames Smart  *
5186d2cc9bcdSJames Smart  * Get Offset/Wrap count of the log message written
5187d2cc9bcdSJames Smart  * in host memory
5188d2cc9bcdSJames Smart  **/
5189d2cc9bcdSJames Smart static int
5190d2cc9bcdSJames Smart lpfc_bsg_get_ras_lwpd(struct bsg_job *job)
5191d2cc9bcdSJames Smart {
5192d2cc9bcdSJames Smart 	struct Scsi_Host *shost = fc_bsg_to_shost(job);
5193d2cc9bcdSJames Smart 	struct lpfc_vport *vport = shost_priv(shost);
5194d2cc9bcdSJames Smart 	struct lpfc_bsg_get_ras_lwpd *ras_reply;
5195d2cc9bcdSJames Smart 	struct lpfc_hba *phba = vport->phba;
5196d2cc9bcdSJames Smart 	struct lpfc_ras_fwlog *ras_fwlog = &phba->ras_fwlog;
5197d2cc9bcdSJames Smart 	struct fc_bsg_reply *bsg_reply = job->reply;
5198191e2f74SJames Smart 	u32 *lwpd_ptr = NULL;
5199d2cc9bcdSJames Smart 	int rc = 0;
5200d2cc9bcdSJames Smart 
5201d2cc9bcdSJames Smart 	rc = lpfc_check_fwlog_support(phba);
5202cb34990bSJames Smart 	if (rc)
5203d2cc9bcdSJames Smart 		goto ras_job_error;
5204d2cc9bcdSJames Smart 
5205d2cc9bcdSJames Smart 	if (job->request_len <
5206d2cc9bcdSJames Smart 	    sizeof(struct fc_bsg_request) +
5207d2cc9bcdSJames Smart 	    sizeof(struct lpfc_bsg_ras_req)) {
5208d2cc9bcdSJames Smart 		lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
5209d2cc9bcdSJames Smart 				"6183 Received RAS_LOG request "
5210d2cc9bcdSJames Smart 				"below minimum size\n");
5211d2cc9bcdSJames Smart 		rc = -EINVAL;
5212d2cc9bcdSJames Smart 		goto ras_job_error;
5213d2cc9bcdSJames Smart 	}
5214d2cc9bcdSJames Smart 
5215d2cc9bcdSJames Smart 	ras_reply = (struct lpfc_bsg_get_ras_lwpd *)
5216d2cc9bcdSJames Smart 		bsg_reply->reply_data.vendor_reply.vendor_rsp;
5217d2cc9bcdSJames Smart 
5218cb34990bSJames Smart 	if (!ras_fwlog->lwpd.virt) {
5219cb34990bSJames Smart 		lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
5220cb34990bSJames Smart 				"6193 Restart FW Logging\n");
52211165a5c2SJames Smart 		rc = -EINVAL;
52221165a5c2SJames Smart 		goto ras_job_error;
5223cb34990bSJames Smart 	}
5224cb34990bSJames Smart 
5225191e2f74SJames Smart 	/* Get lwpd offset */
5226191e2f74SJames Smart 	lwpd_ptr = (uint32_t *)(ras_fwlog->lwpd.virt);
5227191e2f74SJames Smart 	ras_reply->offset = be32_to_cpu(*lwpd_ptr & 0xffffffff);
5228d2cc9bcdSJames Smart 
5229191e2f74SJames Smart 	/* Get wrap count */
5230191e2f74SJames Smart 	ras_reply->wrap_count = be32_to_cpu(*(++lwpd_ptr) & 0xffffffff);
5231d2cc9bcdSJames Smart 
5232d2cc9bcdSJames Smart ras_job_error:
5233d2cc9bcdSJames Smart 	/* make error code available to userspace */
5234d2cc9bcdSJames Smart 	bsg_reply->result = rc;
5235d2cc9bcdSJames Smart 
5236d2cc9bcdSJames Smart 	/* complete the job back to userspace */
52376db51abbSJames Smart 	if (!rc)
52386db51abbSJames Smart 		bsg_job_done(job, bsg_reply->result,
52396db51abbSJames Smart 			     bsg_reply->reply_payload_rcv_len);
5240d2cc9bcdSJames Smart 
5241d2cc9bcdSJames Smart 	return rc;
5242d2cc9bcdSJames Smart }
5243d2cc9bcdSJames Smart 
5244d2cc9bcdSJames Smart /**
5245d2cc9bcdSJames Smart  * lpfc_bsg_get_ras_fwlog: Read FW log
5246d2cc9bcdSJames Smart  * @job: fc_bsg_job to handle
5247d2cc9bcdSJames Smart  *
5248d2cc9bcdSJames Smart  * Copy the FW log into the passed buffer.
5249d2cc9bcdSJames Smart  **/
5250d2cc9bcdSJames Smart static int
5251d2cc9bcdSJames Smart lpfc_bsg_get_ras_fwlog(struct bsg_job *job)
5252d2cc9bcdSJames Smart {
5253d2cc9bcdSJames Smart 	struct Scsi_Host *shost = fc_bsg_to_shost(job);
5254d2cc9bcdSJames Smart 	struct lpfc_vport *vport = shost_priv(shost);
5255d2cc9bcdSJames Smart 	struct lpfc_hba *phba = vport->phba;
5256d2cc9bcdSJames Smart 	struct fc_bsg_request *bsg_request = job->request;
5257d2cc9bcdSJames Smart 	struct fc_bsg_reply *bsg_reply = job->reply;
5258d2cc9bcdSJames Smart 	struct lpfc_bsg_get_fwlog_req *ras_req;
5259191e2f74SJames Smart 	u32 rd_offset, rd_index, offset;
5260191e2f74SJames Smart 	void *src, *fwlog_buff;
5261d2cc9bcdSJames Smart 	struct lpfc_ras_fwlog *ras_fwlog = NULL;
5262d2cc9bcdSJames Smart 	struct lpfc_dmabuf *dmabuf, *next;
5263d2cc9bcdSJames Smart 	int rc = 0;
5264d2cc9bcdSJames Smart 
5265d2cc9bcdSJames Smart 	ras_fwlog = &phba->ras_fwlog;
5266d2cc9bcdSJames Smart 
5267d2cc9bcdSJames Smart 	rc = lpfc_check_fwlog_support(phba);
5268cb34990bSJames Smart 	if (rc)
5269d2cc9bcdSJames Smart 		goto ras_job_error;
5270d2cc9bcdSJames Smart 
5271d2cc9bcdSJames Smart 	/* Logging to be stopped before reading */
5272f733a76eSJustin Tee 	spin_lock_irq(&phba->ras_fwlog_lock);
527395bfc6d8SJames Smart 	if (ras_fwlog->state == ACTIVE) {
5274f733a76eSJustin Tee 		spin_unlock_irq(&phba->ras_fwlog_lock);
5275d2cc9bcdSJames Smart 		rc = -EINPROGRESS;
5276d2cc9bcdSJames Smart 		goto ras_job_error;
5277d2cc9bcdSJames Smart 	}
5278f733a76eSJustin Tee 	spin_unlock_irq(&phba->ras_fwlog_lock);
5279d2cc9bcdSJames Smart 
5280d2cc9bcdSJames Smart 	if (job->request_len <
5281d2cc9bcdSJames Smart 	    sizeof(struct fc_bsg_request) +
5282d2cc9bcdSJames Smart 	    sizeof(struct lpfc_bsg_get_fwlog_req)) {
5283d2cc9bcdSJames Smart 		lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
5284d2cc9bcdSJames Smart 				"6184 Received RAS_LOG request "
5285d2cc9bcdSJames Smart 				"below minimum size\n");
5286d2cc9bcdSJames Smart 		rc = -EINVAL;
5287d2cc9bcdSJames Smart 		goto ras_job_error;
5288d2cc9bcdSJames Smart 	}
5289d2cc9bcdSJames Smart 
5290d2cc9bcdSJames Smart 	ras_req = (struct lpfc_bsg_get_fwlog_req *)
5291d2cc9bcdSJames Smart 		bsg_request->rqst_data.h_vendor.vendor_cmd;
5292d2cc9bcdSJames Smart 	rd_offset = ras_req->read_offset;
5293d2cc9bcdSJames Smart 
5294d2cc9bcdSJames Smart 	/* Allocate memory to read fw log*/
5295d2cc9bcdSJames Smart 	fwlog_buff = vmalloc(ras_req->read_size);
5296d2cc9bcdSJames Smart 	if (!fwlog_buff) {
5297d2cc9bcdSJames Smart 		rc = -ENOMEM;
5298d2cc9bcdSJames Smart 		goto ras_job_error;
5299d2cc9bcdSJames Smart 	}
5300d2cc9bcdSJames Smart 
5301d2cc9bcdSJames Smart 	rd_index = (rd_offset / LPFC_RAS_MAX_ENTRY_SIZE);
5302d2cc9bcdSJames Smart 	offset = (rd_offset % LPFC_RAS_MAX_ENTRY_SIZE);
5303d2cc9bcdSJames Smart 
5304d2cc9bcdSJames Smart 	list_for_each_entry_safe(dmabuf, next,
5305d2cc9bcdSJames Smart 			      &ras_fwlog->fwlog_buff_list, list) {
5306d2cc9bcdSJames Smart 
5307d2cc9bcdSJames Smart 		if (dmabuf->buffer_tag < rd_index)
5308d2cc9bcdSJames Smart 			continue;
5309d2cc9bcdSJames Smart 
5310d2cc9bcdSJames Smart 		src = dmabuf->virt + offset;
5311191e2f74SJames Smart 		memcpy(fwlog_buff, src, ras_req->read_size);
5312d2cc9bcdSJames Smart 		break;
5313d2cc9bcdSJames Smart 	}
5314d2cc9bcdSJames Smart 
5315d2cc9bcdSJames Smart 	bsg_reply->reply_payload_rcv_len =
5316d2cc9bcdSJames Smart 		sg_copy_from_buffer(job->reply_payload.sg_list,
5317d2cc9bcdSJames Smart 				    job->reply_payload.sg_cnt,
5318d2cc9bcdSJames Smart 				    fwlog_buff, ras_req->read_size);
5319d2cc9bcdSJames Smart 
5320d2cc9bcdSJames Smart 	vfree(fwlog_buff);
5321d2cc9bcdSJames Smart 
5322d2cc9bcdSJames Smart ras_job_error:
5323d2cc9bcdSJames Smart 	bsg_reply->result = rc;
53246db51abbSJames Smart 	if (!rc)
53256db51abbSJames Smart 		bsg_job_done(job, bsg_reply->result,
53266db51abbSJames Smart 			     bsg_reply->reply_payload_rcv_len);
5327d2cc9bcdSJames Smart 
5328d2cc9bcdSJames Smart 	return rc;
5329d2cc9bcdSJames Smart }
5330d2cc9bcdSJames Smart 
53311dc5ec24SJames Smart static int
53321dc5ec24SJames Smart lpfc_get_trunk_info(struct bsg_job *job)
53331dc5ec24SJames Smart {
53341dc5ec24SJames Smart 	struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
53351dc5ec24SJames Smart 	struct lpfc_hba *phba = vport->phba;
53361dc5ec24SJames Smart 	struct fc_bsg_reply *bsg_reply = job->reply;
53371dc5ec24SJames Smart 	struct lpfc_trunk_info *event_reply;
53381dc5ec24SJames Smart 	int rc = 0;
53391dc5ec24SJames Smart 
53401dc5ec24SJames Smart 	if (job->request_len <
53411dc5ec24SJames Smart 	    sizeof(struct fc_bsg_request) + sizeof(struct get_trunk_info_req)) {
53421dc5ec24SJames Smart 		lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
53431dc5ec24SJames Smart 				"2744 Received GET TRUNK _INFO request below "
53441dc5ec24SJames Smart 				"minimum size\n");
53451dc5ec24SJames Smart 		rc = -EINVAL;
53461dc5ec24SJames Smart 		goto job_error;
53471dc5ec24SJames Smart 	}
53481dc5ec24SJames Smart 
53491dc5ec24SJames Smart 	event_reply = (struct lpfc_trunk_info *)
53501dc5ec24SJames Smart 		bsg_reply->reply_data.vendor_reply.vendor_rsp;
53511dc5ec24SJames Smart 
5352feb3cc57SDick Kennedy 	if (job->reply_len < sizeof(*bsg_reply) + sizeof(*event_reply)) {
53531dc5ec24SJames Smart 		lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
53541dc5ec24SJames Smart 				"2728 Received GET TRUNK _INFO reply below "
53551dc5ec24SJames Smart 				"minimum size\n");
53561dc5ec24SJames Smart 		rc = -EINVAL;
53571dc5ec24SJames Smart 		goto job_error;
53581dc5ec24SJames Smart 	}
53591dc5ec24SJames Smart 	if (event_reply == NULL) {
53601dc5ec24SJames Smart 		rc = -EINVAL;
53611dc5ec24SJames Smart 		goto job_error;
53621dc5ec24SJames Smart 	}
53631dc5ec24SJames Smart 
53641dc5ec24SJames Smart 	bsg_bf_set(lpfc_trunk_info_link_status, event_reply,
53651dc5ec24SJames Smart 		   (phba->link_state >= LPFC_LINK_UP) ? 1 : 0);
53661dc5ec24SJames Smart 
53671dc5ec24SJames Smart 	bsg_bf_set(lpfc_trunk_info_trunk_active0, event_reply,
53681dc5ec24SJames Smart 		   (phba->trunk_link.link0.state == LPFC_LINK_UP) ? 1 : 0);
53691dc5ec24SJames Smart 
53701dc5ec24SJames Smart 	bsg_bf_set(lpfc_trunk_info_trunk_active1, event_reply,
53711dc5ec24SJames Smart 		   (phba->trunk_link.link1.state == LPFC_LINK_UP) ? 1 : 0);
53721dc5ec24SJames Smart 
53731dc5ec24SJames Smart 	bsg_bf_set(lpfc_trunk_info_trunk_active2, event_reply,
53741dc5ec24SJames Smart 		   (phba->trunk_link.link2.state == LPFC_LINK_UP) ? 1 : 0);
53751dc5ec24SJames Smart 
53761dc5ec24SJames Smart 	bsg_bf_set(lpfc_trunk_info_trunk_active3, event_reply,
53771dc5ec24SJames Smart 		   (phba->trunk_link.link3.state == LPFC_LINK_UP) ? 1 : 0);
53781dc5ec24SJames Smart 
53791dc5ec24SJames Smart 	bsg_bf_set(lpfc_trunk_info_trunk_config0, event_reply,
53801dc5ec24SJames Smart 		   bf_get(lpfc_conf_trunk_port0, &phba->sli4_hba));
53811dc5ec24SJames Smart 
53821dc5ec24SJames Smart 	bsg_bf_set(lpfc_trunk_info_trunk_config1, event_reply,
53831dc5ec24SJames Smart 		   bf_get(lpfc_conf_trunk_port1, &phba->sli4_hba));
53841dc5ec24SJames Smart 
53851dc5ec24SJames Smart 	bsg_bf_set(lpfc_trunk_info_trunk_config2, event_reply,
53861dc5ec24SJames Smart 		   bf_get(lpfc_conf_trunk_port2, &phba->sli4_hba));
53871dc5ec24SJames Smart 
53881dc5ec24SJames Smart 	bsg_bf_set(lpfc_trunk_info_trunk_config3, event_reply,
53891dc5ec24SJames Smart 		   bf_get(lpfc_conf_trunk_port3, &phba->sli4_hba));
53901dc5ec24SJames Smart 
53911dc5ec24SJames Smart 	event_reply->port_speed = phba->sli4_hba.link_state.speed / 1000;
53921dc5ec24SJames Smart 	event_reply->logical_speed =
5393b8e6f136SJames Smart 				phba->sli4_hba.link_state.logical_speed / 1000;
53941dc5ec24SJames Smart job_error:
53951dc5ec24SJames Smart 	bsg_reply->result = rc;
53966db51abbSJames Smart 	if (!rc)
53971dc5ec24SJames Smart 		bsg_job_done(job, bsg_reply->result,
53981dc5ec24SJames Smart 			     bsg_reply->reply_payload_rcv_len);
53991dc5ec24SJames Smart 	return rc;
54001dc5ec24SJames Smart 
54011dc5ec24SJames Smart }
5402d2cc9bcdSJames Smart 
5403acbaa8c8SJames Smart static int
5404acbaa8c8SJames Smart lpfc_get_cgnbuf_info(struct bsg_job *job)
5405acbaa8c8SJames Smart {
5406acbaa8c8SJames Smart 	struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
5407acbaa8c8SJames Smart 	struct lpfc_hba *phba = vport->phba;
5408acbaa8c8SJames Smart 	struct fc_bsg_request *bsg_request = job->request;
5409acbaa8c8SJames Smart 	struct fc_bsg_reply *bsg_reply = job->reply;
5410acbaa8c8SJames Smart 	struct get_cgnbuf_info_req *cgnbuf_req;
5411acbaa8c8SJames Smart 	struct lpfc_cgn_info *cp;
5412acbaa8c8SJames Smart 	uint8_t *cgn_buff;
5413*3417c957SSherry Yang 	size_t size, cinfosz;
5414acbaa8c8SJames Smart 	int  rc = 0;
5415acbaa8c8SJames Smart 
5416acbaa8c8SJames Smart 	if (job->request_len < sizeof(struct fc_bsg_request) +
5417acbaa8c8SJames Smart 	    sizeof(struct get_cgnbuf_info_req)) {
5418acbaa8c8SJames Smart 		rc = -ENOMEM;
5419acbaa8c8SJames Smart 		goto job_exit;
5420acbaa8c8SJames Smart 	}
5421acbaa8c8SJames Smart 
5422acbaa8c8SJames Smart 	if (!phba->sli4_hba.pc_sli4_params.cmf) {
5423acbaa8c8SJames Smart 		rc = -ENOENT;
5424acbaa8c8SJames Smart 		goto job_exit;
5425acbaa8c8SJames Smart 	}
5426acbaa8c8SJames Smart 
5427acbaa8c8SJames Smart 	if (!phba->cgn_i || !phba->cgn_i->virt) {
5428acbaa8c8SJames Smart 		rc = -ENOENT;
5429acbaa8c8SJames Smart 		goto job_exit;
5430acbaa8c8SJames Smart 	}
5431acbaa8c8SJames Smart 
5432acbaa8c8SJames Smart 	cp = phba->cgn_i->virt;
5433acbaa8c8SJames Smart 	if (cp->cgn_info_version < LPFC_CGN_INFO_V3) {
5434acbaa8c8SJames Smart 		rc = -EPERM;
5435acbaa8c8SJames Smart 		goto job_exit;
5436acbaa8c8SJames Smart 	}
5437acbaa8c8SJames Smart 
5438acbaa8c8SJames Smart 	cgnbuf_req = (struct get_cgnbuf_info_req *)
5439acbaa8c8SJames Smart 		bsg_request->rqst_data.h_vendor.vendor_cmd;
5440acbaa8c8SJames Smart 
5441acbaa8c8SJames Smart 	/* For reset or size == 0 */
5442acbaa8c8SJames Smart 	bsg_reply->reply_payload_rcv_len = 0;
5443acbaa8c8SJames Smart 
5444acbaa8c8SJames Smart 	if (cgnbuf_req->reset == LPFC_BSG_CGN_RESET_STAT) {
5445acbaa8c8SJames Smart 		lpfc_init_congestion_stat(phba);
5446acbaa8c8SJames Smart 		goto job_exit;
5447acbaa8c8SJames Smart 	}
5448acbaa8c8SJames Smart 
5449acbaa8c8SJames Smart 	/* We don't want to include the CRC at the end */
5450acbaa8c8SJames Smart 	cinfosz = sizeof(struct lpfc_cgn_info) - sizeof(uint32_t);
5451acbaa8c8SJames Smart 
5452acbaa8c8SJames Smart 	size = cgnbuf_req->read_size;
5453acbaa8c8SJames Smart 	if (!size)
5454acbaa8c8SJames Smart 		goto job_exit;
5455acbaa8c8SJames Smart 
5456acbaa8c8SJames Smart 	if (size < cinfosz) {
5457acbaa8c8SJames Smart 		/* Just copy back what we can */
5458acbaa8c8SJames Smart 		cinfosz = size;
5459acbaa8c8SJames Smart 		rc = -E2BIG;
5460acbaa8c8SJames Smart 	}
5461acbaa8c8SJames Smart 
5462acbaa8c8SJames Smart 	/* Allocate memory to read congestion info */
5463acbaa8c8SJames Smart 	cgn_buff = vmalloc(cinfosz);
5464acbaa8c8SJames Smart 	if (!cgn_buff) {
5465acbaa8c8SJames Smart 		rc = -ENOMEM;
5466acbaa8c8SJames Smart 		goto job_exit;
5467acbaa8c8SJames Smart 	}
5468acbaa8c8SJames Smart 
5469acbaa8c8SJames Smart 	memcpy(cgn_buff, cp, cinfosz);
5470acbaa8c8SJames Smart 
5471acbaa8c8SJames Smart 	bsg_reply->reply_payload_rcv_len =
5472acbaa8c8SJames Smart 		sg_copy_from_buffer(job->reply_payload.sg_list,
5473acbaa8c8SJames Smart 				    job->reply_payload.sg_cnt,
5474acbaa8c8SJames Smart 				    cgn_buff, cinfosz);
5475acbaa8c8SJames Smart 
5476acbaa8c8SJames Smart 	vfree(cgn_buff);
5477acbaa8c8SJames Smart 
5478acbaa8c8SJames Smart job_exit:
5479acbaa8c8SJames Smart 	bsg_reply->result = rc;
5480acbaa8c8SJames Smart 	if (!rc)
5481acbaa8c8SJames Smart 		bsg_job_done(job, bsg_reply->result,
5482acbaa8c8SJames Smart 			     bsg_reply->reply_payload_rcv_len);
5483acbaa8c8SJames Smart 	else
5484acbaa8c8SJames Smart 		lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
5485acbaa8c8SJames Smart 				"2724 GET CGNBUF error: %d\n", rc);
5486acbaa8c8SJames Smart 	return rc;
5487acbaa8c8SJames Smart }
5488acbaa8c8SJames Smart 
5489d2cc9bcdSJames Smart /**
5490f1c3b0fcSJames Smart  * lpfc_bsg_hst_vendor - process a vendor-specific fc_bsg_job
5491f1c3b0fcSJames Smart  * @job: fc_bsg_job to handle
54923b5dd52aSJames Smart  **/
5493f1c3b0fcSJames Smart static int
549475cc8cfcSJohannes Thumshirn lpfc_bsg_hst_vendor(struct bsg_job *job)
5495f1c3b0fcSJames Smart {
549601e0e15cSJohannes Thumshirn 	struct fc_bsg_request *bsg_request = job->request;
549701e0e15cSJohannes Thumshirn 	struct fc_bsg_reply *bsg_reply = job->reply;
549801e0e15cSJohannes Thumshirn 	int command = bsg_request->rqst_data.h_vendor.vendor_cmd[0];
54994cc0e56eSJames Smart 	int rc;
5500f1c3b0fcSJames Smart 
5501f1c3b0fcSJames Smart 	switch (command) {
5502f1c3b0fcSJames Smart 	case LPFC_BSG_VENDOR_SET_CT_EVENT:
55034cc0e56eSJames Smart 		rc = lpfc_bsg_hba_set_event(job);
5504f1c3b0fcSJames Smart 		break;
5505f1c3b0fcSJames Smart 	case LPFC_BSG_VENDOR_GET_CT_EVENT:
55064cc0e56eSJames Smart 		rc = lpfc_bsg_hba_get_event(job);
5507f1c3b0fcSJames Smart 		break;
55083b5dd52aSJames Smart 	case LPFC_BSG_VENDOR_SEND_MGMT_RESP:
55093b5dd52aSJames Smart 		rc = lpfc_bsg_send_mgmt_rsp(job);
55103b5dd52aSJames Smart 		break;
55113b5dd52aSJames Smart 	case LPFC_BSG_VENDOR_DIAG_MODE:
55127ad20aa9SJames Smart 		rc = lpfc_bsg_diag_loopback_mode(job);
55133b5dd52aSJames Smart 		break;
55147ad20aa9SJames Smart 	case LPFC_BSG_VENDOR_DIAG_MODE_END:
55157ad20aa9SJames Smart 		rc = lpfc_sli4_bsg_diag_mode_end(job);
55167ad20aa9SJames Smart 		break;
55177ad20aa9SJames Smart 	case LPFC_BSG_VENDOR_DIAG_RUN_LOOPBACK:
55187ad20aa9SJames Smart 		rc = lpfc_bsg_diag_loopback_run(job);
55197ad20aa9SJames Smart 		break;
55207ad20aa9SJames Smart 	case LPFC_BSG_VENDOR_LINK_DIAG_TEST:
55217ad20aa9SJames Smart 		rc = lpfc_sli4_bsg_link_diag_test(job);
55223b5dd52aSJames Smart 		break;
55233b5dd52aSJames Smart 	case LPFC_BSG_VENDOR_GET_MGMT_REV:
55243b5dd52aSJames Smart 		rc = lpfc_bsg_get_dfc_rev(job);
55253b5dd52aSJames Smart 		break;
55263b5dd52aSJames Smart 	case LPFC_BSG_VENDOR_MBOX:
55273b5dd52aSJames Smart 		rc = lpfc_bsg_mbox_cmd(job);
55283b5dd52aSJames Smart 		break;
5529c691816eSJames Smart 	case LPFC_BSG_VENDOR_FORCED_LINK_SPEED:
5530c691816eSJames Smart 		rc = lpfc_forced_link_speed(job);
5531c691816eSJames Smart 		break;
5532d2cc9bcdSJames Smart 	case LPFC_BSG_VENDOR_RAS_GET_LWPD:
5533d2cc9bcdSJames Smart 		rc = lpfc_bsg_get_ras_lwpd(job);
5534d2cc9bcdSJames Smart 		break;
5535d2cc9bcdSJames Smart 	case LPFC_BSG_VENDOR_RAS_GET_FWLOG:
5536d2cc9bcdSJames Smart 		rc = lpfc_bsg_get_ras_fwlog(job);
5537d2cc9bcdSJames Smart 		break;
5538d2cc9bcdSJames Smart 	case LPFC_BSG_VENDOR_RAS_GET_CONFIG:
5539d2cc9bcdSJames Smart 		rc = lpfc_bsg_get_ras_config(job);
5540d2cc9bcdSJames Smart 		break;
5541d2cc9bcdSJames Smart 	case LPFC_BSG_VENDOR_RAS_SET_CONFIG:
5542d2cc9bcdSJames Smart 		rc = lpfc_bsg_set_ras_config(job);
5543d2cc9bcdSJames Smart 		break;
55441dc5ec24SJames Smart 	case LPFC_BSG_VENDOR_GET_TRUNK_INFO:
55451dc5ec24SJames Smart 		rc = lpfc_get_trunk_info(job);
55461dc5ec24SJames Smart 		break;
5547acbaa8c8SJames Smart 	case LPFC_BSG_VENDOR_GET_CGNBUF_INFO:
5548acbaa8c8SJames Smart 		rc = lpfc_get_cgnbuf_info(job);
5549acbaa8c8SJames Smart 		break;
5550f1c3b0fcSJames Smart 	default:
55514cc0e56eSJames Smart 		rc = -EINVAL;
555201e0e15cSJohannes Thumshirn 		bsg_reply->reply_payload_rcv_len = 0;
55534cc0e56eSJames Smart 		/* make error code available to userspace */
555401e0e15cSJohannes Thumshirn 		bsg_reply->result = rc;
55554cc0e56eSJames Smart 		break;
5556f1c3b0fcSJames Smart 	}
55574cc0e56eSJames Smart 
55584cc0e56eSJames Smart 	return rc;
5559f1c3b0fcSJames Smart }
5560f1c3b0fcSJames Smart 
5561f1c3b0fcSJames Smart /**
5562f1c3b0fcSJames Smart  * lpfc_bsg_request - handle a bsg request from the FC transport
5563d2cc9bcdSJames Smart  * @job: bsg_job to handle
55643b5dd52aSJames Smart  **/
5565f1c3b0fcSJames Smart int
556675cc8cfcSJohannes Thumshirn lpfc_bsg_request(struct bsg_job *job)
5567f1c3b0fcSJames Smart {
556801e0e15cSJohannes Thumshirn 	struct fc_bsg_request *bsg_request = job->request;
556901e0e15cSJohannes Thumshirn 	struct fc_bsg_reply *bsg_reply = job->reply;
5570f1c3b0fcSJames Smart 	uint32_t msgcode;
55714cc0e56eSJames Smart 	int rc;
5572f1c3b0fcSJames Smart 
557301e0e15cSJohannes Thumshirn 	msgcode = bsg_request->msgcode;
5574f1c3b0fcSJames Smart 	switch (msgcode) {
5575f1c3b0fcSJames Smart 	case FC_BSG_HST_VENDOR:
5576f1c3b0fcSJames Smart 		rc = lpfc_bsg_hst_vendor(job);
5577f1c3b0fcSJames Smart 		break;
5578f1c3b0fcSJames Smart 	case FC_BSG_RPT_ELS:
5579f1c3b0fcSJames Smart 		rc = lpfc_bsg_rport_els(job);
5580f1c3b0fcSJames Smart 		break;
5581f1c3b0fcSJames Smart 	case FC_BSG_RPT_CT:
55824cc0e56eSJames Smart 		rc = lpfc_bsg_send_mgmt_cmd(job);
5583f1c3b0fcSJames Smart 		break;
5584f1c3b0fcSJames Smart 	default:
55854cc0e56eSJames Smart 		rc = -EINVAL;
558601e0e15cSJohannes Thumshirn 		bsg_reply->reply_payload_rcv_len = 0;
55874cc0e56eSJames Smart 		/* make error code available to userspace */
558801e0e15cSJohannes Thumshirn 		bsg_reply->result = rc;
5589f1c3b0fcSJames Smart 		break;
5590f1c3b0fcSJames Smart 	}
5591f1c3b0fcSJames Smart 
5592f1c3b0fcSJames Smart 	return rc;
5593f1c3b0fcSJames Smart }
5594f1c3b0fcSJames Smart 
5595f1c3b0fcSJames Smart /**
5596f1c3b0fcSJames Smart  * lpfc_bsg_timeout - handle timeout of a bsg request from the FC transport
5597d2cc9bcdSJames Smart  * @job: bsg_job that has timed out
5598f1c3b0fcSJames Smart  *
5599f1c3b0fcSJames Smart  * This function just aborts the job's IOCB.  The aborted IOCB will return to
5600f1c3b0fcSJames Smart  * the waiting function which will handle passing the error back to userspace
56013b5dd52aSJames Smart  **/
5602f1c3b0fcSJames Smart int
560375cc8cfcSJohannes Thumshirn lpfc_bsg_timeout(struct bsg_job *job)
5604f1c3b0fcSJames Smart {
5605cd21c605SJohannes Thumshirn 	struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
5606f1c3b0fcSJames Smart 	struct lpfc_hba *phba = vport->phba;
56074cc0e56eSJames Smart 	struct lpfc_iocbq *cmdiocb;
5608895427bdSJames Smart 	struct lpfc_sli_ring *pring;
56094cc0e56eSJames Smart 	struct bsg_job_data *dd_data;
56104cc0e56eSJames Smart 	unsigned long flags;
5611a33c4f7bSJames Smart 	int rc = 0;
5612a33c4f7bSJames Smart 	LIST_HEAD(completions);
5613a33c4f7bSJames Smart 	struct lpfc_iocbq *check_iocb, *next_iocb;
5614a33c4f7bSJames Smart 
5615895427bdSJames Smart 	pring = lpfc_phba_elsring(phba);
56161234a6d5SDick Kennedy 	if (unlikely(!pring))
56171234a6d5SDick Kennedy 		return -EIO;
5618895427bdSJames Smart 
5619a33c4f7bSJames Smart 	/* if job's driver data is NULL, the command completed or is in the
5620a33c4f7bSJames Smart 	 * the process of completing.  In this case, return status to request
5621a33c4f7bSJames Smart 	 * so the timeout is retried.  This avoids double completion issues
5622a33c4f7bSJames Smart 	 * and the request will be pulled off the timer queue when the
5623a33c4f7bSJames Smart 	 * command's completion handler executes.  Otherwise, prevent the
5624a33c4f7bSJames Smart 	 * command's completion handler from executing the job done callback
5625a33c4f7bSJames Smart 	 * and continue processing to abort the outstanding the command.
5626a33c4f7bSJames Smart 	 */
5627f1c3b0fcSJames Smart 
56284cc0e56eSJames Smart 	spin_lock_irqsave(&phba->ct_ev_lock, flags);
56294cc0e56eSJames Smart 	dd_data = (struct bsg_job_data *)job->dd_data;
5630a33c4f7bSJames Smart 	if (dd_data) {
5631a33c4f7bSJames Smart 		dd_data->set_job = NULL;
5632a33c4f7bSJames Smart 		job->dd_data = NULL;
5633a33c4f7bSJames Smart 	} else {
56344cc0e56eSJames Smart 		spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
5635a33c4f7bSJames Smart 		return -EAGAIN;
56364cc0e56eSJames Smart 	}
56374cc0e56eSJames Smart 
56384cc0e56eSJames Smart 	switch (dd_data->type) {
56394cc0e56eSJames Smart 	case TYPE_IOCB:
5640a33c4f7bSJames Smart 		/* Check to see if IOCB was issued to the port or not. If not,
5641a33c4f7bSJames Smart 		 * remove it from the txq queue and call cancel iocbs.
5642a33c4f7bSJames Smart 		 * Otherwise, call abort iotag
5643a33c4f7bSJames Smart 		 */
5644a33c4f7bSJames Smart 		cmdiocb = dd_data->context_un.iocb.cmdiocbq;
5645b5a9b2dfSJames Smart 		spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
5646b5a9b2dfSJames Smart 
5647b5a9b2dfSJames Smart 		spin_lock_irqsave(&phba->hbalock, flags);
5648b5a9b2dfSJames Smart 		/* make sure the I/O abort window is still open */
5649a680a929SJames Smart 		if (!(cmdiocb->cmd_flag & LPFC_IO_CMD_OUTSTANDING)) {
5650b5a9b2dfSJames Smart 			spin_unlock_irqrestore(&phba->hbalock, flags);
5651b5a9b2dfSJames Smart 			return -EAGAIN;
5652b5a9b2dfSJames Smart 		}
5653a33c4f7bSJames Smart 		list_for_each_entry_safe(check_iocb, next_iocb, &pring->txq,
5654a33c4f7bSJames Smart 					 list) {
5655a33c4f7bSJames Smart 			if (check_iocb == cmdiocb) {
5656a33c4f7bSJames Smart 				list_move_tail(&check_iocb->list, &completions);
5657a33c4f7bSJames Smart 				break;
5658a33c4f7bSJames Smart 			}
5659a33c4f7bSJames Smart 		}
5660a33c4f7bSJames Smart 		if (list_empty(&completions))
5661db7531d2SJames Smart 			lpfc_sli_issue_abort_iotag(phba, pring, cmdiocb, NULL);
5662b5a9b2dfSJames Smart 		spin_unlock_irqrestore(&phba->hbalock, flags);
5663a33c4f7bSJames Smart 		if (!list_empty(&completions)) {
5664a33c4f7bSJames Smart 			lpfc_sli_cancel_iocbs(phba, &completions,
5665a33c4f7bSJames Smart 					      IOSTAT_LOCAL_REJECT,
5666a33c4f7bSJames Smart 					      IOERR_SLI_ABORTED);
5667a33c4f7bSJames Smart 		}
56684cc0e56eSJames Smart 		break;
5669a33c4f7bSJames Smart 
56704cc0e56eSJames Smart 	case TYPE_EVT:
56714cc0e56eSJames Smart 		spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
56724cc0e56eSJames Smart 		break;
5673a33c4f7bSJames Smart 
56743b5dd52aSJames Smart 	case TYPE_MBOX:
5675a33c4f7bSJames Smart 		/* Update the ext buf ctx state if needed */
5676a33c4f7bSJames Smart 
56777ad20aa9SJames Smart 		if (phba->mbox_ext_buf_ctx.state == LPFC_BSG_MBOX_PORT)
56787ad20aa9SJames Smart 			phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_ABTS;
5679a33c4f7bSJames Smart 		spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
56803b5dd52aSJames Smart 		break;
56814cc0e56eSJames Smart 	default:
56824cc0e56eSJames Smart 		spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
56834cc0e56eSJames Smart 		break;
56844cc0e56eSJames Smart 	}
5685f1c3b0fcSJames Smart 
56864cc0e56eSJames Smart 	/* scsi transport fc fc_bsg_job_timeout expects a zero return code,
56874cc0e56eSJames Smart 	 * otherwise an error message will be displayed on the console
56884cc0e56eSJames Smart 	 * so always return success (zero)
56894cc0e56eSJames Smart 	 */
5690a33c4f7bSJames Smart 	return rc;
5691f1c3b0fcSJames Smart }
5692