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
lpfc_free_bsg_buffers(struct lpfc_hba * phba,struct lpfc_dmabuf * mlist)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 *
lpfc_alloc_bsg_buffers(struct lpfc_hba * phba,unsigned int size,int outbound_buffers,struct ulp_bde64 * bpl,int * bpl_entries)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
lpfc_bsg_copy_data(struct lpfc_dmabuf * dma_buffers,struct bsg_buffer * bsg_buffers,unsigned int bytes_to_transfer,int to_buffers)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
lpfc_bsg_send_mgmt_cmd_cmp(struct lpfc_hba * phba,struct lpfc_iocbq * cmdiocbq,struct lpfc_iocbq * rspiocbq)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
lpfc_bsg_send_mgmt_cmd(struct bsg_job * job)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
lpfc_bsg_rport_els_cmp(struct lpfc_hba * phba,struct lpfc_iocbq * cmdiocbq,struct lpfc_iocbq * rspiocbq)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
lpfc_bsg_rport_els(struct bsg_job * job)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
lpfc_bsg_event_free(struct kref * kref)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
lpfc_bsg_event_ref(struct lpfc_bsg_event * evt)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
lpfc_bsg_event_unref(struct lpfc_bsg_event * evt)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 *
lpfc_bsg_event_new(uint32_t ev_mask,int ev_reg_id,uint32_t ev_req_id)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
diag_cmd_data_free(struct lpfc_hba * phba,struct lpfc_dmabufext * mlist)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
lpfc_bsg_ct_unsol_event(struct lpfc_hba * phba,struct lpfc_sli_ring * pring,struct lpfc_iocbq * piocbq)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
lpfc_bsg_ct_unsol_abort(struct lpfc_hba * phba,struct hbq_dmabuf * dmabuf)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
lpfc_bsg_hba_set_event(struct bsg_job * job)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
lpfc_bsg_hba_get_event(struct bsg_job * job)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
lpfc_issue_ct_rsp_cmp(struct lpfc_hba * phba,struct lpfc_iocbq * cmdiocbq,struct lpfc_iocbq * rspiocbq)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
lpfc_issue_ct_rsp(struct lpfc_hba * phba,struct bsg_job * job,uint32_t tag,struct lpfc_dmabuf * cmp,struct lpfc_dmabuf * bmp,int num_entry)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
lpfc_bsg_send_mgmt_rsp(struct bsg_job * job)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
lpfc_bsg_diag_mode_enter(struct lpfc_hba * phba)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
lpfc_bsg_diag_mode_exit(struct lpfc_hba * phba)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
lpfc_sli3_bsg_diag_loopback_mode(struct lpfc_hba * phba,struct bsg_job * job)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
lpfc_sli4_bsg_set_link_diag_state(struct lpfc_hba * phba,uint32_t diag)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
lpfc_sli4_bsg_set_loopback_mode(struct lpfc_hba * phba,int mode,uint32_t link_no)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
lpfc_sli4_diag_fcport_reg_setup(struct lpfc_hba * phba)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
lpfc_sli4_bsg_diag_loopback_mode(struct lpfc_hba * phba,struct bsg_job * job)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
lpfc_bsg_diag_loopback_mode(struct bsg_job * job)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
lpfc_sli4_bsg_diag_mode_end(struct bsg_job * job)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
lpfc_sli4_bsg_link_diag_test(struct bsg_job * job)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 **/
lpfcdiag_loop_self_reg(struct lpfc_hba * phba,uint16_t * rpi)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 **/
lpfcdiag_loop_self_unreg(struct lpfc_hba * phba,uint16_t rpi)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 **/
lpfcdiag_loop_get_xri(struct lpfc_hba * phba,uint16_t rpi,uint16_t * txxri,uint16_t * rxxri)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 *
lpfc_bsg_dma_page_alloc(struct lpfc_hba * phba)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
lpfc_bsg_dma_page_free(struct lpfc_hba * phba,struct lpfc_dmabuf * dmabuf)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
lpfc_bsg_dma_page_list_free(struct lpfc_hba * phba,struct list_head * dmabuf_list)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 *
diag_cmd_data_alloc(struct lpfc_hba * phba,struct ulp_bde64 * bpl,uint32_t size,int nocopydata)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 **/
lpfcdiag_sli3_loop_post_rxbufs(struct lpfc_hba * phba,uint16_t rxxri,size_t len)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
lpfc_bsg_diag_loopback_run(struct bsg_job * job)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
lpfc_bsg_get_dfc_rev(struct bsg_job * job)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
lpfc_bsg_issue_mbox_cmpl(struct lpfc_hba * phba,LPFC_MBOXQ_t * pmboxq)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 **/
lpfc_bsg_check_cmd_access(struct lpfc_hba * phba,MAILBOX_t * mb,struct lpfc_vport * vport)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
lpfc_bsg_mbox_ext_session_reset(struct lpfc_hba * phba)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 *
lpfc_bsg_issue_mbox_ext_handle_job(struct lpfc_hba * phba,LPFC_MBOXQ_t * pmboxq)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
lpfc_bsg_issue_read_mbox_ext_cmpl(struct lpfc_hba * phba,LPFC_MBOXQ_t * pmboxq)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
lpfc_bsg_issue_write_mbox_ext_cmpl(struct lpfc_hba * phba,LPFC_MBOXQ_t * pmboxq)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
lpfc_bsg_sli_cfg_dma_desc_setup(struct lpfc_hba * phba,enum nemb_type nemb_tp,uint32_t index,struct lpfc_dmabuf * mbx_dmabuf,struct lpfc_dmabuf * ext_dmabuf)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
lpfc_bsg_sli_cfg_read_cmd_ext(struct lpfc_hba * phba,struct bsg_job * job,enum nemb_type nemb_tp,struct lpfc_dmabuf * dmabuf)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
lpfc_bsg_sli_cfg_write_cmd_ext(struct lpfc_hba * phba,struct bsg_job * job,enum nemb_type nemb_tp,struct lpfc_dmabuf * dmabuf)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
lpfc_bsg_handle_sli_cfg_mbox(struct lpfc_hba * phba,struct bsg_job * job,struct lpfc_dmabuf * dmabuf)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
lpfc_bsg_mbox_ext_abort(struct lpfc_hba * phba)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
lpfc_bsg_read_ebuf_get(struct lpfc_hba * phba,struct bsg_job * job)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
lpfc_bsg_write_ebuf_set(struct lpfc_hba * phba,struct bsg_job * job,struct lpfc_dmabuf * dmabuf)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
lpfc_bsg_handle_sli_cfg_ebuf(struct lpfc_hba * phba,struct bsg_job * job,struct lpfc_dmabuf * dmabuf)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
lpfc_bsg_handle_sli_cfg_ext(struct lpfc_hba * phba,struct bsg_job * job,struct lpfc_dmabuf * dmabuf)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
lpfc_bsg_issue_mbox(struct lpfc_hba * phba,struct bsg_job * job,struct lpfc_vport * vport)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
lpfc_bsg_mbox_cmd(struct bsg_job * job)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
lpfc_forced_link_speed(struct bsg_job * job)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
lpfc_check_fwlog_support(struct lpfc_hba * phba)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
lpfc_bsg_get_ras_config(struct bsg_job * job)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
lpfc_bsg_set_ras_config(struct bsg_job * job)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
lpfc_bsg_get_ras_lwpd(struct bsg_job * job)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
lpfc_bsg_get_ras_fwlog(struct bsg_job * job)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
lpfc_get_trunk_info(struct bsg_job * job)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
lpfc_get_cgnbuf_info(struct bsg_job * job)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
lpfc_bsg_hst_vendor(struct bsg_job * job)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
lpfc_bsg_request(struct bsg_job * job)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
lpfc_bsg_timeout(struct bsg_job * job)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