Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/scsi/lpfc/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 162 kB image not shown  

Quelle  lpfc_bsg.c   Sprache: C

 
/*******************************************************************
 * This file is part of the Emulex Linux Device Driver for         *
 * Fibre Channel Host Bus Adapters.                                *
 * Copyright (C) 2017-2024 Broadcom. All Rights Reserved. The term *
 * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.     *
 * Copyright (C) 2009-2015 Emulex.  All rights reserved.           *
 * EMULEX and SLI are trademarks of Emulex.                        *
 * www.broadcom.com                                                *
 *                                                                 *
 * This program is free software; you can redistribute it and/or   *
 * modify it under the terms of version 2 of the GNU General       *
 * Public License as published by the Free Software Foundation.    *
 * This program is distributed in the hope that it will be useful. *
 * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND          *
 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,  *
 * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE      *
 * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD *
 * TO BE LEGALLY INVALID.  See the GNU General Public License for  *
 * more details, a copy of which can be found in the file COPYING  *
 * included with this package.                                     *
 *******************************************************************/


#include <linux/interrupt.h>
#include <linux/mempool.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/list.h>
#include <linux/bsg-lib.h>
#include <linux/vmalloc.h>

#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
#include <scsi/scsi_bsg_fc.h>
#include <scsi/fc/fc_fs.h>

#include "lpfc_hw4.h"
#include "lpfc_hw.h"
#include "lpfc_sli.h"
#include "lpfc_sli4.h"
#include "lpfc_nl.h"
#include "lpfc_bsg.h"
#include "lpfc_disc.h"
#include "lpfc_scsi.h"
#include "lpfc.h"
#include "lpfc_logmsg.h"
#include "lpfc_crtn.h"
#include "lpfc_debugfs.h"
#include "lpfc_vport.h"
#include "lpfc_version.h"

struct lpfc_bsg_event {
 struct list_head node;
 struct kref kref;
 wait_queue_head_t wq;

 /* Event type and waiter identifiers */
 uint32_t type_mask;
 uint32_t req_id;
 uint32_t reg_id;

 /* next two flags are here for the auto-delete logic */
 unsigned long wait_time_stamp;
 int waiting;

 /* seen and not seen events */
 struct list_head events_to_get;
 struct list_head events_to_see;

 /* driver data associated with the job */
 void *dd_data;
};

struct lpfc_bsg_iocb {
 struct lpfc_iocbq *cmdiocbq;
 struct lpfc_dmabuf *rmp;
 struct lpfc_nodelist *ndlp;
};

struct lpfc_bsg_mbox {
 LPFC_MBOXQ_t *pmboxq;
 MAILBOX_t *mb;
 struct lpfc_dmabuf *dmabuffers; /* for BIU diags */
 uint8_t *ext; /* extended mailbox data */
 uint32_t mbOffset; /* from app */
 uint32_t inExtWLen; /* from app */
 uint32_t outExtWLen; /* from app */
};

#define TYPE_EVT  1
#define TYPE_IOCB 2
#define TYPE_MBOX 3
struct bsg_job_data {
 uint32_t type;
 struct bsg_job *set_job; /* job waiting for this iocb to finish */
 union {
  struct lpfc_bsg_event *evt;
  struct lpfc_bsg_iocb iocb;
  struct lpfc_bsg_mbox mbox;
 } context_un;
};

struct event_data {
 struct list_head node;
 uint32_t type;
 uint32_t immed_dat;
 void *data;
 uint32_t len;
};

#define BUF_SZ_4K 4096
#define SLI_CT_ELX_LOOPBACK 0x10

enum ELX_LOOPBACK_CMD {
 ELX_LOOPBACK_XRI_SETUP,
 ELX_LOOPBACK_DATA,
};

#define ELX_LOOPBACK_HEADER_SZ \
 (size_t)(&((struct lpfc_sli_ct_request *)NULL)->un)

/* For non-embedded read object command */
#define READ_OBJ_EMB0_SCHEME_0 {1, 10, 256, 128}
#define READ_OBJ_EMB0_SCHEME_1 {11, LPFC_EMB0_MAX_RD_OBJ_HBD_CNT, 512, 192}
static const struct lpfc_read_object_cmd_scheme {
 u32 min_hbd_cnt;
 u32 max_hbd_cnt;
 u32 cmd_size;
 u32 payload_word_offset;
}  rd_obj_scheme[2] = {READ_OBJ_EMB0_SCHEME_0, READ_OBJ_EMB0_SCHEME_1};

struct lpfc_dmabufext {
 struct lpfc_dmabuf dma;
 uint32_t size;
 uint32_t flag;
};

static void
lpfc_free_bsg_buffers(struct lpfc_hba *phba, struct lpfc_dmabuf *mlist)
{
 struct lpfc_dmabuf *mlast, *next_mlast;

 if (mlist) {
  list_for_each_entry_safe(mlast, next_mlast, &mlist->list,
      list) {
   list_del(&mlast->list);
   lpfc_mbuf_free(phba, mlast->virt, mlast->phys);
   kfree(mlast);
  }
  lpfc_mbuf_free(phba, mlist->virt, mlist->phys);
  kfree(mlist);
 }
 return;
}

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)
{
 struct lpfc_dmabuf *mlist = NULL;
 struct lpfc_dmabuf *mp;
 unsigned int bytes_left = size;

 /* Verify we can support the size specified */
 if (!size || (size > (*bpl_entries * LPFC_BPL_SIZE)))
  return NULL;

 /* Determine the number of dma buffers to allocate */
 *bpl_entries = (size % LPFC_BPL_SIZE ? size/LPFC_BPL_SIZE + 1 :
   size/LPFC_BPL_SIZE);

 /* Allocate dma buffer and place in BPL passed */
 while (bytes_left) {
  /* Allocate dma buffer  */
  mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
  if (!mp) {
   if (mlist)
    lpfc_free_bsg_buffers(phba, mlist);
   return NULL;
  }

  INIT_LIST_HEAD(&mp->list);
  mp->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &(mp->phys));

  if (!mp->virt) {
   kfree(mp);
   if (mlist)
    lpfc_free_bsg_buffers(phba, mlist);
   return NULL;
  }

  /* Queue it to a linked list */
  if (!mlist)
   mlist = mp;
  else
   list_add_tail(&mp->list, &mlist->list);

  /* Add buffer to buffer pointer list */
  if (outbound_buffers)
   bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
  else
   bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
  bpl->addrLow = le32_to_cpu(putPaddrLow(mp->phys));
  bpl->addrHigh = le32_to_cpu(putPaddrHigh(mp->phys));
  bpl->tus.f.bdeSize = (uint16_t)
   (bytes_left >= LPFC_BPL_SIZE ? LPFC_BPL_SIZE :
    bytes_left);
  bytes_left -= bpl->tus.f.bdeSize;
  bpl->tus.w = le32_to_cpu(bpl->tus.w);
  bpl++;
 }
 return mlist;
}

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)
{

 struct lpfc_dmabuf *mp;
 unsigned int transfer_bytes, bytes_copied = 0;
 unsigned int sg_offset, dma_offset;
 unsigned char *dma_address, *sg_address;
 LIST_HEAD(temp_list);
 struct sg_mapping_iter miter;
 unsigned long flags;
 unsigned int sg_flags = SG_MITER_ATOMIC;
 bool sg_valid;

 list_splice_init(&dma_buffers->list, &temp_list);
 list_add(&dma_buffers->list, &temp_list);
 sg_offset = 0;
 if (to_buffers)
  sg_flags |= SG_MITER_FROM_SG;
 else
  sg_flags |= SG_MITER_TO_SG;
 sg_miter_start(&miter, bsg_buffers->sg_list, bsg_buffers->sg_cnt,
         sg_flags);
 local_irq_save(flags);
 sg_valid = sg_miter_next(&miter);
 list_for_each_entry(mp, &temp_list, list) {
  dma_offset = 0;
  while (bytes_to_transfer && sg_valid &&
         (dma_offset < LPFC_BPL_SIZE)) {
   dma_address = mp->virt + dma_offset;
   if (sg_offset) {
    /* Continue previous partial transfer of sg */
    sg_address = miter.addr + sg_offset;
    transfer_bytes = miter.length - sg_offset;
   } else {
    sg_address = miter.addr;
    transfer_bytes = miter.length;
   }
   if (bytes_to_transfer < transfer_bytes)
    transfer_bytes = bytes_to_transfer;
   if (transfer_bytes > (LPFC_BPL_SIZE - dma_offset))
    transfer_bytes = LPFC_BPL_SIZE - dma_offset;
   if (to_buffers)
    memcpy(dma_address, sg_address, transfer_bytes);
   else
    memcpy(sg_address, dma_address, transfer_bytes);
   dma_offset += transfer_bytes;
   sg_offset += transfer_bytes;
   bytes_to_transfer -= transfer_bytes;
   bytes_copied += transfer_bytes;
   if (sg_offset >= miter.length) {
    sg_offset = 0;
    sg_valid = sg_miter_next(&miter);
   }
  }
 }
 sg_miter_stop(&miter);
 local_irq_restore(flags);
 list_del_init(&dma_buffers->list);
 list_splice(&temp_list, &dma_buffers->list);
 return bytes_copied;
}

/**
 * lpfc_bsg_send_mgmt_cmd_cmp - lpfc_bsg_send_mgmt_cmd's completion handler
 * @phba: Pointer to HBA context object.
 * @cmdiocbq: Pointer to command iocb.
 * @rspiocbq: Pointer to response iocb.
 *
 * This function is the completion handler for iocbs issued using
 * lpfc_bsg_send_mgmt_cmd function. This function is called by the
 * ring event handler function without any lock held. This function
 * can be called from both worker thread context and interrupt
 * context. This function also can be called from another thread which
 * cleans up the SLI layer objects.
 * This function copies the contents of the response iocb to the
 * response iocb memory object provided by the caller of
 * lpfc_sli_issue_iocb_wait and then wakes up the thread which
 * sleeps for the iocb completion.
 **/

static void
lpfc_bsg_send_mgmt_cmd_cmp(struct lpfc_hba *phba,
   struct lpfc_iocbq *cmdiocbq,
   struct lpfc_iocbq *rspiocbq)
{
 struct bsg_job_data *dd_data;
 struct bsg_job *job;
 struct fc_bsg_reply *bsg_reply;
 struct lpfc_dmabuf *bmp, *cmp, *rmp;
 struct lpfc_nodelist *ndlp;
 struct lpfc_bsg_iocb *iocb;
 unsigned long flags;
 int rc = 0;
 u32 ulp_status, ulp_word4, total_data_placed;

 dd_data = cmdiocbq->context_un.dd_data;

 /* Determine if job has been aborted */
 spin_lock_irqsave(&phba->ct_ev_lock, flags);
 job = dd_data->set_job;
 if (job) {
  bsg_reply = job->reply;
  /* Prevent timeout handling from trying to abort job */
  job->dd_data = NULL;
 }
 spin_unlock_irqrestore(&phba->ct_ev_lock, flags);

 /* Close the timeout handler abort window */
 spin_lock_irqsave(&phba->hbalock, flags);
 cmdiocbq->cmd_flag &= ~LPFC_IO_CMD_OUTSTANDING;
 spin_unlock_irqrestore(&phba->hbalock, flags);

 iocb = &dd_data->context_un.iocb;
 ndlp = iocb->cmdiocbq->ndlp;
 rmp = iocb->rmp;
 cmp = cmdiocbq->cmd_dmabuf;
 bmp = cmdiocbq->bpl_dmabuf;
 ulp_status = get_job_ulpstatus(phba, rspiocbq);
 ulp_word4 = get_job_word4(phba, rspiocbq);
 total_data_placed = get_job_data_placed(phba, rspiocbq);

 /* Copy the completed data or set the error status */

 if (job) {
  if (ulp_status) {
   if (ulp_status == IOSTAT_LOCAL_REJECT) {
    switch (ulp_word4 & IOERR_PARAM_MASK) {
    case IOERR_SEQUENCE_TIMEOUT:
     rc = -ETIMEDOUT;
     break;
    case IOERR_INVALID_RPI:
     rc = -EFAULT;
     break;
    default:
     rc = -EACCES;
     break;
    }
   } else {
    rc = -EACCES;
   }
  } else {
   bsg_reply->reply_payload_rcv_len =
    lpfc_bsg_copy_data(rmp, &job->reply_payload,
         total_data_placed, 0);
  }
 }

 lpfc_free_bsg_buffers(phba, cmp);
 lpfc_free_bsg_buffers(phba, rmp);
 lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
 kfree(bmp);
 lpfc_nlp_put(ndlp);
 lpfc_sli_release_iocbq(phba, cmdiocbq);
 kfree(dd_data);

 /* Complete the job if the job is still active */

 if (job) {
  bsg_reply->result = rc;
  bsg_job_done(job, bsg_reply->result,
          bsg_reply->reply_payload_rcv_len);
 }
 return;
}

/**
 * lpfc_bsg_send_mgmt_cmd - send a CT command from a bsg request
 * @job: fc_bsg_job to handle
 **/

static int
lpfc_bsg_send_mgmt_cmd(struct bsg_job *job)
{
 struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
 struct lpfc_rport_data *rdata = fc_bsg_to_rport(job)->dd_data;
 struct lpfc_hba *phba = vport->phba;
 struct lpfc_nodelist *ndlp = rdata->pnode;
 struct fc_bsg_reply *bsg_reply = job->reply;
 struct ulp_bde64 *bpl = NULL;
 struct lpfc_iocbq *cmdiocbq = NULL;
 struct lpfc_dmabuf *bmp = NULL, *cmp = NULL, *rmp = NULL;
 int request_nseg, reply_nseg;
 u32 num_entry;
 struct bsg_job_data *dd_data;
 unsigned long flags;
 uint32_t creg_val;
 int rc = 0;
 int iocb_stat;
 u16 ulp_context;

 /* in case no data is transferred */
 bsg_reply->reply_payload_rcv_len = 0;

 if (test_bit(NLP_PLOGI_SND, &ndlp->nlp_flag) ||
     test_bit(NLP_PRLI_SND, &ndlp->nlp_flag) ||
     test_bit(NLP_ADISC_SND, &ndlp->nlp_flag) ||
     test_bit(NLP_LOGO_SND, &ndlp->nlp_flag) ||
     test_bit(NLP_RNID_SND, &ndlp->nlp_flag))
  return -ENODEV;

 /* allocate our bsg tracking structure */
 dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
 if (!dd_data) {
  lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
    "2733 Failed allocation of dd_data\n");
  rc = -ENOMEM;
  goto no_dd_data;
 }

 cmdiocbq = lpfc_sli_get_iocbq(phba);
 if (!cmdiocbq) {
  rc = -ENOMEM;
  goto free_dd;
 }

 bmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
 if (!bmp) {
  rc = -ENOMEM;
  goto free_cmdiocbq;
 }
 bmp->virt = lpfc_mbuf_alloc(phba, 0, &bmp->phys);
 if (!bmp->virt) {
  rc = -ENOMEM;
  goto free_bmp;
 }

 INIT_LIST_HEAD(&bmp->list);

 bpl = (struct ulp_bde64 *) bmp->virt;
 request_nseg = LPFC_BPL_SIZE/sizeof(struct ulp_bde64);
 cmp = lpfc_alloc_bsg_buffers(phba, job->request_payload.payload_len,
         1, bpl, &request_nseg);
 if (!cmp) {
  rc = -ENOMEM;
  goto free_bmp;
 }
 lpfc_bsg_copy_data(cmp, &job->request_payload,
      job->request_payload.payload_len, 1);

 bpl += request_nseg;
 reply_nseg = LPFC_BPL_SIZE/sizeof(struct ulp_bde64) - request_nseg;
 rmp = lpfc_alloc_bsg_buffers(phba, job->reply_payload.payload_len, 0,
         bpl, &reply_nseg);
 if (!rmp) {
  rc = -ENOMEM;
  goto free_cmp;
 }

 num_entry = request_nseg + reply_nseg;

 if (phba->sli_rev == LPFC_SLI_REV4)
  ulp_context = phba->sli4_hba.rpi_ids[ndlp->nlp_rpi];
 else
  ulp_context = ndlp->nlp_rpi;

 lpfc_sli_prep_gen_req(phba, cmdiocbq, bmp, ulp_context, num_entry,
         phba->fc_ratov * 2);

 cmdiocbq->num_bdes = num_entry;
 cmdiocbq->vport = phba->pport;
 cmdiocbq->cmd_dmabuf = cmp;
 cmdiocbq->bpl_dmabuf = bmp;
 cmdiocbq->cmd_flag |= LPFC_IO_LIBDFC;

 cmdiocbq->cmd_cmpl = lpfc_bsg_send_mgmt_cmd_cmp;
 cmdiocbq->context_un.dd_data = dd_data;

 dd_data->type = TYPE_IOCB;
 dd_data->set_job = job;
 dd_data->context_un.iocb.cmdiocbq = cmdiocbq;
 dd_data->context_un.iocb.rmp = rmp;
 job->dd_data = dd_data;

 if (phba->cfg_poll & DISABLE_FCP_RING_INT) {
  if (lpfc_readl(phba->HCregaddr, &creg_val)) {
   rc = -EIO ;
   goto free_rmp;
  }
  creg_val |= (HC_R0INT_ENA << LPFC_FCP_RING);
  writel(creg_val, phba->HCregaddr);
  readl(phba->HCregaddr); /* flush */
 }

 cmdiocbq->ndlp = lpfc_nlp_get(ndlp);
 if (!cmdiocbq->ndlp) {
  rc = -ENODEV;
  goto free_rmp;
 }

 iocb_stat = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq, 0);
 if (iocb_stat == IOCB_SUCCESS) {
  spin_lock_irqsave(&phba->hbalock, flags);
  /* make sure the I/O had not been completed yet */
  if (cmdiocbq->cmd_flag & LPFC_IO_LIBDFC) {
   /* open up abort window to timeout handler */
   cmdiocbq->cmd_flag |= LPFC_IO_CMD_OUTSTANDING;
  }
  spin_unlock_irqrestore(&phba->hbalock, flags);
  return 0; /* done for now */
 } else if (iocb_stat == IOCB_BUSY) {
  rc = -EAGAIN;
 } else {
  rc = -EIO;
 }

 /* iocb failed so cleanup */
 lpfc_nlp_put(ndlp);

free_rmp:
 lpfc_free_bsg_buffers(phba, rmp);
free_cmp:
 lpfc_free_bsg_buffers(phba, cmp);
free_bmp:
 if (bmp->virt)
  lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
 kfree(bmp);
free_cmdiocbq:
 lpfc_sli_release_iocbq(phba, cmdiocbq);
free_dd:
 kfree(dd_data);
no_dd_data:
 /* make error code available to userspace */
 bsg_reply->result = rc;
 job->dd_data = NULL;
 return rc;
}

/**
 * lpfc_bsg_rport_els_cmp - lpfc_bsg_rport_els's completion handler
 * @phba: Pointer to HBA context object.
 * @cmdiocbq: Pointer to command iocb.
 * @rspiocbq: Pointer to response iocb.
 *
 * This function is the completion handler for iocbs issued using
 * lpfc_bsg_rport_els_cmp function. This function is called by the
 * ring event handler function without any lock held. This function
 * can be called from both worker thread context and interrupt
 * context. This function also can be called from other thread which
 * cleans up the SLI layer objects.
 * This function copies the contents of the response iocb to the
 * response iocb memory object provided by the caller of
 * lpfc_sli_issue_iocb_wait and then wakes up the thread which
 * sleeps for the iocb completion.
 **/

static void
lpfc_bsg_rport_els_cmp(struct lpfc_hba *phba,
   struct lpfc_iocbq *cmdiocbq,
   struct lpfc_iocbq *rspiocbq)
{
 struct bsg_job_data *dd_data;
 struct bsg_job *job;
 struct fc_bsg_reply *bsg_reply;
 struct lpfc_nodelist *ndlp;
 struct lpfc_dmabuf *pcmd = NULL, *prsp = NULL;
 struct fc_bsg_ctels_reply *els_reply;
 uint8_t *rjt_data;
 unsigned long flags;
 unsigned int rsp_size;
 int rc = 0;
 u32 ulp_status, ulp_word4, total_data_placed;

 dd_data = cmdiocbq->context_un.dd_data;
 ndlp = dd_data->context_un.iocb.ndlp;
 cmdiocbq->ndlp = ndlp;

 /* Determine if job has been aborted */
 spin_lock_irqsave(&phba->ct_ev_lock, flags);
 job = dd_data->set_job;
 if (job) {
  bsg_reply = job->reply;
  /* Prevent timeout handling from trying to abort job  */
  job->dd_data = NULL;
 }
 spin_unlock_irqrestore(&phba->ct_ev_lock, flags);

 /* Close the timeout handler abort window */
 spin_lock_irqsave(&phba->hbalock, flags);
 cmdiocbq->cmd_flag &= ~LPFC_IO_CMD_OUTSTANDING;
 spin_unlock_irqrestore(&phba->hbalock, flags);

 ulp_status = get_job_ulpstatus(phba, rspiocbq);
 ulp_word4 = get_job_word4(phba, rspiocbq);
 total_data_placed = get_job_data_placed(phba, rspiocbq);
 pcmd = cmdiocbq->cmd_dmabuf;
 prsp = (struct lpfc_dmabuf *)pcmd->list.next;

 /* Copy the completed job data or determine the job status if job is
 * still active
 */


 if (job) {
  if (ulp_status == IOSTAT_SUCCESS) {
   rsp_size = total_data_placed;
   bsg_reply->reply_payload_rcv_len =
    sg_copy_from_buffer(job->reply_payload.sg_list,
          job->reply_payload.sg_cnt,
          prsp->virt,
          rsp_size);
  } else if (ulp_status == IOSTAT_LS_RJT) {
   bsg_reply->reply_payload_rcv_len =
    sizeof(struct fc_bsg_ctels_reply);
   /* LS_RJT data returned in word 4 */
   rjt_data = (uint8_t *)&ulp_word4;
   els_reply = &bsg_reply->reply_data.ctels_reply;
   els_reply->status = FC_CTELS_STATUS_REJECT;
   els_reply->rjt_data.action = rjt_data[3];
   els_reply->rjt_data.reason_code = rjt_data[2];
   els_reply->rjt_data.reason_explanation = rjt_data[1];
   els_reply->rjt_data.vendor_unique = rjt_data[0];
  } else if (ulp_status == IOSTAT_LOCAL_REJECT &&
      (ulp_word4 & IOERR_PARAM_MASK) ==
      IOERR_SEQUENCE_TIMEOUT) {
   rc = -ETIMEDOUT;
  } else {
   rc = -EIO;
  }
 }

 lpfc_els_free_iocb(phba, cmdiocbq);

 lpfc_nlp_put(ndlp);
 kfree(dd_data);

 /* Complete the job if the job is still active */

 if (job) {
  bsg_reply->result = rc;
  bsg_job_done(job, bsg_reply->result,
          bsg_reply->reply_payload_rcv_len);
 }
 return;
}

/**
 * lpfc_bsg_rport_els - send an ELS command from a bsg request
 * @job: fc_bsg_job to handle
 **/

static int
lpfc_bsg_rport_els(struct bsg_job *job)
{
 struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
 struct lpfc_hba *phba = vport->phba;
 struct lpfc_rport_data *rdata = fc_bsg_to_rport(job)->dd_data;
 struct lpfc_nodelist *ndlp = rdata->pnode;
 struct fc_bsg_request *bsg_request = job->request;
 struct fc_bsg_reply *bsg_reply = job->reply;
 uint32_t elscmd;
 uint32_t cmdsize;
 struct lpfc_iocbq *cmdiocbq;
 uint16_t rpi = 0;
 struct bsg_job_data *dd_data;
 unsigned long flags;
 uint32_t creg_val;
 int rc = 0;

 /* in case no data is transferred */
 bsg_reply->reply_payload_rcv_len = 0;

 /* verify the els command is not greater than the
 * maximum ELS transfer size.
 */


 if (job->request_payload.payload_len > FCELSSIZE) {
  rc = -EINVAL;
  goto no_dd_data;
 }

 /* allocate our bsg tracking structure */
 dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
 if (!dd_data) {
  lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
    "2735 Failed allocation of dd_data\n");
  rc = -ENOMEM;
  goto no_dd_data;
 }

 elscmd = bsg_request->rqst_data.r_els.els_code;
 cmdsize = job->request_payload.payload_len;

 if (!lpfc_nlp_get(ndlp)) {
  rc = -ENODEV;
  goto free_dd_data;
 }

 /* We will use the allocated dma buffers by prep els iocb for command
 * and response to ensure if the job times out and the request is freed,
 * we won't be dma into memory that is no longer allocated to for the
 * request.
 */

 cmdiocbq = lpfc_prep_els_iocb(vport, 1, cmdsize, 0, ndlp,
          ndlp->nlp_DID, elscmd);
 if (!cmdiocbq) {
  rc = -EIO;
  goto release_ndlp;
 }

 /* Transfer the request payload to allocated command dma buffer */
 sg_copy_to_buffer(job->request_payload.sg_list,
     job->request_payload.sg_cnt,
     cmdiocbq->cmd_dmabuf->virt,
     cmdsize);

 rpi = ndlp->nlp_rpi;

 if (phba->sli_rev == LPFC_SLI_REV4)
  bf_set(wqe_ctxt_tag, &cmdiocbq->wqe.generic.wqe_com,
         phba->sli4_hba.rpi_ids[rpi]);
 else
  cmdiocbq->iocb.ulpContext = rpi;
 cmdiocbq->cmd_flag |= LPFC_IO_LIBDFC;
 cmdiocbq->context_un.dd_data = dd_data;
 cmdiocbq->ndlp = ndlp;
 cmdiocbq->cmd_cmpl = lpfc_bsg_rport_els_cmp;
 dd_data->type = TYPE_IOCB;
 dd_data->set_job = job;
 dd_data->context_un.iocb.cmdiocbq = cmdiocbq;
 dd_data->context_un.iocb.ndlp = ndlp;
 dd_data->context_un.iocb.rmp = NULL;
 job->dd_data = dd_data;

 if (phba->cfg_poll & DISABLE_FCP_RING_INT) {
  if (lpfc_readl(phba->HCregaddr, &creg_val)) {
   rc = -EIO;
   goto linkdown_err;
  }
  creg_val |= (HC_R0INT_ENA << LPFC_FCP_RING);
  writel(creg_val, phba->HCregaddr);
  readl(phba->HCregaddr); /* flush */
 }

 rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq, 0);
 if (rc == IOCB_SUCCESS) {
  spin_lock_irqsave(&phba->hbalock, flags);
  /* make sure the I/O had not been completed/released */
  if (cmdiocbq->cmd_flag & LPFC_IO_LIBDFC) {
   /* open up abort window to timeout handler */
   cmdiocbq->cmd_flag |= LPFC_IO_CMD_OUTSTANDING;
  }
  spin_unlock_irqrestore(&phba->hbalock, flags);
  return 0; /* done for now */
 } else if (rc == IOCB_BUSY) {
  rc = -EAGAIN;
 } else {
  rc = -EIO;
 }

 /* I/O issue failed.  Cleanup resources. */

linkdown_err:
 lpfc_els_free_iocb(phba, cmdiocbq);

release_ndlp:
 lpfc_nlp_put(ndlp);

free_dd_data:
 kfree(dd_data);

no_dd_data:
 /* make error code available to userspace */
 bsg_reply->result = rc;
 job->dd_data = NULL;
 return rc;
}

/**
 * lpfc_bsg_event_free - frees an allocated event structure
 * @kref: Pointer to a kref.
 *
 * Called from kref_put. Back cast the kref into an event structure address.
 * Free any events to get, delete associated nodes, free any events to see,
 * free any data then free the event itself.
 **/

static void
lpfc_bsg_event_free(struct kref *kref)
{
 struct lpfc_bsg_event *evt = container_of(kref, struct lpfc_bsg_event,
        kref);
 struct event_data *ed;

 list_del(&evt->node);

 while (!list_empty(&evt->events_to_get)) {
  ed = list_entry(evt->events_to_get.next, typeof(*ed), node);
  list_del(&ed->node);
  kfree(ed->data);
  kfree(ed);
 }

 while (!list_empty(&evt->events_to_see)) {
  ed = list_entry(evt->events_to_see.next, typeof(*ed), node);
  list_del(&ed->node);
  kfree(ed->data);
  kfree(ed);
 }

 kfree(evt->dd_data);
 kfree(evt);
}

/**
 * lpfc_bsg_event_ref - increments the kref for an event
 * @evt: Pointer to an event structure.
 **/

static inline void
lpfc_bsg_event_ref(struct lpfc_bsg_event *evt)
{
 kref_get(&evt->kref);
}

/**
 * lpfc_bsg_event_unref - Uses kref_put to free an event structure
 * @evt: Pointer to an event structure.
 **/

static inline void
lpfc_bsg_event_unref(struct lpfc_bsg_event *evt)
{
 kref_put(&evt->kref, lpfc_bsg_event_free);
}

/**
 * lpfc_bsg_event_new - allocate and initialize a event structure
 * @ev_mask: Mask of events.
 * @ev_reg_id: Event reg id.
 * @ev_req_id: Event request id.
 **/

static struct lpfc_bsg_event *
lpfc_bsg_event_new(uint32_t ev_mask, int ev_reg_id, uint32_t ev_req_id)
{
 struct lpfc_bsg_event *evt = kzalloc(sizeof(*evt), GFP_KERNEL);

 if (!evt)
  return NULL;

 INIT_LIST_HEAD(&evt->events_to_get);
 INIT_LIST_HEAD(&evt->events_to_see);
 evt->type_mask = ev_mask;
 evt->req_id = ev_req_id;
 evt->reg_id = ev_reg_id;
 evt->wait_time_stamp = jiffies;
 evt->dd_data = NULL;
 init_waitqueue_head(&evt->wq);
 kref_init(&evt->kref);
 return evt;
}

/**
 * diag_cmd_data_free - Frees an lpfc dma buffer extension
 * @phba: Pointer to HBA context object.
 * @mlist: Pointer to an lpfc dma buffer extension.
 **/

static int
diag_cmd_data_free(struct lpfc_hba *phba, struct lpfc_dmabufext *mlist)
{
 struct lpfc_dmabufext *mlast;
 struct pci_dev *pcidev;
 struct list_head head, *curr, *next;

 if ((!mlist) || (!lpfc_is_link_up(phba) &&
  (phba->link_flag & LS_LOOPBACK_MODE))) {
  return 0;
 }

 pcidev = phba->pcidev;
 list_add_tail(&head, &mlist->dma.list);

 list_for_each_safe(curr, next, &head) {
  mlast = list_entry(curr, struct lpfc_dmabufext , dma.list);
  if (mlast->dma.virt)
   dma_free_coherent(&pcidev->dev,
       mlast->size,
       mlast->dma.virt,
       mlast->dma.phys);
  kfree(mlast);
 }
 return 0;
}

/*
 * lpfc_bsg_ct_unsol_event - process an unsolicited CT command
 *
 * This function is called when an unsolicited CT command is received.  It
 * forwards the event to any processes registered to receive CT events.
 **/

int
lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
   struct lpfc_iocbq *piocbq)
{
 uint32_t evt_req_id = 0;
 u16 cmd;
 struct lpfc_dmabuf *dmabuf = NULL;
 struct lpfc_bsg_event *evt;
 struct event_data *evt_dat = NULL;
 struct lpfc_iocbq *iocbq;
 IOCB_t *iocb = NULL;
 size_t offset = 0;
 struct list_head head;
 struct ulp_bde64 *bde;
 dma_addr_t dma_addr;
 int i;
 struct lpfc_dmabuf *bdeBuf1 = piocbq->cmd_dmabuf;
 struct lpfc_dmabuf *bdeBuf2 = piocbq->bpl_dmabuf;
 struct lpfc_sli_ct_request *ct_req;
 struct bsg_job *job = NULL;
 struct fc_bsg_reply *bsg_reply;
 struct bsg_job_data *dd_data = NULL;
 unsigned long flags;
 int size = 0;
 u32 bde_count = 0;

 INIT_LIST_HEAD(&head);
 list_add_tail(&head, &piocbq->list);

 ct_req = (struct lpfc_sli_ct_request *)bdeBuf1->virt;
 evt_req_id = ct_req->FsType;
 cmd = be16_to_cpu(ct_req->CommandResponse.bits.CmdRsp);

 spin_lock_irqsave(&phba->ct_ev_lock, flags);
 list_for_each_entry(evt, &phba->ct_ev_waiters, node) {
  if (!(evt->type_mask & FC_REG_CT_EVENT) ||
   evt->req_id != evt_req_id)
   continue;

  lpfc_bsg_event_ref(evt);
  spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
  evt_dat = kzalloc(sizeof(*evt_dat), GFP_KERNEL);
  if (evt_dat == NULL) {
   spin_lock_irqsave(&phba->ct_ev_lock, flags);
   lpfc_bsg_event_unref(evt);
   lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
     "2614 Memory allocation failed for "
     "CT event\n");
   break;
  }

  if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) {
   /* take accumulated byte count from the last iocbq */
   iocbq = list_entry(head.prev, typeof(*iocbq), list);
   if (phba->sli_rev == LPFC_SLI_REV4)
    evt_dat->len = iocbq->wcqe_cmpl.total_data_placed;
   else
    evt_dat->len = iocbq->iocb.unsli3.rcvsli3.acc_len;
  } else {
   list_for_each_entry(iocbq, &head, list) {
    iocb = &iocbq->iocb;
    for (i = 0; i < iocb->ulpBdeCount;
         i++)
     evt_dat->len +=
     iocb->un.cont64[i].tus.f.bdeSize;
   }
  }

  evt_dat->data = kzalloc(evt_dat->len, GFP_KERNEL);
  if (evt_dat->data == NULL) {
   lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
     "2615 Memory allocation failed for "
     "CT event data, size %d\n",
     evt_dat->len);
   kfree(evt_dat);
   spin_lock_irqsave(&phba->ct_ev_lock, flags);
   lpfc_bsg_event_unref(evt);
   spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
   goto error_ct_unsol_exit;
  }

  list_for_each_entry(iocbq, &head, list) {
   size = 0;
   if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) {
    bdeBuf1 = iocbq->cmd_dmabuf;
    bdeBuf2 = iocbq->bpl_dmabuf;
   }
   if (phba->sli_rev == LPFC_SLI_REV4)
    bde_count = iocbq->wcqe_cmpl.word3;
   else
    bde_count = iocbq->iocb.ulpBdeCount;
   for (i = 0; i < bde_count; i++) {
    if (phba->sli3_options &
        LPFC_SLI3_HBQ_ENABLED) {
     if (i == 0) {
      size = iocbq->wqe.gen_req.bde.tus.f.bdeSize;
      dmabuf = bdeBuf1;
     } else if (i == 1) {
      size = iocbq->unsol_rcv_len;
      dmabuf = bdeBuf2;
     }
     if ((offset + size) > evt_dat->len)
      size = evt_dat->len - offset;
    } else {
     size = iocbq->iocb.un.cont64[i].
      tus.f.bdeSize;
     bde = &iocbq->iocb.un.cont64[i];
     dma_addr = getPaddr(bde->addrHigh,
           bde->addrLow);
     dmabuf = lpfc_sli_ringpostbuf_get(phba,
       pring, dma_addr);
    }
    if (!dmabuf) {
     lpfc_printf_log(phba, KERN_ERR,
      LOG_LIBDFC, "2616 No dmabuf "
      "found for iocbq x%px\n",
      iocbq);
     kfree(evt_dat->data);
     kfree(evt_dat);
     spin_lock_irqsave(&phba->ct_ev_lock,
      flags);
     lpfc_bsg_event_unref(evt);
     spin_unlock_irqrestore(
      &phba->ct_ev_lock, flags);
     goto error_ct_unsol_exit;
    }
    memcpy((char *)(evt_dat->data) + offset,
           dmabuf->virt, size);
    offset += size;
    if (evt_req_id != SLI_CT_ELX_LOOPBACK &&
        !(phba->sli3_options &
          LPFC_SLI3_HBQ_ENABLED)) {
     lpfc_sli_ringpostbuf_put(phba, pring,
         dmabuf);
    } else {
     switch (cmd) {
     case ELX_LOOPBACK_DATA:
      if (phba->sli_rev <
          LPFC_SLI_REV4)
       diag_cmd_data_free(phba,
       (struct lpfc_dmabufext
        *)dmabuf);
      break;
     case ELX_LOOPBACK_XRI_SETUP:
      if ((phba->sli_rev ==
       LPFC_SLI_REV2) ||
       (phba->sli3_options &
       LPFC_SLI3_HBQ_ENABLED
       )) {
       lpfc_in_buf_free(phba,
         dmabuf);
      } else {
       lpfc_sli3_post_buffer(phba,
               pring,
               1);
      }
      break;
     default:
      if (!(phba->sli3_options &
            LPFC_SLI3_HBQ_ENABLED))
       lpfc_sli3_post_buffer(phba,
               pring,
               1);
      break;
     }
    }
   }
  }

  spin_lock_irqsave(&phba->ct_ev_lock, flags);
  if (phba->sli_rev == LPFC_SLI_REV4) {
   evt_dat->immed_dat = phba->ctx_idx;
   phba->ctx_idx = (phba->ctx_idx + 1) % LPFC_CT_CTX_MAX;
   /* Provide warning for over-run of the ct_ctx array */
   if (phba->ct_ctx[evt_dat->immed_dat].valid ==
       UNSOL_VALID)
    lpfc_printf_log(phba, KERN_WARNING, LOG_ELS,
      "2717 CT context array entry "
      "[%d] over-run: oxid:x%x, "
      "sid:x%x\n", phba->ctx_idx,
      phba->ct_ctx[
          evt_dat->immed_dat].oxid,
      phba->ct_ctx[
          evt_dat->immed_dat].SID);
   phba->ct_ctx[evt_dat->immed_dat].rxid =
    get_job_ulpcontext(phba, piocbq);
   phba->ct_ctx[evt_dat->immed_dat].oxid =
    get_job_rcvoxid(phba, piocbq);
   phba->ct_ctx[evt_dat->immed_dat].SID =
    bf_get(wqe_els_did,
           &piocbq->wqe.xmit_els_rsp.wqe_dest);
   phba->ct_ctx[evt_dat->immed_dat].valid = UNSOL_VALID;
  } else
   evt_dat->immed_dat = get_job_ulpcontext(phba, piocbq);

  evt_dat->type = FC_REG_CT_EVENT;
  list_add(&evt_dat->node, &evt->events_to_see);
  if (evt_req_id == SLI_CT_ELX_LOOPBACK) {
   wake_up_interruptible(&evt->wq);
   lpfc_bsg_event_unref(evt);
   break;
  }

  list_move(evt->events_to_see.prev, &evt->events_to_get);

  dd_data = (struct bsg_job_data *)evt->dd_data;
  job = dd_data->set_job;
  dd_data->set_job = NULL;
  lpfc_bsg_event_unref(evt);
  if (job) {
   bsg_reply = job->reply;
   bsg_reply->reply_payload_rcv_len = size;
   /* make error code available to userspace */
   bsg_reply->result = 0;
   job->dd_data = NULL;
   /* complete the job back to userspace */
   spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
   bsg_job_done(job, bsg_reply->result,
           bsg_reply->reply_payload_rcv_len);
   spin_lock_irqsave(&phba->ct_ev_lock, flags);
  }
 }
 spin_unlock_irqrestore(&phba->ct_ev_lock, flags);

error_ct_unsol_exit:
 if (!list_empty(&head))
  list_del(&head);
 if ((phba->sli_rev < LPFC_SLI_REV4) &&
     (evt_req_id == SLI_CT_ELX_LOOPBACK))
  return 0;
 return 1;
}

/**
 * lpfc_bsg_ct_unsol_abort - handler ct abort to management plane
 * @phba: Pointer to HBA context object.
 * @dmabuf: pointer to a dmabuf that describes the FC sequence
 *
 * This function handles abort to the CT command toward management plane
 * for SLI4 port.
 *
 * If the pending context of a CT command to management plane present, clears
 * such context and returns 1 for handled; otherwise, it returns 0 indicating
 * no context exists.
 **/

int
lpfc_bsg_ct_unsol_abort(struct lpfc_hba *phba, struct hbq_dmabuf *dmabuf)
{
 struct fc_frame_header fc_hdr;
 struct fc_frame_header *fc_hdr_ptr = &fc_hdr;
 int ctx_idx, handled = 0;
 uint16_t oxid, rxid;
 uint32_t sid;

 memcpy(fc_hdr_ptr, dmabuf->hbuf.virt, sizeof(struct fc_frame_header));
 sid = sli4_sid_from_fc_hdr(fc_hdr_ptr);
 oxid = be16_to_cpu(fc_hdr_ptr->fh_ox_id);
 rxid = be16_to_cpu(fc_hdr_ptr->fh_rx_id);

 for (ctx_idx = 0; ctx_idx < LPFC_CT_CTX_MAX; ctx_idx++) {
  if (phba->ct_ctx[ctx_idx].valid != UNSOL_VALID)
   continue;
  if (phba->ct_ctx[ctx_idx].rxid != rxid)
   continue;
  if (phba->ct_ctx[ctx_idx].oxid != oxid)
   continue;
  if (phba->ct_ctx[ctx_idx].SID != sid)
   continue;
  phba->ct_ctx[ctx_idx].valid = UNSOL_INVALID;
  handled = 1;
 }
 return handled;
}

/**
 * lpfc_bsg_hba_set_event - process a SET_EVENT bsg vendor command
 * @job: SET_EVENT fc_bsg_job
 **/

static int
lpfc_bsg_hba_set_event(struct bsg_job *job)
{
 struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
 struct lpfc_hba *phba = vport->phba;
 struct fc_bsg_request *bsg_request = job->request;
 struct set_ct_event *event_req;
 struct lpfc_bsg_event *evt;
 int rc = 0;
 struct bsg_job_data *dd_data = NULL;
 uint32_t ev_mask;
 unsigned long flags;

 if (job->request_len <
     sizeof(struct fc_bsg_request) + sizeof(struct set_ct_event)) {
  lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
    "2612 Received SET_CT_EVENT below minimum "
    "size\n");
  rc = -EINVAL;
  goto job_error;
 }

 event_req = (struct set_ct_event *)
  bsg_request->rqst_data.h_vendor.vendor_cmd;
 ev_mask = ((uint32_t)(unsigned long)event_req->type_mask &
    FC_REG_EVENT_MASK);
 spin_lock_irqsave(&phba->ct_ev_lock, flags);
 list_for_each_entry(evt, &phba->ct_ev_waiters, node) {
  if (evt->reg_id == event_req->ev_reg_id) {
   lpfc_bsg_event_ref(evt);
   evt->wait_time_stamp = jiffies;
   dd_data = (struct bsg_job_data *)evt->dd_data;
   break;
  }
 }
 spin_unlock_irqrestore(&phba->ct_ev_lock, flags);

 if (&evt->node == &phba->ct_ev_waiters) {
  /* no event waiting struct yet - first call */
  dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
  if (dd_data == NULL) {
   lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
     "2734 Failed allocation of dd_data\n");
   rc = -ENOMEM;
   goto job_error;
  }
  evt = lpfc_bsg_event_new(ev_mask, event_req->ev_reg_id,
     event_req->ev_req_id);
  if (!evt) {
   lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
     "2617 Failed allocation of event "
     "waiter\n");
   rc = -ENOMEM;
   goto job_error;
  }
  dd_data->type = TYPE_EVT;
  dd_data->set_job = NULL;
  dd_data->context_un.evt = evt;
  evt->dd_data = (void *)dd_data;
  spin_lock_irqsave(&phba->ct_ev_lock, flags);
  list_add(&evt->node, &phba->ct_ev_waiters);
  lpfc_bsg_event_ref(evt);
  evt->wait_time_stamp = jiffies;
  spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
 }

 spin_lock_irqsave(&phba->ct_ev_lock, flags);
 evt->waiting = 1;
 dd_data->set_job = job; /* for unsolicited command */
 job->dd_data = dd_data; /* for fc transport timeout callback*/
 spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
 return 0; /* call job done later */

job_error:
 kfree(dd_data);
 job->dd_data = NULL;
 return rc;
}

/**
 * lpfc_bsg_hba_get_event - process a GET_EVENT bsg vendor command
 * @job: GET_EVENT fc_bsg_job
 **/

static int
lpfc_bsg_hba_get_event(struct bsg_job *job)
{
 struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
 struct lpfc_hba *phba = vport->phba;
 struct fc_bsg_request *bsg_request = job->request;
 struct fc_bsg_reply *bsg_reply = job->reply;
 struct get_ct_event *event_req;
 struct get_ct_event_reply *event_reply;
 struct lpfc_bsg_event *evt, *evt_next;
 struct event_data *evt_dat = NULL;
 unsigned long flags;
 uint32_t rc = 0;

 if (job->request_len <
     sizeof(struct fc_bsg_request) + sizeof(struct get_ct_event)) {
  lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
    "2613 Received GET_CT_EVENT request below "
    "minimum size\n");
  rc = -EINVAL;
  goto job_error;
 }

 event_req = (struct get_ct_event *)
  bsg_request->rqst_data.h_vendor.vendor_cmd;

 event_reply = (struct get_ct_event_reply *)
  bsg_reply->reply_data.vendor_reply.vendor_rsp;
 spin_lock_irqsave(&phba->ct_ev_lock, flags);
 list_for_each_entry_safe(evt, evt_next, &phba->ct_ev_waiters, node) {
  if (evt->reg_id == event_req->ev_reg_id) {
   if (list_empty(&evt->events_to_get))
    break;
   lpfc_bsg_event_ref(evt);
   evt->wait_time_stamp = jiffies;
   evt_dat = list_entry(evt->events_to_get.prev,
          struct event_data, node);
   list_del(&evt_dat->node);
   break;
  }
 }
 spin_unlock_irqrestore(&phba->ct_ev_lock, flags);

 /* The app may continue to ask for event data until it gets
 * an error indicating that there isn't anymore
 */

 if (evt_dat == NULL) {
  bsg_reply->reply_payload_rcv_len = 0;
  rc = -ENOENT;
  goto job_error;
 }

 if (evt_dat->len > job->request_payload.payload_len) {
  evt_dat->len = job->request_payload.payload_len;
  lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
    "2618 Truncated event data at %d "
    "bytes\n",
    job->request_payload.payload_len);
 }

 event_reply->type = evt_dat->type;
 event_reply->immed_data = evt_dat->immed_dat;
 if (evt_dat->len > 0)
  bsg_reply->reply_payload_rcv_len =
   sg_copy_from_buffer(job->request_payload.sg_list,
         job->request_payload.sg_cnt,
         evt_dat->data, evt_dat->len);
 else
  bsg_reply->reply_payload_rcv_len = 0;

 if (evt_dat) {
  kfree(evt_dat->data);
  kfree(evt_dat);
 }

 spin_lock_irqsave(&phba->ct_ev_lock, flags);
 lpfc_bsg_event_unref(evt);
 spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
 job->dd_data = NULL;
 bsg_reply->result = 0;
 bsg_job_done(job, bsg_reply->result,
         bsg_reply->reply_payload_rcv_len);
 return 0;

job_error:
 job->dd_data = NULL;
 bsg_reply->result = rc;
 return rc;
}

/**
 * lpfc_issue_ct_rsp_cmp - lpfc_issue_ct_rsp's completion handler
 * @phba: Pointer to HBA context object.
 * @cmdiocbq: Pointer to command iocb.
 * @rspiocbq: Pointer to response iocb.
 *
 * This function is the completion handler for iocbs issued using
 * lpfc_issue_ct_rsp_cmp function. This function is called by the
 * ring event handler function without any lock held. This function
 * can be called from both worker thread context and interrupt
 * context. This function also can be called from other thread which
 * cleans up the SLI layer objects.
 * This function copy the contents of the response iocb to the
 * response iocb memory object provided by the caller of
 * lpfc_sli_issue_iocb_wait and then wakes up the thread which
 * sleeps for the iocb completion.
 **/

static void
lpfc_issue_ct_rsp_cmp(struct lpfc_hba *phba,
   struct lpfc_iocbq *cmdiocbq,
   struct lpfc_iocbq *rspiocbq)
{
 struct bsg_job_data *dd_data;
 struct bsg_job *job;
 struct fc_bsg_reply *bsg_reply;
 struct lpfc_dmabuf *bmp, *cmp;
 struct lpfc_nodelist *ndlp;
 unsigned long flags;
 int rc = 0;
 u32 ulp_status, ulp_word4;

 dd_data = cmdiocbq->context_un.dd_data;

 /* Determine if job has been aborted */
 spin_lock_irqsave(&phba->ct_ev_lock, flags);
 job = dd_data->set_job;
 if (job) {
  /* Prevent timeout handling from trying to abort job  */
  job->dd_data = NULL;
 }
 spin_unlock_irqrestore(&phba->ct_ev_lock, flags);

 /* Close the timeout handler abort window */
 spin_lock_irqsave(&phba->hbalock, flags);
 cmdiocbq->cmd_flag &= ~LPFC_IO_CMD_OUTSTANDING;
 spin_unlock_irqrestore(&phba->hbalock, flags);

 ndlp = dd_data->context_un.iocb.ndlp;
 cmp = cmdiocbq->cmd_dmabuf;
 bmp = cmdiocbq->bpl_dmabuf;

 ulp_status = get_job_ulpstatus(phba, rspiocbq);
 ulp_word4 = get_job_word4(phba, rspiocbq);

 /* Copy the completed job data or set the error status */

 if (job) {
  bsg_reply = job->reply;
  if (ulp_status) {
   if (ulp_status == IOSTAT_LOCAL_REJECT) {
    switch (ulp_word4 & IOERR_PARAM_MASK) {
    case IOERR_SEQUENCE_TIMEOUT:
     rc = -ETIMEDOUT;
     break;
    case IOERR_INVALID_RPI:
     rc = -EFAULT;
     break;
    default:
     rc = -EACCES;
     break;
    }
   } else {
    rc = -EACCES;
   }
  } else {
   bsg_reply->reply_payload_rcv_len = 0;
  }
 }

 lpfc_free_bsg_buffers(phba, cmp);
 lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
 kfree(bmp);
 lpfc_sli_release_iocbq(phba, cmdiocbq);
 lpfc_nlp_put(ndlp);
 kfree(dd_data);

 /* Complete the job if the job is still active */

 if (job) {
  bsg_reply->result = rc;
  bsg_job_done(job, bsg_reply->result,
          bsg_reply->reply_payload_rcv_len);
 }
 return;
}

/**
 * lpfc_issue_ct_rsp - issue a ct response
 * @phba: Pointer to HBA context object.
 * @job: Pointer to the job object.
 * @tag: tag index value into the ports context exchange array.
 * @cmp: Pointer to a cmp dma buffer descriptor.
 * @bmp: Pointer to a bmp dma buffer descriptor.
 * @num_entry: Number of enties in the bde.
 **/

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)
{
 struct lpfc_iocbq *ctiocb = NULL;
 int rc = 0;
 struct lpfc_nodelist *ndlp = NULL;
 struct bsg_job_data *dd_data;
 unsigned long flags;
 uint32_t creg_val;
 u16 ulp_context, iotag;

 ndlp = lpfc_findnode_did(phba->pport, phba->ct_ctx[tag].SID);
 if (!ndlp) {
  lpfc_printf_log(phba, KERN_WARNING, LOG_ELS,
    "2721 ndlp null for oxid %x SID %x\n",
    phba->ct_ctx[tag].rxid,
    phba->ct_ctx[tag].SID);
  return IOCB_ERROR;
 }

 /* allocate our bsg tracking structure */
 dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
 if (!dd_data) {
  lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
    "2736 Failed allocation of dd_data\n");
  rc = -ENOMEM;
  goto no_dd_data;
 }

 /* Allocate buffer for  command iocb */
 ctiocb = lpfc_sli_get_iocbq(phba);
 if (!ctiocb) {
  rc = -ENOMEM;
  goto no_ctiocb;
 }

 if (phba->sli_rev == LPFC_SLI_REV4) {
  /* Do not issue unsol response if oxid not marked as valid */
  if (phba->ct_ctx[tag].valid != UNSOL_VALID) {
   rc = IOCB_ERROR;
   goto issue_ct_rsp_exit;
  }

  lpfc_sli_prep_xmit_seq64(phba, ctiocb, bmp,
      phba->sli4_hba.rpi_ids[ndlp->nlp_rpi],
      phba->ct_ctx[tag].oxid, num_entry,
      FC_RCTL_DD_SOL_CTL, 1,
      CMD_XMIT_SEQUENCE64_WQE);

  /* The exchange is done, mark the entry as invalid */
  phba->ct_ctx[tag].valid = UNSOL_INVALID;
  iotag = get_wqe_reqtag(ctiocb);
 } else {
  lpfc_sli_prep_xmit_seq64(phba, ctiocb, bmp, 0, tag, num_entry,
      FC_RCTL_DD_SOL_CTL, 1,
      CMD_XMIT_SEQUENCE64_CX);
  ctiocb->num_bdes = num_entry;
  iotag = ctiocb->iocb.ulpIoTag;
 }

 ulp_context = get_job_ulpcontext(phba, ctiocb);

 /* Xmit CT response on exchange <xid> */
 lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
   "2722 Xmit CT response on exchange x%x Data: x%x x%x x%x\n",
   ulp_context, iotag, tag, phba->link_state);

 ctiocb->cmd_flag |= LPFC_IO_LIBDFC;
 ctiocb->vport = phba->pport;
 ctiocb->context_un.dd_data = dd_data;
 ctiocb->cmd_dmabuf = cmp;
 ctiocb->bpl_dmabuf = bmp;
 ctiocb->ndlp = ndlp;
 ctiocb->cmd_cmpl = lpfc_issue_ct_rsp_cmp;

 dd_data->type = TYPE_IOCB;
 dd_data->set_job = job;
 dd_data->context_un.iocb.cmdiocbq = ctiocb;
 dd_data->context_un.iocb.ndlp = lpfc_nlp_get(ndlp);
 if (!dd_data->context_un.iocb.ndlp) {
  rc = -IOCB_ERROR;
  goto issue_ct_rsp_exit;
 }
 dd_data->context_un.iocb.rmp = NULL;
 job->dd_data = dd_data;

 if (phba->cfg_poll & DISABLE_FCP_RING_INT) {
  if (lpfc_readl(phba->HCregaddr, &creg_val)) {
   rc = -IOCB_ERROR;
   goto issue_ct_rsp_exit;
  }
  creg_val |= (HC_R0INT_ENA << LPFC_FCP_RING);
  writel(creg_val, phba->HCregaddr);
  readl(phba->HCregaddr); /* flush */
 }

 rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, ctiocb, 0);
 if (rc == IOCB_SUCCESS) {
  spin_lock_irqsave(&phba->hbalock, flags);
  /* make sure the I/O had not been completed/released */
  if (ctiocb->cmd_flag & LPFC_IO_LIBDFC) {
   /* open up abort window to timeout handler */
   ctiocb->cmd_flag |= LPFC_IO_CMD_OUTSTANDING;
  }
  spin_unlock_irqrestore(&phba->hbalock, flags);
  return 0; /* done for now */
 }

 /* iocb failed so cleanup */
 job->dd_data = NULL;
 lpfc_nlp_put(ndlp);

issue_ct_rsp_exit:
 lpfc_sli_release_iocbq(phba, ctiocb);
no_ctiocb:
 kfree(dd_data);
no_dd_data:
 return rc;
}

/**
 * lpfc_bsg_send_mgmt_rsp - process a SEND_MGMT_RESP bsg vendor command
 * @job: SEND_MGMT_RESP fc_bsg_job
 **/

static int
lpfc_bsg_send_mgmt_rsp(struct bsg_job *job)
{
 struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
 struct lpfc_hba *phba = vport->phba;
 struct fc_bsg_request *bsg_request = job->request;
 struct fc_bsg_reply *bsg_reply = job->reply;
 struct send_mgmt_resp *mgmt_resp = (struct send_mgmt_resp *)
  bsg_request->rqst_data.h_vendor.vendor_cmd;
 struct ulp_bde64 *bpl;
 struct lpfc_dmabuf *bmp = NULL, *cmp = NULL;
 int bpl_entries;
 uint32_t tag = mgmt_resp->tag;
 unsigned long reqbfrcnt =
   (unsigned long)job->request_payload.payload_len;
 int rc = 0;

 /* in case no data is transferred */
 bsg_reply->reply_payload_rcv_len = 0;

 if (!reqbfrcnt || (reqbfrcnt > (80 * BUF_SZ_4K))) {
  rc = -ERANGE;
  goto send_mgmt_rsp_exit;
 }

 bmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
 if (!bmp) {
  rc = -ENOMEM;
  goto send_mgmt_rsp_exit;
 }

 bmp->virt = lpfc_mbuf_alloc(phba, 0, &bmp->phys);
 if (!bmp->virt) {
  rc = -ENOMEM;
  goto send_mgmt_rsp_free_bmp;
 }

 INIT_LIST_HEAD(&bmp->list);
 bpl = (struct ulp_bde64 *) bmp->virt;
 bpl_entries = (LPFC_BPL_SIZE/sizeof(struct ulp_bde64));
 cmp = lpfc_alloc_bsg_buffers(phba, job->request_payload.payload_len,
         1, bpl, &bpl_entries);
 if (!cmp) {
  rc = -ENOMEM;
  goto send_mgmt_rsp_free_bmp;
 }
 lpfc_bsg_copy_data(cmp, &job->request_payload,
      job->request_payload.payload_len, 1);

 rc = lpfc_issue_ct_rsp(phba, job, tag, cmp, bmp, bpl_entries);

 if (rc == IOCB_SUCCESS)
  return 0; /* done for now */

 rc = -EACCES;

 lpfc_free_bsg_buffers(phba, cmp);

send_mgmt_rsp_free_bmp:
 if (bmp->virt)
  lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
 kfree(bmp);
send_mgmt_rsp_exit:
 /* make error code available to userspace */
 bsg_reply->result = rc;
 job->dd_data = NULL;
 return rc;
}

/**
 * lpfc_bsg_diag_mode_enter - process preparing into device diag loopback mode
 * @phba: Pointer to HBA context object.
 *
 * This function is responsible for preparing driver for diag loopback
 * on device.
 */

static int
lpfc_bsg_diag_mode_enter(struct lpfc_hba *phba)
{
 struct lpfc_vport **vports;
 struct Scsi_Host *shost;
 struct lpfc_sli *psli;
 struct lpfc_queue *qp = NULL;
 struct lpfc_sli_ring *pring;
 int i = 0;

 psli = &phba->sli;
 if (!psli)
  return -ENODEV;


 if ((phba->link_state == LPFC_HBA_ERROR) ||
     (psli->sli_flag & LPFC_BLOCK_MGMT_IO) ||
     (!(psli->sli_flag & LPFC_SLI_ACTIVE)))
  return -EACCES;

 vports = lpfc_create_vport_work_array(phba);
 if (vports) {
  for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
   shost = lpfc_shost_from_vport(vports[i]);
   scsi_block_requests(shost);
  }
  lpfc_destroy_vport_work_array(phba, vports);
 } else {
  shost = lpfc_shost_from_vport(phba->pport);
  scsi_block_requests(shost);
 }

 if (phba->sli_rev != LPFC_SLI_REV4) {
  pring = &psli->sli3_ring[LPFC_FCP_RING];
  lpfc_emptyq_wait(phba, &pring->txcmplq, &phba->hbalock);
  return 0;
 }
 list_for_each_entry(qp, &phba->sli4_hba.lpfc_wq_list, wq_list) {
  pring = qp->pring;
  if (!pring || (pring->ringno != LPFC_FCP_RING))
   continue;
  if (!lpfc_emptyq_wait(phba, &pring->txcmplq,
          &pring->ring_lock))
   break;
 }
 return 0;
}

/**
 * lpfc_bsg_diag_mode_exit - exit process from device diag loopback mode
 * @phba: Pointer to HBA context object.
 *
 * This function is responsible for driver exit processing of setting up
 * diag loopback mode on device.
 */

static void
lpfc_bsg_diag_mode_exit(struct lpfc_hba *phba)
{
 struct Scsi_Host *shost;
 struct lpfc_vport **vports;
 int i;

 vports = lpfc_create_vport_work_array(phba);
 if (vports) {
  for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
   shost = lpfc_shost_from_vport(vports[i]);
   scsi_unblock_requests(shost);
  }
  lpfc_destroy_vport_work_array(phba, vports);
 } else {
  shost = lpfc_shost_from_vport(phba->pport);
  scsi_unblock_requests(shost);
 }
 return;
}

/**
 * lpfc_sli3_bsg_diag_loopback_mode - process an sli3 bsg vendor command
 * @phba: Pointer to HBA context object.
 * @job: LPFC_BSG_VENDOR_DIAG_MODE
 *
 * This function is responsible for placing an sli3  port into diagnostic
 * loopback mode in order to perform a diagnostic loopback test.
 * All new scsi requests are blocked, a small delay is used to allow the
 * scsi requests to complete then the link is brought down. If the link is
 * is placed in loopback mode then scsi requests are again allowed
 * so the scsi mid-layer doesn't give up on the port.
 * All of this is done in-line.
 */

static int
lpfc_sli3_bsg_diag_loopback_mode(struct lpfc_hba *phba, struct bsg_job *job)
{
 struct fc_bsg_request *bsg_request = job->request;
 struct fc_bsg_reply *bsg_reply = job->reply;
 struct diag_mode_set *loopback_mode;
 uint32_t link_flags;
 uint32_t timeout;
 LPFC_MBOXQ_t *pmboxq  = NULL;
 int mbxstatus = MBX_SUCCESS;
 int i = 0;
 int rc = 0;

 /* no data to return just the return code */
 bsg_reply->reply_payload_rcv_len = 0;

 if (job->request_len < sizeof(struct fc_bsg_request) +
     sizeof(struct diag_mode_set)) {
  lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
    "2738 Received DIAG MODE request size:%d "
    "below the minimum size:%d\n",
    job->request_len,
    (int)(sizeof(struct fc_bsg_request) +
    sizeof(struct diag_mode_set)));
  rc = -EINVAL;
  goto job_error;
 }

 rc = lpfc_bsg_diag_mode_enter(phba);
 if (rc)
  goto job_error;

 /* bring the link to diagnostic mode */
 loopback_mode = (struct diag_mode_set *)
  bsg_request->rqst_data.h_vendor.vendor_cmd;
 link_flags = loopback_mode->type;
 timeout = loopback_mode->timeout * 100;

 pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
 if (!pmboxq) {
  rc = -ENOMEM;
  goto loopback_mode_exit;
 }
 memset((void *)pmboxq, 0, sizeof(LPFC_MBOXQ_t));
 pmboxq->u.mb.mbxCommand = MBX_DOWN_LINK;
 pmboxq->u.mb.mbxOwner = OWN_HOST;

 mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq, LPFC_MBOX_TMO);

 if ((mbxstatus == MBX_SUCCESS) && (pmboxq->u.mb.mbxStatus == 0)) {
  /* wait for link down before proceeding */
  i = 0;
  while (phba->link_state != LPFC_LINK_DOWN) {
   if (i++ > timeout) {
    rc = -ETIMEDOUT;
    goto loopback_mode_exit;
   }
   msleep(10);
  }

  memset((void *)pmboxq, 0, sizeof(LPFC_MBOXQ_t));
  if (link_flags == INTERNAL_LOOP_BACK)
   pmboxq->u.mb.un.varInitLnk.link_flags = FLAGS_LOCAL_LB;
  else
   pmboxq->u.mb.un.varInitLnk.link_flags =
    FLAGS_TOPOLOGY_MODE_LOOP;

  pmboxq->u.mb.mbxCommand = MBX_INIT_LINK;
  pmboxq->u.mb.mbxOwner = OWN_HOST;

  mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq,
           LPFC_MBOX_TMO);

  if ((mbxstatus != MBX_SUCCESS) || (pmboxq->u.mb.mbxStatus))
   rc = -ENODEV;
  else {
   spin_lock_irq(&phba->hbalock);
   phba->link_flag |= LS_LOOPBACK_MODE;
   spin_unlock_irq(&phba->hbalock);
   /* wait for the link attention interrupt */
   msleep(100);

   i = 0;
   while (phba->link_state != LPFC_HBA_READY) {
    if (i++ > timeout) {
     rc = -ETIMEDOUT;
     break;
    }

    msleep(10);
   }
  }

 } else
  rc = -ENODEV;

loopback_mode_exit:
 lpfc_bsg_diag_mode_exit(phba);

 /*
 * Let SLI layer release mboxq if mbox command completed after timeout.
 */

 if (pmboxq && mbxstatus != MBX_TIMEOUT)
  mempool_free(pmboxq, phba->mbox_mem_pool);

job_error:
 /* make error code available to userspace */
 bsg_reply->result = rc;
 /* complete the job back to userspace if no error */
 if (rc == 0)
  bsg_job_done(job, bsg_reply->result,
          bsg_reply->reply_payload_rcv_len);
 return rc;
}

/**
 * lpfc_sli4_bsg_set_link_diag_state - set sli4 link diag state
 * @phba: Pointer to HBA context object.
 * @diag: Flag for set link to diag or nomral operation state.
 *
 * This function is responsible for issuing a sli4 mailbox command for setting
 * link to either diag state or normal operation state.
 */

static int
lpfc_sli4_bsg_set_link_diag_state(struct lpfc_hba *phba, uint32_t diag)
{
 LPFC_MBOXQ_t *pmboxq;
 struct lpfc_mbx_set_link_diag_state *link_diag_state;
 uint32_t req_len, alloc_len;
 int mbxstatus = MBX_SUCCESS, rc;

 pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
 if (!pmboxq)
  return -ENOMEM;

 req_len = (sizeof(struct lpfc_mbx_set_link_diag_state) -
     sizeof(struct lpfc_sli4_cfg_mhdr));
 alloc_len = lpfc_sli4_config(phba, pmboxq, LPFC_MBOX_SUBSYSTEM_FCOE,
    LPFC_MBOX_OPCODE_FCOE_LINK_DIAG_STATE,
    req_len, LPFC_SLI4_MBX_EMBED);
 if (alloc_len != req_len) {
  rc = -ENOMEM;
  goto link_diag_state_set_out;
 }
 lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
   "3128 Set link to diagnostic state:x%x (x%x/x%x)\n",
   diag, phba->sli4_hba.lnk_info.lnk_tp,
   phba->sli4_hba.lnk_info.lnk_no);

 link_diag_state = &pmboxq->u.mqe.un.link_diag_state;
 bf_set(lpfc_mbx_set_diag_state_diag_bit_valid, &link_diag_state->u.req,
        LPFC_DIAG_STATE_DIAG_BIT_VALID_CHANGE);
 bf_set(lpfc_mbx_set_diag_state_link_num, &link_diag_state->u.req,
        phba->sli4_hba.lnk_info.lnk_no);
 bf_set(lpfc_mbx_set_diag_state_link_type, &link_diag_state->u.req,
        phba->sli4_hba.lnk_info.lnk_tp);
 if (diag)
  bf_set(lpfc_mbx_set_diag_state_diag,
         &link_diag_state->u.req, 1);
 else
  bf_set(lpfc_mbx_set_diag_state_diag,
         &link_diag_state->u.req, 0);

 mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq, LPFC_MBOX_TMO);

 if ((mbxstatus == MBX_SUCCESS) && (pmboxq->u.mb.mbxStatus == 0))
  rc = 0;
 else
  rc = -ENODEV;

link_diag_state_set_out:
 if (pmboxq && (mbxstatus != MBX_TIMEOUT))
  mempool_free(pmboxq, phba->mbox_mem_pool);

 return rc;
}

/**
 * lpfc_sli4_bsg_set_loopback_mode - set sli4 internal loopback diagnostic
 * @phba: Pointer to HBA context object.
 * @mode: loopback mode to set
 * @link_no: link number for loopback mode to set
 *
 * This function is responsible for issuing a sli4 mailbox command for setting
 * up loopback diagnostic for a link.
 */

static int
lpfc_sli4_bsg_set_loopback_mode(struct lpfc_hba *phba, int mode,
    uint32_t link_no)
{
 LPFC_MBOXQ_t *pmboxq;
 uint32_t req_len, alloc_len;
 struct lpfc_mbx_set_link_diag_loopback *link_diag_loopback;
 int mbxstatus = MBX_SUCCESS, rc = 0;

 pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
 if (!pmboxq)
  return -ENOMEM;
 req_len = (sizeof(struct lpfc_mbx_set_link_diag_loopback) -
     sizeof(struct lpfc_sli4_cfg_mhdr));
 alloc_len = lpfc_sli4_config(phba, pmboxq, LPFC_MBOX_SUBSYSTEM_FCOE,
    LPFC_MBOX_OPCODE_FCOE_LINK_DIAG_LOOPBACK,
    req_len, LPFC_SLI4_MBX_EMBED);
 if (alloc_len != req_len) {
  mempool_free(pmboxq, phba->mbox_mem_pool);
  return -ENOMEM;
 }
 link_diag_loopback = &pmboxq->u.mqe.un.link_diag_loopback;
 bf_set(lpfc_mbx_set_diag_state_link_num,
        &link_diag_loopback->u.req, link_no);

 if (phba->sli4_hba.conf_trunk & (1 << link_no)) {
  bf_set(lpfc_mbx_set_diag_state_link_type,
         &link_diag_loopback->u.req, LPFC_LNK_FC_TRUNKED);
 } else {
  bf_set(lpfc_mbx_set_diag_state_link_type,
         &link_diag_loopback->u.req,
         phba->sli4_hba.lnk_info.lnk_tp);
 }

 bf_set(lpfc_mbx_set_diag_lpbk_type, &link_diag_loopback->u.req,
        mode);

 mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq, LPFC_MBOX_TMO);
 if ((mbxstatus != MBX_SUCCESS) || (pmboxq->u.mb.mbxStatus)) {
  lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
    "3127 Failed setup loopback mode mailbox "
    "command, rc:x%x, status:x%x\n", mbxstatus,
    pmboxq->u.mb.mbxStatus);
  rc = -ENODEV;
 }
 if (pmboxq && (mbxstatus != MBX_TIMEOUT))
  mempool_free(pmboxq, phba->mbox_mem_pool);
 return rc;
}

/**
 * lpfc_sli4_diag_fcport_reg_setup - setup port registrations for diagnostic
 * @phba: Pointer to HBA context object.
 *
 * This function set up SLI4 FC port registrations for diagnostic run, which
 * includes all the rpis, vfi, and also vpi.
 */

static int
lpfc_sli4_diag_fcport_reg_setup(struct lpfc_hba *phba)
{
 if (test_bit(FC_VFI_REGISTERED, &phba->pport->fc_flag)) {
  lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
    "3136 Port still had vfi registered: "
    "mydid:x%x, fcfi:%d, vfi:%d, vpi:%d\n",
    phba->pport->fc_myDID, phba->fcf.fcfi,
    phba->sli4_hba.vfi_ids[phba->pport->vfi],
    phba->vpi_ids[phba->pport->vpi]);
  return -EINVAL;
 }
 return lpfc_issue_reg_vfi(phba->pport);
}

/**
 * lpfc_sli4_bsg_diag_loopback_mode - process an sli4 bsg vendor command
 * @phba: Pointer to HBA context object.
 * @job: LPFC_BSG_VENDOR_DIAG_MODE
 *
 * This function is responsible for placing an sli4 port into diagnostic
 * loopback mode in order to perform a diagnostic loopback test.
 */

static int
lpfc_sli4_bsg_diag_loopback_mode(struct lpfc_hba *phba, struct bsg_job *job)
{
 struct fc_bsg_request *bsg_request = job->request;
 struct fc_bsg_reply *bsg_reply = job->reply;
 struct diag_mode_set *loopback_mode;
 uint32_t link_flags, timeout, link_no;
 int i, rc = 0;

 /* no data to return just the return code */
 bsg_reply->reply_payload_rcv_len = 0;

 if (job->request_len < sizeof(struct fc_bsg_request) +
     sizeof(struct diag_mode_set)) {
  lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
    "3011 Received DIAG MODE request size:%d "
    "below the minimum size:%d\n",
    job->request_len,
    (int)(sizeof(struct fc_bsg_request) +
    sizeof(struct diag_mode_set)));
  rc = -EINVAL;
  goto job_done;
 }

 loopback_mode = (struct diag_mode_set *)
  bsg_request->rqst_data.h_vendor.vendor_cmd;
 link_flags = loopback_mode->type;
 timeout = loopback_mode->timeout * 100;

 if (loopback_mode->physical_link == -1)
  link_no = phba->sli4_hba.lnk_info.lnk_no;
 else
  link_no = loopback_mode->physical_link;

 if (link_flags == DISABLE_LOOP_BACK) {
  rc = lpfc_sli4_bsg_set_loopback_mode(phba,
     LPFC_DIAG_LOOPBACK_TYPE_DISABLE,
     link_no);
  if (!rc) {
   /* Unset the need disable bit */
   phba->sli4_hba.conf_trunk &= ~((1 << link_no) << 4);
  }
  goto job_done;
 } else {
  /* Check if we need to disable the loopback state */
  if (phba->sli4_hba.conf_trunk & ((1 << link_no) << 4)) {
   rc = -EPERM;
   goto job_done;
  }
 }

 rc = lpfc_bsg_diag_mode_enter(phba);
 if (rc)
  goto job_done;

 /* indicate we are in loobpack diagnostic mode */
 spin_lock_irq(&phba->hbalock);
 phba->link_flag |= LS_LOOPBACK_MODE;
 spin_unlock_irq(&phba->hbalock);

 /* reset port to start frome scratch */
 rc = lpfc_selective_reset(phba);
 if (rc)
  goto job_done;

 /* bring the link to diagnostic mode */
 lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
   "3129 Bring link to diagnostic state.\n");

 rc = lpfc_sli4_bsg_set_link_diag_state(phba, 1);
 if (rc) {
  lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
    "3130 Failed to bring link to diagnostic "
    "state, rc:x%x\n", rc);
  goto loopback_mode_exit;
 }

 /* wait for link down before proceeding */
 i = 0;
 while (phba->link_state != LPFC_LINK_DOWN) {
  if (i++ > timeout) {
   rc = -ETIMEDOUT;
   lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
     "3131 Timeout waiting for link to "
     "diagnostic mode, timeout:%d ms\n",
     timeout * 10);
   goto loopback_mode_exit;
  }
  msleep(10);
 }

 /* set up loopback mode */
 lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
   "3132 Set up loopback mode:x%x\n", link_flags);

 switch (link_flags) {
 case INTERNAL_LOOP_BACK:
  if (phba->sli4_hba.conf_trunk & (1 << link_no)) {
   rc = lpfc_sli4_bsg_set_loopback_mode(phba,
     LPFC_DIAG_LOOPBACK_TYPE_INTERNAL,
     link_no);
  } else {
   /* Trunk is configured, but link is not in this trunk */
   if (phba->sli4_hba.conf_trunk) {
    rc = -ELNRNG;
    goto loopback_mode_exit;
   }

   rc = lpfc_sli4_bsg_set_loopback_mode(phba,
     LPFC_DIAG_LOOPBACK_TYPE_INTERNAL,
     link_no);
  }

  if (!rc) {
   /* Set the need disable bit */
   phba->sli4_hba.conf_trunk |= (1 << link_no) << 4;
  }

  break;
 case EXTERNAL_LOOP_BACK:
  if (phba->sli4_hba.conf_trunk & (1 << link_no)) {
   rc = lpfc_sli4_bsg_set_loopback_mode(phba,
    LPFC_DIAG_LOOPBACK_TYPE_EXTERNAL_TRUNKED,
    link_no);
  } else {
   /* Trunk is configured, but link is not in this trunk */
   if (phba->sli4_hba.conf_trunk) {
    rc = -ELNRNG;
    goto loopback_mode_exit;
   }

   rc = lpfc_sli4_bsg_set_loopback_mode(phba,
      LPFC_DIAG_LOOPBACK_TYPE_SERDES,
      link_no);
  }

  if (!rc) {
   /* Set the need disable bit */
   phba->sli4_hba.conf_trunk |= (1 << link_no) << 4;
  }

  break;
 default:
  rc = -EINVAL;
  lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
    "3141 Loopback mode:x%x not supported\n",
    link_flags);
  goto loopback_mode_exit;
 }

 if (!rc) {
  /* wait for the link attention interrupt */
  msleep(100);
  i = 0;
  while (phba->link_state < LPFC_LINK_UP) {
   if (i++ > timeout) {
    rc = -ETIMEDOUT;
    lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
     "3137 Timeout waiting for link up "
     "in loopback mode, timeout:%d ms\n",
     timeout * 10);
    break;
   }
   msleep(10);
  }
 }

 /* port resource registration setup for loopback diagnostic */
 if (!rc) {
  /* set up a none zero myDID for loopback test */
  phba->pport->fc_myDID = 1;
  rc = lpfc_sli4_diag_fcport_reg_setup(phba);
 } else
  goto loopback_mode_exit;

 if (!rc) {
  /* wait for the port ready */
  msleep(100);
  i = 0;
  while (phba->link_state != LPFC_HBA_READY) {
   if (i++ > timeout) {
    rc = -ETIMEDOUT;
    lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
     "3133 Timeout waiting for port "
     "loopback mode ready, timeout:%d ms\n",
     timeout * 10);
    break;
   }
   msleep(10);
  }
 }

loopback_mode_exit:
 /* clear loopback diagnostic mode */
 if (rc) {
  spin_lock_irq(&phba->hbalock);
  phba->link_flag &= ~LS_LOOPBACK_MODE;
  spin_unlock_irq(&phba->hbalock);
 }
 lpfc_bsg_diag_mode_exit(phba);

job_done:
 /* make error code available to userspace */
 bsg_reply->result = rc;
 /* complete the job back to userspace if no error */
 if (rc == 0)
  bsg_job_done(job, bsg_reply->result,
          bsg_reply->reply_payload_rcv_len);
 return rc;
}

/**
 * lpfc_bsg_diag_loopback_mode - bsg vendor command for diag loopback mode
 * @job: LPFC_BSG_VENDOR_DIAG_MODE
 *
 * This function is responsible for responding to check and dispatch bsg diag
 * command from the user to proper driver action routines.
 */

static int
lpfc_bsg_diag_loopback_mode(struct bsg_job *job)
{
 struct Scsi_Host *shost;
 struct lpfc_vport *vport;
 struct lpfc_hba *phba;
 int rc;

 shost = fc_bsg_to_shost(job);
 if (!shost)
  return -ENODEV;
 vport = shost_priv(shost);
 if (!vport)
  return -ENODEV;
 phba = vport->phba;
 if (!phba)
  return -ENODEV;

 if (phba->sli_rev < LPFC_SLI_REV4)
  rc = lpfc_sli3_bsg_diag_loopback_mode(phba, job);
 else if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) >=
   LPFC_SLI_INTF_IF_TYPE_2)
  rc = lpfc_sli4_bsg_diag_loopback_mode(phba, job);
 else
  rc = -ENODEV;

 return rc;
}

/**
 * lpfc_sli4_bsg_diag_mode_end - sli4 bsg vendor command for ending diag mode
 * @job: LPFC_BSG_VENDOR_DIAG_MODE_END
 *
 * This function is responsible for responding to check and dispatch bsg diag
 * command from the user to proper driver action routines.
 */

static int
lpfc_sli4_bsg_diag_mode_end(struct bsg_job *job)
{
 struct fc_bsg_request *bsg_request = job->request;
 struct fc_bsg_reply *bsg_reply = job->reply;
 struct Scsi_Host *shost;
 struct lpfc_vport *vport;
 struct lpfc_hba *phba;
 struct diag_mode_set *loopback_mode_end_cmd;
 uint32_t timeout;
 int rc, i;

 shost = fc_bsg_to_shost(job);
 if (!shost)
  return -ENODEV;
 vport = shost_priv(shost);
 if (!vport)
  return -ENODEV;
 phba = vport->phba;
 if (!phba)
  return -ENODEV;

 if (phba->sli_rev < LPFC_SLI_REV4)
  return -ENODEV;
 if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) <
     LPFC_SLI_INTF_IF_TYPE_2)
  return -ENODEV;

 /* clear loopback diagnostic mode */
 spin_lock_irq(&phba->hbalock);
 phba->link_flag &= ~LS_LOOPBACK_MODE;
 spin_unlock_irq(&phba->hbalock);
 loopback_mode_end_cmd = (struct diag_mode_set *)
   bsg_request->rqst_data.h_vendor.vendor_cmd;
 timeout = loopback_mode_end_cmd->timeout * 100;

 rc = lpfc_sli4_bsg_set_link_diag_state(phba, 0);
 if (rc) {
  lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
    "3139 Failed to bring link to diagnostic "
    "state, rc:x%x\n", rc);
  goto loopback_mode_end_exit;
 }

 /* wait for link down before proceeding */
 i = 0;
 while (phba->link_state != LPFC_LINK_DOWN) {
  if (i++ > timeout) {
   lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
     "3140 Timeout waiting for link to "
     "diagnostic mode_end, timeout:%d ms\n",
     timeout * 10);
   /* there is nothing much we can do here */
   break;
  }
  msleep(10);
 }

 /* reset port resource registrations */
 rc = lpfc_selective_reset(phba);
 phba->pport->fc_myDID = 0;

loopback_mode_end_exit:
 /* make return code available to userspace */
 bsg_reply->result = rc;
 /* complete the job back to userspace if no error */
 if (rc == 0)
  bsg_job_done(job, bsg_reply->result,
          bsg_reply->reply_payload_rcv_len);
 return rc;
}

/**
 * lpfc_sli4_bsg_link_diag_test - sli4 bsg vendor command for diag link test
 * @job: LPFC_BSG_VENDOR_DIAG_LINK_TEST
 *
 * This function is to perform SLI4 diag link test request from the user
 * applicaiton.
 */

static int
lpfc_sli4_bsg_link_diag_test(struct bsg_job *job)
{
 struct fc_bsg_request *bsg_request = job->request;
 struct fc_bsg_reply *bsg_reply = job->reply;
 struct Scsi_Host *shost;
 struct lpfc_vport *vport;
 struct lpfc_hba *phba;
 LPFC_MBOXQ_t *pmboxq;
 struct sli4_link_diag *link_diag_test_cmd;
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=95 H=90 G=92

¤ Dauer der Verarbeitung: 0.12 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.