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 684 kB image not shown  

Quelle  lpfc_sli.c   Sprache: C

 
/*******************************************************************
 * This file is part of the Emulex Linux Device Driver for         *
 * Fibre Channel Host Bus Adapters.                                *
 * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term *
 * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.     *
 * Copyright (C) 2004-2016 Emulex.  All rights reserved.           *
 * EMULEX and SLI are trademarks of Emulex.                        *
 * www.broadcom.com                                                *
 * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
 *                                                                 *
 * 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/blkdev.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/lockdep.h>

#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
#include <scsi/fc/fc_fs.h>
#include <linux/crash_dump.h>
#ifdef CONFIG_X86
#include <asm/set_memory.h>
#endif

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

/* There are only four IOCB completion types. */
typedef enum _lpfc_iocb_type {
 LPFC_UNKNOWN_IOCB,
 LPFC_UNSOL_IOCB,
 LPFC_SOL_IOCB,
 LPFC_ABORT_IOCB
} lpfc_iocb_type;


/* Provide function prototypes local to this module. */
static int lpfc_sli_issue_mbox_s4(struct lpfc_hba *, LPFC_MBOXQ_t *,
      uint32_t);
static int lpfc_sli4_read_rev(struct lpfc_hba *, LPFC_MBOXQ_t *,
         uint8_t *, uint32_t *);
static struct lpfc_iocbq *
lpfc_sli4_els_preprocess_rspiocbq(struct lpfc_hba *phba,
      struct lpfc_iocbq *rspiocbq);
static void lpfc_sli4_send_seq_to_ulp(struct lpfc_vport *,
          struct hbq_dmabuf *);
static void lpfc_sli4_handle_mds_loopback(struct lpfc_vport *vport,
       struct hbq_dmabuf *dmabuf);
static bool lpfc_sli4_fp_handle_cqe(struct lpfc_hba *phba,
       struct lpfc_queue *cq, struct lpfc_cqe *cqe);
static int lpfc_sli4_post_sgl_list(struct lpfc_hba *, struct list_head *,
           int);
static void lpfc_sli4_hba_handle_eqe(struct lpfc_hba *phba,
         struct lpfc_queue *eq,
         struct lpfc_eqe *eqe,
         enum lpfc_poll_mode poll_mode);
static bool lpfc_sli4_mbox_completions_pending(struct lpfc_hba *phba);
static bool lpfc_sli4_process_missed_mbox_completions(struct lpfc_hba *phba);
static struct lpfc_cqe *lpfc_sli4_cq_get(struct lpfc_queue *q);
static void __lpfc_sli4_consume_cqe(struct lpfc_hba *phba,
        struct lpfc_queue *cq,
        struct lpfc_cqe *cqe);
static uint16_t lpfc_wqe_bpl2sgl(struct lpfc_hba *phba,
     struct lpfc_iocbq *pwqeq,
     struct lpfc_sglq *sglq);

union lpfc_wqe128 lpfc_iread_cmd_template;
union lpfc_wqe128 lpfc_iwrite_cmd_template;
union lpfc_wqe128 lpfc_icmnd_cmd_template;

/* Setup WQE templates for IOs */
void lpfc_wqe_cmd_template(void)
{
 union lpfc_wqe128 *wqe;

 /* IREAD template */
 wqe = &lpfc_iread_cmd_template;
 memset(wqe, 0, sizeof(union lpfc_wqe128));

 /* Word 0, 1, 2 - BDE is variable */

 /* Word 3 - cmd_buff_len, payload_offset_len is zero */

 /* Word 4 - total_xfer_len is variable */

 /* Word 5 - is zero */

 /* Word 6 - ctxt_tag, xri_tag is variable */

 /* Word 7 */
 bf_set(wqe_cmnd, &wqe->fcp_iread.wqe_com, CMD_FCP_IREAD64_WQE);
 bf_set(wqe_pu, &wqe->fcp_iread.wqe_com, PARM_READ_CHECK);
 bf_set(wqe_class, &wqe->fcp_iread.wqe_com, CLASS3);
 bf_set(wqe_ct, &wqe->fcp_iread.wqe_com, SLI4_CT_RPI);

 /* Word 8 - abort_tag is variable */

 /* Word 9  - reqtag is variable */

 /* Word 10 - dbde, wqes is variable */
 bf_set(wqe_qosd, &wqe->fcp_iread.wqe_com, 0);
 bf_set(wqe_iod, &wqe->fcp_iread.wqe_com, LPFC_WQE_IOD_READ);
 bf_set(wqe_lenloc, &wqe->fcp_iread.wqe_com, LPFC_WQE_LENLOC_WORD4);
 bf_set(wqe_dbde, &wqe->fcp_iread.wqe_com, 0);
 bf_set(wqe_wqes, &wqe->fcp_iread.wqe_com, 1);

 /* Word 11 - pbde is variable */
 bf_set(wqe_cmd_type, &wqe->fcp_iread.wqe_com, COMMAND_DATA_IN);
 bf_set(wqe_cqid, &wqe->fcp_iread.wqe_com, LPFC_WQE_CQ_ID_DEFAULT);
 bf_set(wqe_pbde, &wqe->fcp_iread.wqe_com, 0);

 /* Word 12 - is zero */

 /* Word 13, 14, 15 - PBDE is variable */

 /* IWRITE template */
 wqe = &lpfc_iwrite_cmd_template;
 memset(wqe, 0, sizeof(union lpfc_wqe128));

 /* Word 0, 1, 2 - BDE is variable */

 /* Word 3 - cmd_buff_len, payload_offset_len is zero */

 /* Word 4 - total_xfer_len is variable */

 /* Word 5 - initial_xfer_len is variable */

 /* Word 6 - ctxt_tag, xri_tag is variable */

 /* Word 7 */
 bf_set(wqe_cmnd, &wqe->fcp_iwrite.wqe_com, CMD_FCP_IWRITE64_WQE);
 bf_set(wqe_pu, &wqe->fcp_iwrite.wqe_com, PARM_READ_CHECK);
 bf_set(wqe_class, &wqe->fcp_iwrite.wqe_com, CLASS3);
 bf_set(wqe_ct, &wqe->fcp_iwrite.wqe_com, SLI4_CT_RPI);

 /* Word 8 - abort_tag is variable */

 /* Word 9  - reqtag is variable */

 /* Word 10 - dbde, wqes is variable */
 bf_set(wqe_qosd, &wqe->fcp_iwrite.wqe_com, 0);
 bf_set(wqe_iod, &wqe->fcp_iwrite.wqe_com, LPFC_WQE_IOD_WRITE);
 bf_set(wqe_lenloc, &wqe->fcp_iwrite.wqe_com, LPFC_WQE_LENLOC_WORD4);
 bf_set(wqe_dbde, &wqe->fcp_iwrite.wqe_com, 0);
 bf_set(wqe_wqes, &wqe->fcp_iwrite.wqe_com, 1);

 /* Word 11 - pbde is variable */
 bf_set(wqe_cmd_type, &wqe->fcp_iwrite.wqe_com, COMMAND_DATA_OUT);
 bf_set(wqe_cqid, &wqe->fcp_iwrite.wqe_com, LPFC_WQE_CQ_ID_DEFAULT);
 bf_set(wqe_pbde, &wqe->fcp_iwrite.wqe_com, 0);

 /* Word 12 - is zero */

 /* Word 13, 14, 15 - PBDE is variable */

 /* ICMND template */
 wqe = &lpfc_icmnd_cmd_template;
 memset(wqe, 0, sizeof(union lpfc_wqe128));

 /* Word 0, 1, 2 - BDE is variable */

 /* Word 3 - payload_offset_len is variable */

 /* Word 4, 5 - is zero */

 /* Word 6 - ctxt_tag, xri_tag is variable */

 /* Word 7 */
 bf_set(wqe_cmnd, &wqe->fcp_icmd.wqe_com, CMD_FCP_ICMND64_WQE);
 bf_set(wqe_pu, &wqe->fcp_icmd.wqe_com, 0);
 bf_set(wqe_class, &wqe->fcp_icmd.wqe_com, CLASS3);
 bf_set(wqe_ct, &wqe->fcp_icmd.wqe_com, SLI4_CT_RPI);

 /* Word 8 - abort_tag is variable */

 /* Word 9  - reqtag is variable */

 /* Word 10 - dbde, wqes is variable */
 bf_set(wqe_qosd, &wqe->fcp_icmd.wqe_com, 1);
 bf_set(wqe_iod, &wqe->fcp_icmd.wqe_com, LPFC_WQE_IOD_NONE);
 bf_set(wqe_lenloc, &wqe->fcp_icmd.wqe_com, LPFC_WQE_LENLOC_NONE);
 bf_set(wqe_dbde, &wqe->fcp_icmd.wqe_com, 0);
 bf_set(wqe_wqes, &wqe->fcp_icmd.wqe_com, 1);

 /* Word 11 */
 bf_set(wqe_cmd_type, &wqe->fcp_icmd.wqe_com, COMMAND_DATA_IN);
 bf_set(wqe_cqid, &wqe->fcp_icmd.wqe_com, LPFC_WQE_CQ_ID_DEFAULT);
 bf_set(wqe_pbde, &wqe->fcp_icmd.wqe_com, 0);

 /* Word 12, 13, 14, 15 - is zero */
}

#if defined(CONFIG_64BIT) && defined(__LITTLE_ENDIAN)
/**
 * lpfc_sli4_pcimem_bcopy - SLI4 memory copy function
 * @srcp: Source memory pointer.
 * @destp: Destination memory pointer.
 * @cnt: Number of words required to be copied.
 *       Must be a multiple of sizeof(uint64_t)
 *
 * This function is used for copying data between driver memory
 * and the SLI WQ. This function also changes the endianness
 * of each word if native endianness is different from SLI
 * endianness. This function can be called with or without
 * lock.
 **/

static void
lpfc_sli4_pcimem_bcopy(void *srcp, void *destp, uint32_t cnt)
{
 uint64_t *src = srcp;
 uint64_t *dest = destp;
 int i;

 for (i = 0; i < (int)cnt; i += sizeof(uint64_t))
  *dest++ = *src++;
}
#else
#define lpfc_sli4_pcimem_bcopy(a, b, c) lpfc_sli_pcimem_bcopy(a, b, c)
#endif

/**
 * lpfc_sli4_wq_put - Put a Work Queue Entry on an Work Queue
 * @q: The Work Queue to operate on.
 * @wqe: The work Queue Entry to put on the Work queue.
 *
 * This routine will copy the contents of @wqe to the next available entry on
 * the @q. This function will then ring the Work Queue Doorbell to signal the
 * HBA to start processing the Work Queue Entry. This function returns 0 if
 * successful. If no entries are available on @q then this function will return
 * -ENOMEM.
 * The caller is expected to hold the hbalock when calling this routine.
 **/

static int
lpfc_sli4_wq_put(struct lpfc_queue *q, union lpfc_wqe128 *wqe)
{
 union lpfc_wqe *temp_wqe;
 struct lpfc_register doorbell;
 uint32_t host_index;
 uint32_t idx;
 uint32_t i = 0;
 uint8_t *tmp;
 u32 if_type;

 /* sanity check on queue memory */
 if (unlikely(!q))
  return -ENOMEM;

 temp_wqe = lpfc_sli4_qe(q, q->host_index);

 /* If the host has not yet processed the next entry then we are done */
 idx = ((q->host_index + 1) % q->entry_count);
 if (idx == q->hba_index) {
  q->WQ_overflow++;
  return -EBUSY;
 }
 q->WQ_posted++;
 /* set consumption flag every once in a while */
 if (!((q->host_index + 1) % q->notify_interval))
  bf_set(wqe_wqec, &wqe->generic.wqe_com, 1);
 else
  bf_set(wqe_wqec, &wqe->generic.wqe_com, 0);
 if (q->phba->sli3_options & LPFC_SLI4_PHWQ_ENABLED)
  bf_set(wqe_wqid, &wqe->generic.wqe_com, q->queue_id);
 lpfc_sli4_pcimem_bcopy(wqe, temp_wqe, q->entry_size);
 if (q->dpp_enable && q->phba->cfg_enable_dpp) {
  /* write to DPP aperture taking advatage of Combined Writes */
  tmp = (uint8_t *)temp_wqe;
#ifdef __raw_writeq
  for (i = 0; i < q->entry_size; i += sizeof(uint64_t))
   __raw_writeq(*((uint64_t *)(tmp + i)),
     q->dpp_regaddr + i);
#else
  for (i = 0; i < q->entry_size; i += sizeof(uint32_t))
   __raw_writel(*((uint32_t *)(tmp + i)),
     q->dpp_regaddr + i);
#endif
 }
 /* ensure WQE bcopy and DPP flushed before doorbell write */
 wmb();

 /* Update the host index before invoking device */
 host_index = q->host_index;

 q->host_index = idx;

 /* Ring Doorbell */
 doorbell.word0 = 0;
 if (q->db_format == LPFC_DB_LIST_FORMAT) {
  if (q->dpp_enable && q->phba->cfg_enable_dpp) {
   bf_set(lpfc_if6_wq_db_list_fm_num_posted, &doorbell, 1);
   bf_set(lpfc_if6_wq_db_list_fm_dpp, &doorbell, 1);
   bf_set(lpfc_if6_wq_db_list_fm_dpp_id, &doorbell,
       q->dpp_id);
   bf_set(lpfc_if6_wq_db_list_fm_id, &doorbell,
       q->queue_id);
  } else {
   bf_set(lpfc_wq_db_list_fm_num_posted, &doorbell, 1);
   bf_set(lpfc_wq_db_list_fm_id, &doorbell, q->queue_id);

   /* Leave bits <23:16> clear for if_type 6 dpp */
   if_type = bf_get(lpfc_sli_intf_if_type,
      &q->phba->sli4_hba.sli_intf);
   if (if_type != LPFC_SLI_INTF_IF_TYPE_6)
    bf_set(lpfc_wq_db_list_fm_index, &doorbell,
           host_index);
  }
 } else if (q->db_format == LPFC_DB_RING_FORMAT) {
  bf_set(lpfc_wq_db_ring_fm_num_posted, &doorbell, 1);
  bf_set(lpfc_wq_db_ring_fm_id, &doorbell, q->queue_id);
 } else {
  return -EINVAL;
 }
 writel(doorbell.word0, q->db_regaddr);

 return 0;
}

/**
 * lpfc_sli4_wq_release - Updates internal hba index for WQ
 * @q: The Work Queue to operate on.
 * @index: The index to advance the hba index to.
 *
 * This routine will update the HBA index of a queue to reflect consumption of
 * Work Queue Entries by the HBA. When the HBA indicates that it has consumed
 * an entry the host calls this function to update the queue's internal
 * pointers.
 **/

static void
lpfc_sli4_wq_release(struct lpfc_queue *q, uint32_t index)
{
 /* sanity check on queue memory */
 if (unlikely(!q))
  return;

 q->hba_index = index;
}

/**
 * lpfc_sli4_mq_put - Put a Mailbox Queue Entry on an Mailbox Queue
 * @q: The Mailbox Queue to operate on.
 * @mqe: The Mailbox Queue Entry to put on the Work queue.
 *
 * This routine will copy the contents of @mqe to the next available entry on
 * the @q. This function will then ring the Work Queue Doorbell to signal the
 * HBA to start processing the Work Queue Entry. This function returns 0 if
 * successful. If no entries are available on @q then this function will return
 * -ENOMEM.
 * The caller is expected to hold the hbalock when calling this routine.
 **/

static uint32_t
lpfc_sli4_mq_put(struct lpfc_queue *q, struct lpfc_mqe *mqe)
{
 struct lpfc_mqe *temp_mqe;
 struct lpfc_register doorbell;

 /* sanity check on queue memory */
 if (unlikely(!q))
  return -ENOMEM;
 temp_mqe = lpfc_sli4_qe(q, q->host_index);

 /* If the host has not yet processed the next entry then we are done */
 if (((q->host_index + 1) % q->entry_count) == q->hba_index)
  return -ENOMEM;
 lpfc_sli4_pcimem_bcopy(mqe, temp_mqe, q->entry_size);
 /* Save off the mailbox pointer for completion */
 q->phba->mbox = (MAILBOX_t *)temp_mqe;

 /* Update the host index before invoking device */
 q->host_index = ((q->host_index + 1) % q->entry_count);

 /* Ring Doorbell */
 doorbell.word0 = 0;
 bf_set(lpfc_mq_doorbell_num_posted, &doorbell, 1);
 bf_set(lpfc_mq_doorbell_id, &doorbell, q->queue_id);
 writel(doorbell.word0, q->phba->sli4_hba.MQDBregaddr);
 return 0;
}

/**
 * lpfc_sli4_mq_release - Updates internal hba index for MQ
 * @q: The Mailbox Queue to operate on.
 *
 * This routine will update the HBA index of a queue to reflect consumption of
 * a Mailbox Queue Entry by the HBA. When the HBA indicates that it has consumed
 * an entry the host calls this function to update the queue's internal
 * pointers. This routine returns the number of entries that were consumed by
 * the HBA.
 **/

static uint32_t
lpfc_sli4_mq_release(struct lpfc_queue *q)
{
 /* sanity check on queue memory */
 if (unlikely(!q))
  return 0;

 /* Clear the mailbox pointer for completion */
 q->phba->mbox = NULL;
 q->hba_index = ((q->hba_index + 1) % q->entry_count);
 return 1;
}

/**
 * lpfc_sli4_eq_get - Gets the next valid EQE from a EQ
 * @q: The Event Queue to get the first valid EQE from
 *
 * This routine will get the first valid Event Queue Entry from @q, update
 * the queue's internal hba index, and return the EQE. If no valid EQEs are in
 * the Queue (no more work to do), or the Queue is full of EQEs that have been
 * processed, but not popped back to the HBA then this routine will return NULL.
 **/

static struct lpfc_eqe *
lpfc_sli4_eq_get(struct lpfc_queue *q)
{
 struct lpfc_eqe *eqe;

 /* sanity check on queue memory */
 if (unlikely(!q))
  return NULL;
 eqe = lpfc_sli4_qe(q, q->host_index);

 /* If the next EQE is not valid then we are done */
 if (bf_get_le32(lpfc_eqe_valid, eqe) != q->qe_valid)
  return NULL;

 /*
 * insert barrier for instruction interlock : data from the hardware
 * must have the valid bit checked before it can be copied and acted
 * upon. Speculative instructions were allowing a bcopy at the start
 * of lpfc_sli4_fp_handle_wcqe(), which is called immediately
 * after our return, to copy data before the valid bit check above
 * was done. As such, some of the copied data was stale. The barrier
 * ensures the check is before any data is copied.
 */

 mb();
 return eqe;
}

/**
 * lpfc_sli4_eq_clr_intr - Turn off interrupts from this EQ
 * @q: The Event Queue to disable interrupts
 *
 **/

void
lpfc_sli4_eq_clr_intr(struct lpfc_queue *q)
{
 struct lpfc_register doorbell;

 doorbell.word0 = 0;
 bf_set(lpfc_eqcq_doorbell_eqci, &doorbell, 1);
 bf_set(lpfc_eqcq_doorbell_qt, &doorbell, LPFC_QUEUE_TYPE_EVENT);
 bf_set(lpfc_eqcq_doorbell_eqid_hi, &doorbell,
  (q->queue_id >> LPFC_EQID_HI_FIELD_SHIFT));
 bf_set(lpfc_eqcq_doorbell_eqid_lo, &doorbell, q->queue_id);
 writel(doorbell.word0, q->phba->sli4_hba.EQDBregaddr);
}

/**
 * lpfc_sli4_if6_eq_clr_intr - Turn off interrupts from this EQ
 * @q: The Event Queue to disable interrupts
 *
 **/

void
lpfc_sli4_if6_eq_clr_intr(struct lpfc_queue *q)
{
 struct lpfc_register doorbell;

 doorbell.word0 = 0;
 bf_set(lpfc_if6_eq_doorbell_eqid, &doorbell, q->queue_id);
 writel(doorbell.word0, q->phba->sli4_hba.EQDBregaddr);
}

/**
 * lpfc_sli4_write_eq_db - write EQ DB for eqe's consumed or arm state
 * @phba: adapter with EQ
 * @q: The Event Queue that the host has completed processing for.
 * @count: Number of elements that have been consumed
 * @arm: Indicates whether the host wants to arms this CQ.
 *
 * This routine will notify the HBA, by ringing the doorbell, that count
 * number of EQEs have been processed. The @arm parameter indicates whether
 * the queue should be rearmed when ringing the doorbell.
 **/

void
lpfc_sli4_write_eq_db(struct lpfc_hba *phba, struct lpfc_queue *q,
       uint32_t count, bool arm)
{
 struct lpfc_register doorbell;

 /* sanity check on queue memory */
 if (unlikely(!q || (count == 0 && !arm)))
  return;

 /* ring doorbell for number popped */
 doorbell.word0 = 0;
 if (arm) {
  bf_set(lpfc_eqcq_doorbell_arm, &doorbell, 1);
  bf_set(lpfc_eqcq_doorbell_eqci, &doorbell, 1);
 }
 bf_set(lpfc_eqcq_doorbell_num_released, &doorbell, count);
 bf_set(lpfc_eqcq_doorbell_qt, &doorbell, LPFC_QUEUE_TYPE_EVENT);
 bf_set(lpfc_eqcq_doorbell_eqid_hi, &doorbell,
   (q->queue_id >> LPFC_EQID_HI_FIELD_SHIFT));
 bf_set(lpfc_eqcq_doorbell_eqid_lo, &doorbell, q->queue_id);
 writel(doorbell.word0, q->phba->sli4_hba.EQDBregaddr);
 /* PCI read to flush PCI pipeline on re-arming for INTx mode */
 if ((q->phba->intr_type == INTx) && (arm == LPFC_QUEUE_REARM))
  readl(q->phba->sli4_hba.EQDBregaddr);
}

/**
 * lpfc_sli4_if6_write_eq_db - write EQ DB for eqe's consumed or arm state
 * @phba: adapter with EQ
 * @q: The Event Queue that the host has completed processing for.
 * @count: Number of elements that have been consumed
 * @arm: Indicates whether the host wants to arms this CQ.
 *
 * This routine will notify the HBA, by ringing the doorbell, that count
 * number of EQEs have been processed. The @arm parameter indicates whether
 * the queue should be rearmed when ringing the doorbell.
 **/

void
lpfc_sli4_if6_write_eq_db(struct lpfc_hba *phba, struct lpfc_queue *q,
     uint32_t count, bool arm)
{
 struct lpfc_register doorbell;

 /* sanity check on queue memory */
 if (unlikely(!q || (count == 0 && !arm)))
  return;

 /* ring doorbell for number popped */
 doorbell.word0 = 0;
 if (arm)
  bf_set(lpfc_if6_eq_doorbell_arm, &doorbell, 1);
 bf_set(lpfc_if6_eq_doorbell_num_released, &doorbell, count);
 bf_set(lpfc_if6_eq_doorbell_eqid, &doorbell, q->queue_id);
 writel(doorbell.word0, q->phba->sli4_hba.EQDBregaddr);
 /* PCI read to flush PCI pipeline on re-arming for INTx mode */
 if ((q->phba->intr_type == INTx) && (arm == LPFC_QUEUE_REARM))
  readl(q->phba->sli4_hba.EQDBregaddr);
}

static void
__lpfc_sli4_consume_eqe(struct lpfc_hba *phba, struct lpfc_queue *eq,
   struct lpfc_eqe *eqe)
{
 if (!phba->sli4_hba.pc_sli4_params.eqav)
  bf_set_le32(lpfc_eqe_valid, eqe, 0);

 eq->host_index = ((eq->host_index + 1) % eq->entry_count);

 /* if the index wrapped around, toggle the valid bit */
 if (phba->sli4_hba.pc_sli4_params.eqav && !eq->host_index)
  eq->qe_valid = (eq->qe_valid) ? 0 : 1;
}

static void
lpfc_sli4_eqcq_flush(struct lpfc_hba *phba, struct lpfc_queue *eq)
{
 struct lpfc_eqe *eqe = NULL;
 u32 eq_count = 0, cq_count = 0;
 struct lpfc_cqe *cqe = NULL;
 struct lpfc_queue *cq = NULL, *childq = NULL;
 int cqid = 0;

 /* walk all the EQ entries and drop on the floor */
 eqe = lpfc_sli4_eq_get(eq);
 while (eqe) {
  /* Get the reference to the corresponding CQ */
  cqid = bf_get_le32(lpfc_eqe_resource_id, eqe);
  cq = NULL;

  list_for_each_entry(childq, &eq->child_list, list) {
   if (childq->queue_id == cqid) {
    cq = childq;
    break;
   }
  }
  /* If CQ is valid, iterate through it and drop all the CQEs */
  if (cq) {
   cqe = lpfc_sli4_cq_get(cq);
   while (cqe) {
    __lpfc_sli4_consume_cqe(phba, cq, cqe);
    cq_count++;
    cqe = lpfc_sli4_cq_get(cq);
   }
   /* Clear and re-arm the CQ */
   phba->sli4_hba.sli4_write_cq_db(phba, cq, cq_count,
       LPFC_QUEUE_REARM);
   cq_count = 0;
  }
  __lpfc_sli4_consume_eqe(phba, eq, eqe);
  eq_count++;
  eqe = lpfc_sli4_eq_get(eq);
 }

 /* Clear and re-arm the EQ */
 phba->sli4_hba.sli4_write_eq_db(phba, eq, eq_count, LPFC_QUEUE_REARM);
}

static int
lpfc_sli4_process_eq(struct lpfc_hba *phba, struct lpfc_queue *eq,
       u8 rearm, enum lpfc_poll_mode poll_mode)
{
 struct lpfc_eqe *eqe;
 int count = 0, consumed = 0;

 if (cmpxchg(&eq->queue_claimed, 0, 1) != 0)
  goto rearm_and_exit;

 eqe = lpfc_sli4_eq_get(eq);
 while (eqe) {
  lpfc_sli4_hba_handle_eqe(phba, eq, eqe, poll_mode);
  __lpfc_sli4_consume_eqe(phba, eq, eqe);

  consumed++;
  if (!(++count % eq->max_proc_limit))
   break;

  if (!(count % eq->notify_interval)) {
   phba->sli4_hba.sli4_write_eq_db(phba, eq, consumed,
       LPFC_QUEUE_NOARM);
   consumed = 0;
  }

  eqe = lpfc_sli4_eq_get(eq);
 }
 eq->EQ_processed += count;

 /* Track the max number of EQEs processed in 1 intr */
 if (count > eq->EQ_max_eqe)
  eq->EQ_max_eqe = count;

 xchg(&eq->queue_claimed, 0);

rearm_and_exit:
 /* Always clear the EQ. */
 phba->sli4_hba.sli4_write_eq_db(phba, eq, consumed, rearm);

 return count;
}

/**
 * lpfc_sli4_cq_get - Gets the next valid CQE from a CQ
 * @q: The Completion Queue to get the first valid CQE from
 *
 * This routine will get the first valid Completion Queue Entry from @q, update
 * the queue's internal hba index, and return the CQE. If no valid CQEs are in
 * the Queue (no more work to do), or the Queue is full of CQEs that have been
 * processed, but not popped back to the HBA then this routine will return NULL.
 **/

static struct lpfc_cqe *
lpfc_sli4_cq_get(struct lpfc_queue *q)
{
 struct lpfc_cqe *cqe;

 /* sanity check on queue memory */
 if (unlikely(!q))
  return NULL;
 cqe = lpfc_sli4_qe(q, q->host_index);

 /* If the next CQE is not valid then we are done */
 if (bf_get_le32(lpfc_cqe_valid, cqe) != q->qe_valid)
  return NULL;

 /*
 * insert barrier for instruction interlock : data from the hardware
 * must have the valid bit checked before it can be copied and acted
 * upon. Given what was seen in lpfc_sli4_cq_get() of speculative
 * instructions allowing action on content before valid bit checked,
 * add barrier here as well. May not be needed as "content" is a
 * single 32-bit entity here (vs multi word structure for cq's).
 */

 mb();
 return cqe;
}

static void
__lpfc_sli4_consume_cqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
   struct lpfc_cqe *cqe)
{
 if (!phba->sli4_hba.pc_sli4_params.cqav)
  bf_set_le32(lpfc_cqe_valid, cqe, 0);

 cq->host_index = ((cq->host_index + 1) % cq->entry_count);

 /* if the index wrapped around, toggle the valid bit */
 if (phba->sli4_hba.pc_sli4_params.cqav && !cq->host_index)
  cq->qe_valid = (cq->qe_valid) ? 0 : 1;
}

/**
 * lpfc_sli4_write_cq_db - write cq DB for entries consumed or arm state.
 * @phba: the adapter with the CQ
 * @q: The Completion Queue that the host has completed processing for.
 * @count: the number of elements that were consumed
 * @arm: Indicates whether the host wants to arms this CQ.
 *
 * This routine will notify the HBA, by ringing the doorbell, that the
 * CQEs have been processed. The @arm parameter specifies whether the
 * queue should be rearmed when ringing the doorbell.
 **/

void
lpfc_sli4_write_cq_db(struct lpfc_hba *phba, struct lpfc_queue *q,
       uint32_t count, bool arm)
{
 struct lpfc_register doorbell;

 /* sanity check on queue memory */
 if (unlikely(!q || (count == 0 && !arm)))
  return;

 /* ring doorbell for number popped */
 doorbell.word0 = 0;
 if (arm)
  bf_set(lpfc_eqcq_doorbell_arm, &doorbell, 1);
 bf_set(lpfc_eqcq_doorbell_num_released, &doorbell, count);
 bf_set(lpfc_eqcq_doorbell_qt, &doorbell, LPFC_QUEUE_TYPE_COMPLETION);
 bf_set(lpfc_eqcq_doorbell_cqid_hi, &doorbell,
   (q->queue_id >> LPFC_CQID_HI_FIELD_SHIFT));
 bf_set(lpfc_eqcq_doorbell_cqid_lo, &doorbell, q->queue_id);
 writel(doorbell.word0, q->phba->sli4_hba.CQDBregaddr);
}

/**
 * lpfc_sli4_if6_write_cq_db - write cq DB for entries consumed or arm state.
 * @phba: the adapter with the CQ
 * @q: The Completion Queue that the host has completed processing for.
 * @count: the number of elements that were consumed
 * @arm: Indicates whether the host wants to arms this CQ.
 *
 * This routine will notify the HBA, by ringing the doorbell, that the
 * CQEs have been processed. The @arm parameter specifies whether the
 * queue should be rearmed when ringing the doorbell.
 **/

void
lpfc_sli4_if6_write_cq_db(struct lpfc_hba *phba, struct lpfc_queue *q,
    uint32_t count, bool arm)
{
 struct lpfc_register doorbell;

 /* sanity check on queue memory */
 if (unlikely(!q || (count == 0 && !arm)))
  return;

 /* ring doorbell for number popped */
 doorbell.word0 = 0;
 if (arm)
  bf_set(lpfc_if6_cq_doorbell_arm, &doorbell, 1);
 bf_set(lpfc_if6_cq_doorbell_num_released, &doorbell, count);
 bf_set(lpfc_if6_cq_doorbell_cqid, &doorbell, q->queue_id);
 writel(doorbell.word0, q->phba->sli4_hba.CQDBregaddr);
}

/*
 * lpfc_sli4_rq_put - Put a Receive Buffer Queue Entry on a Receive Queue
 *
 * This routine will copy the contents of @wqe to the next available entry on
 * the @q. This function will then ring the Receive Queue Doorbell to signal the
 * HBA to start processing the Receive Queue Entry. This function returns the
 * index that the rqe was copied to if successful. If no entries are available
 * on @q then this function will return -ENOMEM.
 * The caller is expected to hold the hbalock when calling this routine.
 **/

int
lpfc_sli4_rq_put(struct lpfc_queue *hq, struct lpfc_queue *dq,
   struct lpfc_rqe *hrqe, struct lpfc_rqe *drqe)
{
 struct lpfc_rqe *temp_hrqe;
 struct lpfc_rqe *temp_drqe;
 struct lpfc_register doorbell;
 int hq_put_index;
 int dq_put_index;

 /* sanity check on queue memory */
 if (unlikely(!hq) || unlikely(!dq))
  return -ENOMEM;
 hq_put_index = hq->host_index;
 dq_put_index = dq->host_index;
 temp_hrqe = lpfc_sli4_qe(hq, hq_put_index);
 temp_drqe = lpfc_sli4_qe(dq, dq_put_index);

 if (hq->type != LPFC_HRQ || dq->type != LPFC_DRQ)
  return -EINVAL;
 if (hq_put_index != dq_put_index)
  return -EINVAL;
 /* If the host has not yet processed the next entry then we are done */
 if (((hq_put_index + 1) % hq->entry_count) == hq->hba_index)
  return -EBUSY;
 lpfc_sli4_pcimem_bcopy(hrqe, temp_hrqe, hq->entry_size);
 lpfc_sli4_pcimem_bcopy(drqe, temp_drqe, dq->entry_size);

 /* Update the host index to point to the next slot */
 hq->host_index = ((hq_put_index + 1) % hq->entry_count);
 dq->host_index = ((dq_put_index + 1) % dq->entry_count);
 hq->RQ_buf_posted++;

 /* Ring The Header Receive Queue Doorbell */
 if (!(hq->host_index % hq->notify_interval)) {
  doorbell.word0 = 0;
  if (hq->db_format == LPFC_DB_RING_FORMAT) {
   bf_set(lpfc_rq_db_ring_fm_num_posted, &doorbell,
          hq->notify_interval);
   bf_set(lpfc_rq_db_ring_fm_id, &doorbell, hq->queue_id);
  } else if (hq->db_format == LPFC_DB_LIST_FORMAT) {
   bf_set(lpfc_rq_db_list_fm_num_posted, &doorbell,
          hq->notify_interval);
   bf_set(lpfc_rq_db_list_fm_index, &doorbell,
          hq->host_index);
   bf_set(lpfc_rq_db_list_fm_id, &doorbell, hq->queue_id);
  } else {
   return -EINVAL;
  }
  writel(doorbell.word0, hq->db_regaddr);
 }
 return hq_put_index;
}

/*
 * lpfc_sli4_rq_release - Updates internal hba index for RQ
 *
 * This routine will update the HBA index of a queue to reflect consumption of
 * one Receive Queue Entry by the HBA. When the HBA indicates that it has
 * consumed an entry the host calls this function to update the queue's
 * internal pointers. This routine returns the number of entries that were
 * consumed by the HBA.
 **/

static uint32_t
lpfc_sli4_rq_release(struct lpfc_queue *hq, struct lpfc_queue *dq)
{
 /* sanity check on queue memory */
 if (unlikely(!hq) || unlikely(!dq))
  return 0;

 if ((hq->type != LPFC_HRQ) || (dq->type != LPFC_DRQ))
  return 0;
 hq->hba_index = ((hq->hba_index + 1) % hq->entry_count);
 dq->hba_index = ((dq->hba_index + 1) % dq->entry_count);
 return 1;
}

/**
 * lpfc_cmd_iocb - Get next command iocb entry in the ring
 * @phba: Pointer to HBA context object.
 * @pring: Pointer to driver SLI ring object.
 *
 * This function returns pointer to next command iocb entry
 * in the command ring. The caller must hold hbalock to prevent
 * other threads consume the next command iocb.
 * SLI-2/SLI-3 provide different sized iocbs.
 **/

static inline IOCB_t *
lpfc_cmd_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
{
 return (IOCB_t *) (((char *) pring->sli.sli3.cmdringaddr) +
      pring->sli.sli3.cmdidx * phba->iocb_cmd_size);
}

/**
 * lpfc_resp_iocb - Get next response iocb entry in the ring
 * @phba: Pointer to HBA context object.
 * @pring: Pointer to driver SLI ring object.
 *
 * This function returns pointer to next response iocb entry
 * in the response ring. The caller must hold hbalock to make sure
 * that no other thread consume the next response iocb.
 * SLI-2/SLI-3 provide different sized iocbs.
 **/

static inline IOCB_t *
lpfc_resp_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
{
 return (IOCB_t *) (((char *) pring->sli.sli3.rspringaddr) +
      pring->sli.sli3.rspidx * phba->iocb_rsp_size);
}

/**
 * __lpfc_sli_get_iocbq - Allocates an iocb object from iocb pool
 * @phba: Pointer to HBA context object.
 *
 * This function is called with hbalock held. This function
 * allocates a new driver iocb object from the iocb pool. If the
 * allocation is successful, it returns pointer to the newly
 * allocated iocb object else it returns NULL.
 **/

struct lpfc_iocbq *
__lpfc_sli_get_iocbq(struct lpfc_hba *phba)
{
 struct list_head *lpfc_iocb_list = &phba->lpfc_iocb_list;
 struct lpfc_iocbq * iocbq = NULL;

 lockdep_assert_held(&phba->hbalock);

 list_remove_head(lpfc_iocb_list, iocbq, struct lpfc_iocbq, list);
 if (iocbq)
  phba->iocb_cnt++;
 if (phba->iocb_cnt > phba->iocb_max)
  phba->iocb_max = phba->iocb_cnt;
 return iocbq;
}

/**
 * __lpfc_clear_active_sglq - Remove the active sglq for this XRI.
 * @phba: Pointer to HBA context object.
 * @xritag: XRI value.
 *
 * This function clears the sglq pointer from the array of active
 * sglq's. The xritag that is passed in is used to index into the
 * array. Before the xritag can be used it needs to be adjusted
 * by subtracting the xribase.
 *
 * Returns sglq ponter = success, NULL = Failure.
 **/

struct lpfc_sglq *
__lpfc_clear_active_sglq(struct lpfc_hba *phba, uint16_t xritag)
{
 struct lpfc_sglq *sglq;

 sglq = phba->sli4_hba.lpfc_sglq_active_list[xritag];
 phba->sli4_hba.lpfc_sglq_active_list[xritag] = NULL;
 return sglq;
}

/**
 * __lpfc_get_active_sglq - Get the active sglq for this XRI.
 * @phba: Pointer to HBA context object.
 * @xritag: XRI value.
 *
 * This function returns the sglq pointer from the array of active
 * sglq's. The xritag that is passed in is used to index into the
 * array. Before the xritag can be used it needs to be adjusted
 * by subtracting the xribase.
 *
 * Returns sglq ponter = success, NULL = Failure.
 **/

struct lpfc_sglq *
__lpfc_get_active_sglq(struct lpfc_hba *phba, uint16_t xritag)
{
 struct lpfc_sglq *sglq;

 sglq =  phba->sli4_hba.lpfc_sglq_active_list[xritag];
 return sglq;
}

/**
 * lpfc_clr_rrq_active - Clears RRQ active bit in xri_bitmap.
 * @phba: Pointer to HBA context object.
 * @xritag: xri used in this exchange.
 * @rrq: The RRQ to be cleared.
 *
 **/

void
lpfc_clr_rrq_active(struct lpfc_hba *phba,
      uint16_t xritag,
      struct lpfc_node_rrq *rrq)
{
 struct lpfc_nodelist *ndlp = NULL;

 /* Lookup did to verify if did is still active on this vport */
 if (rrq->vport)
  ndlp = lpfc_findnode_did(rrq->vport, rrq->nlp_DID);

 if (!ndlp)
  goto out;

 if (test_and_clear_bit(xritag, ndlp->active_rrqs_xri_bitmap)) {
  rrq->send_rrq = 0;
  rrq->xritag = 0;
  rrq->rrq_stop_time = 0;
 }
out:
 mempool_free(rrq, phba->rrq_pool);
}

/**
 * lpfc_handle_rrq_active - Checks if RRQ has waithed RATOV.
 * @phba: Pointer to HBA context object.
 *
 * This function is called with hbalock held. This function
 * Checks if stop_time (ratov from setting rrq active) has
 * been reached, if it has and the send_rrq flag is set then
 * it will call lpfc_send_rrq. If the send_rrq flag is not set
 * then it will just call the routine to clear the rrq and
 * free the rrq resource.
 * The timer is set to the next rrq that is going to expire before
 * leaving the routine.
 *
 **/

void
lpfc_handle_rrq_active(struct lpfc_hba *phba)
{
 struct lpfc_node_rrq *rrq;
 struct lpfc_node_rrq *nextrrq;
 unsigned long next_time;
 unsigned long iflags;
 LIST_HEAD(send_rrq);

 clear_bit(HBA_RRQ_ACTIVE, &phba->hba_flag);
 next_time = jiffies + secs_to_jiffies(phba->fc_ratov + 1);
 spin_lock_irqsave(&phba->rrq_list_lock, iflags);
 list_for_each_entry_safe(rrq, nextrrq,
     &phba->active_rrq_list, list) {
  if (time_after(jiffies, rrq->rrq_stop_time))
   list_move(&rrq->list, &send_rrq);
  else if (time_before(rrq->rrq_stop_time, next_time))
   next_time = rrq->rrq_stop_time;
 }
 spin_unlock_irqrestore(&phba->rrq_list_lock, iflags);
 if ((!list_empty(&phba->active_rrq_list)) &&
     (!test_bit(FC_UNLOADING, &phba->pport->load_flag)))
  mod_timer(&phba->rrq_tmr, next_time);
 list_for_each_entry_safe(rrq, nextrrq, &send_rrq, list) {
  list_del(&rrq->list);
  if (!rrq->send_rrq) {
   /* this call will free the rrq */
   lpfc_clr_rrq_active(phba, rrq->xritag, rrq);
  } else if (lpfc_send_rrq(phba, rrq)) {
   /* if we send the rrq then the completion handler
*  will clear the bit in the xribitmap.
*/

   lpfc_clr_rrq_active(phba, rrq->xritag,
         rrq);
  }
 }
}

/**
 * lpfc_get_active_rrq - Get the active RRQ for this exchange.
 * @vport: Pointer to vport context object.
 * @xri: The xri used in the exchange.
 * @did: The targets DID for this exchange.
 *
 * returns NULL = rrq not found in the phba->active_rrq_list.
 *         rrq = rrq for this xri and target.
 **/

struct lpfc_node_rrq *
lpfc_get_active_rrq(struct lpfc_vport *vport, uint16_t xri, uint32_t did)
{
 struct lpfc_hba *phba = vport->phba;
 struct lpfc_node_rrq *rrq;
 struct lpfc_node_rrq *nextrrq;
 unsigned long iflags;

 if (phba->sli_rev != LPFC_SLI_REV4)
  return NULL;
 spin_lock_irqsave(&phba->rrq_list_lock, iflags);
 list_for_each_entry_safe(rrq, nextrrq, &phba->active_rrq_list, list) {
  if (rrq->vport == vport && rrq->xritag == xri &&
    rrq->nlp_DID == did){
   list_del(&rrq->list);
   spin_unlock_irqrestore(&phba->rrq_list_lock, iflags);
   return rrq;
  }
 }
 spin_unlock_irqrestore(&phba->rrq_list_lock, iflags);
 return NULL;
}

/**
 * lpfc_cleanup_vports_rrqs - Remove and clear the active RRQ for this vport.
 * @vport: Pointer to vport context object.
 * @ndlp: Pointer to the lpfc_node_list structure.
 * If ndlp is NULL Remove all active RRQs for this vport from the
 * phba->active_rrq_list and clear the rrq.
 * If ndlp is not NULL then only remove rrqs for this vport & this ndlp.
 **/

void
lpfc_cleanup_vports_rrqs(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)

{
 struct lpfc_hba *phba = vport->phba;
 struct lpfc_node_rrq *rrq;
 struct lpfc_node_rrq *nextrrq;
 unsigned long iflags;
 LIST_HEAD(rrq_list);

 if (phba->sli_rev != LPFC_SLI_REV4)
  return;
 if (!ndlp) {
  lpfc_sli4_vport_delete_els_xri_aborted(vport);
  lpfc_sli4_vport_delete_fcp_xri_aborted(vport);
 }
 spin_lock_irqsave(&phba->rrq_list_lock, iflags);
 list_for_each_entry_safe(rrq, nextrrq, &phba->active_rrq_list, list) {
  if (rrq->vport != vport)
   continue;

  if (!ndlp || ndlp == lpfc_findnode_did(vport, rrq->nlp_DID))
   list_move(&rrq->list, &rrq_list);

 }
 spin_unlock_irqrestore(&phba->rrq_list_lock, iflags);

 list_for_each_entry_safe(rrq, nextrrq, &rrq_list, list) {
  list_del(&rrq->list);
  lpfc_clr_rrq_active(phba, rrq->xritag, rrq);
 }
}

/**
 * lpfc_test_rrq_active - Test RRQ bit in xri_bitmap.
 * @phba: Pointer to HBA context object.
 * @ndlp: Targets nodelist pointer for this exchange.
 * @xritag: the xri in the bitmap to test.
 *
 * This function returns:
 * 0 = rrq not active for this xri
 * 1 = rrq is valid for this xri.
 **/

int
lpfc_test_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
   uint16_t  xritag)
{
 if (!ndlp)
  return 0;
 if (!ndlp->active_rrqs_xri_bitmap)
  return 0;
 if (test_bit(xritag, ndlp->active_rrqs_xri_bitmap))
  return 1;
 else
  return 0;
}

/**
 * lpfc_set_rrq_active - set RRQ active bit in xri_bitmap.
 * @phba: Pointer to HBA context object.
 * @ndlp: nodelist pointer for this target.
 * @xritag: xri used in this exchange.
 * @rxid: Remote Exchange ID.
 * @send_rrq: Flag used to determine if we should send rrq els cmd.
 *
 * This function takes the hbalock.
 * The active bit is always set in the active rrq xri_bitmap even
 * if there is no slot avaiable for the other rrq information.
 *
 * returns 0 rrq actived for this xri
 *         < 0 No memory or invalid ndlp.
 **/

int
lpfc_set_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
      uint16_t xritag, uint16_t rxid, uint16_t send_rrq)
{
 unsigned long iflags;
 struct lpfc_node_rrq *rrq;
 int empty;

 if (!ndlp)
  return -EINVAL;

 if (!phba->cfg_enable_rrq)
  return -EINVAL;

 if (test_bit(FC_UNLOADING, &phba->pport->load_flag)) {
  clear_bit(HBA_RRQ_ACTIVE, &phba->hba_flag);
  goto outnl;
 }

 spin_lock_irqsave(&phba->hbalock, iflags);
 if (ndlp->vport && test_bit(FC_UNLOADING, &ndlp->vport->load_flag))
  goto out;

 if (!ndlp->active_rrqs_xri_bitmap)
  goto out;

 if (test_and_set_bit(xritag, ndlp->active_rrqs_xri_bitmap))
  goto out;

 spin_unlock_irqrestore(&phba->hbalock, iflags);
 rrq = mempool_alloc(phba->rrq_pool, GFP_ATOMIC);
 if (!rrq) {
  lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
    "3155 Unable to allocate RRQ xri:0x%x rxid:0x%x"
    " DID:0x%x Send:%d\n",
    xritag, rxid, ndlp->nlp_DID, send_rrq);
  return -EINVAL;
 }
 if (phba->cfg_enable_rrq == 1)
  rrq->send_rrq = send_rrq;
 else
  rrq->send_rrq = 0;
 rrq->xritag = xritag;
 rrq->rrq_stop_time = jiffies + secs_to_jiffies(phba->fc_ratov + 1);
 rrq->nlp_DID = ndlp->nlp_DID;
 rrq->vport = ndlp->vport;
 rrq->rxid = rxid;

 spin_lock_irqsave(&phba->rrq_list_lock, iflags);
 empty = list_empty(&phba->active_rrq_list);
 list_add_tail(&rrq->list, &phba->active_rrq_list);
 spin_unlock_irqrestore(&phba->rrq_list_lock, iflags);
 set_bit(HBA_RRQ_ACTIVE, &phba->hba_flag);
 if (empty)
  lpfc_worker_wake_up(phba);
 return 0;
out:
 spin_unlock_irqrestore(&phba->hbalock, iflags);
outnl:
 lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
   "2921 Can't set rrq active xri:0x%x rxid:0x%x"
   " DID:0x%x Send:%d\n",
   xritag, rxid, ndlp->nlp_DID, send_rrq);
 return -EINVAL;
}

/**
 * __lpfc_sli_get_els_sglq - Allocates an iocb object from sgl pool
 * @phba: Pointer to HBA context object.
 * @piocbq: Pointer to the iocbq.
 *
 * The driver calls this function with either the nvme ls ring lock
 * or the fc els ring lock held depending on the iocb usage.  This function
 * gets a new driver sglq object from the sglq list. If the list is not empty
 * then it is successful, it returns pointer to the newly allocated sglq
 * object else it returns NULL.
 **/

static struct lpfc_sglq *
__lpfc_sli_get_els_sglq(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq)
{
 struct list_head *lpfc_els_sgl_list = &phba->sli4_hba.lpfc_els_sgl_list;
 struct lpfc_sglq *sglq = NULL;
 struct lpfc_sglq *start_sglq = NULL;
 struct lpfc_io_buf *lpfc_cmd;
 struct lpfc_nodelist *ndlp;
 int found = 0;
 u8 cmnd;

 cmnd = get_job_cmnd(phba, piocbq);

 if (piocbq->cmd_flag & LPFC_IO_FCP) {
  lpfc_cmd = piocbq->io_buf;
  ndlp = lpfc_cmd->rdata->pnode;
 } else  if ((cmnd == CMD_GEN_REQUEST64_CR) &&
   !(piocbq->cmd_flag & LPFC_IO_LIBDFC)) {
  ndlp = piocbq->ndlp;
 } else  if (piocbq->cmd_flag & LPFC_IO_LIBDFC) {
  if (piocbq->cmd_flag & LPFC_IO_LOOPBACK)
   ndlp = NULL;
  else
   ndlp = piocbq->ndlp;
 } else {
  ndlp = piocbq->ndlp;
 }

 spin_lock(&phba->sli4_hba.sgl_list_lock);
 list_remove_head(lpfc_els_sgl_list, sglq, struct lpfc_sglq, list);
 start_sglq = sglq;
 while (!found) {
  if (!sglq)
   break;
  if (ndlp && ndlp->active_rrqs_xri_bitmap &&
      test_bit(sglq->sli4_lxritag,
      ndlp->active_rrqs_xri_bitmap)) {
   /* This xri has an rrq outstanding for this DID.
 * put it back in the list and get another xri.
 */

   list_add_tail(&sglq->list, lpfc_els_sgl_list);
   sglq = NULL;
   list_remove_head(lpfc_els_sgl_list, sglq,
      struct lpfc_sglq, list);
   if (sglq == start_sglq) {
    list_add_tail(&sglq->list, lpfc_els_sgl_list);
    sglq = NULL;
    break;
   } else
    continue;
  }
  sglq->ndlp = ndlp;
  found = 1;
  phba->sli4_hba.lpfc_sglq_active_list[sglq->sli4_lxritag] = sglq;
  sglq->state = SGL_ALLOCATED;
 }
 spin_unlock(&phba->sli4_hba.sgl_list_lock);
 return sglq;
}

/**
 * __lpfc_sli_get_nvmet_sglq - Allocates an iocb object from sgl pool
 * @phba: Pointer to HBA context object.
 * @piocbq: Pointer to the iocbq.
 *
 * This function is called with the sgl_list lock held. This function
 * gets a new driver sglq object from the sglq list. If the
 * list is not empty then it is successful, it returns pointer to the newly
 * allocated sglq object else it returns NULL.
 **/

struct lpfc_sglq *
__lpfc_sli_get_nvmet_sglq(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq)
{
 struct list_head *lpfc_nvmet_sgl_list;
 struct lpfc_sglq *sglq = NULL;

 lpfc_nvmet_sgl_list = &phba->sli4_hba.lpfc_nvmet_sgl_list;

 lockdep_assert_held(&phba->sli4_hba.sgl_list_lock);

 list_remove_head(lpfc_nvmet_sgl_list, sglq, struct lpfc_sglq, list);
 if (!sglq)
  return NULL;
 phba->sli4_hba.lpfc_sglq_active_list[sglq->sli4_lxritag] = sglq;
 sglq->state = SGL_ALLOCATED;
 return sglq;
}

/**
 * lpfc_sli_get_iocbq - Allocates an iocb object from iocb pool
 * @phba: Pointer to HBA context object.
 *
 * This function is called with no lock held. This function
 * allocates a new driver iocb object from the iocb pool. If the
 * allocation is successful, it returns pointer to the newly
 * allocated iocb object else it returns NULL.
 **/

struct lpfc_iocbq *
lpfc_sli_get_iocbq(struct lpfc_hba *phba)
{
 struct lpfc_iocbq * iocbq = NULL;
 unsigned long iflags;

 spin_lock_irqsave(&phba->hbalock, iflags);
 iocbq = __lpfc_sli_get_iocbq(phba);
 spin_unlock_irqrestore(&phba->hbalock, iflags);
 return iocbq;
}

/**
 * __lpfc_sli_release_iocbq_s4 - Release iocb to the iocb pool
 * @phba: Pointer to HBA context object.
 * @iocbq: Pointer to driver iocb object.
 *
 * This function is called to release the driver iocb object
 * to the iocb pool. The iotag in the iocb object
 * does not change for each use of the iocb object. This function
 * clears all other fields of the iocb object when it is freed.
 * The sqlq structure that holds the xritag and phys and virtual
 * mappings for the scatter gather list is retrieved from the
 * active array of sglq. The get of the sglq pointer also clears
 * the entry in the array. If the status of the IO indiactes that
 * this IO was aborted then the sglq entry it put on the
 * lpfc_abts_els_sgl_list until the CQ_ABORTED_XRI is received. If the
 * IO has good status or fails for any other reason then the sglq
 * entry is added to the free list (lpfc_els_sgl_list). The hbalock is
 *  asserted held in the code path calling this routine.
 **/

static void
__lpfc_sli_release_iocbq_s4(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
{
 struct lpfc_sglq *sglq;
 unsigned long iflag = 0;
 struct lpfc_sli_ring *pring;

 if (iocbq->sli4_xritag == NO_XRI)
  sglq = NULL;
 else
  sglq = __lpfc_clear_active_sglq(phba, iocbq->sli4_lxritag);


 if (sglq)  {
  if (iocbq->cmd_flag & LPFC_IO_NVMET) {
   spin_lock_irqsave(&phba->sli4_hba.sgl_list_lock,
       iflag);
   sglq->state = SGL_FREED;
   sglq->ndlp = NULL;
   list_add_tail(&sglq->list,
          &phba->sli4_hba.lpfc_nvmet_sgl_list);
   spin_unlock_irqrestore(
    &phba->sli4_hba.sgl_list_lock, iflag);
   goto out;
  }

  if ((iocbq->cmd_flag & LPFC_EXCHANGE_BUSY) &&
      (!(unlikely(pci_channel_offline(phba->pcidev)))) &&
      sglq->state != SGL_XRI_ABORTED) {
   spin_lock_irqsave(&phba->sli4_hba.sgl_list_lock,
       iflag);

   /* Check if we can get a reference on ndlp */
   if (sglq->ndlp && !lpfc_nlp_get(sglq->ndlp))
    sglq->ndlp = NULL;

   list_add(&sglq->list,
     &phba->sli4_hba.lpfc_abts_els_sgl_list);
   spin_unlock_irqrestore(
    &phba->sli4_hba.sgl_list_lock, iflag);
  } else {
   spin_lock_irqsave(&phba->sli4_hba.sgl_list_lock,
       iflag);
   sglq->state = SGL_FREED;
   sglq->ndlp = NULL;
   list_add_tail(&sglq->list,
          &phba->sli4_hba.lpfc_els_sgl_list);
   spin_unlock_irqrestore(
    &phba->sli4_hba.sgl_list_lock, iflag);
   pring = lpfc_phba_elsring(phba);
   /* Check if TXQ queue needs to be serviced */
   if (pring && (!list_empty(&pring->txq)))
    lpfc_worker_wake_up(phba);
  }
 }

out:
 /*
 * Clean all volatile data fields, preserve iotag and node struct.
 */

 memset_startat(iocbq, 0, wqe);
 iocbq->sli4_lxritag = NO_XRI;
 iocbq->sli4_xritag = NO_XRI;
 iocbq->cmd_flag &= ~(LPFC_IO_NVME | LPFC_IO_NVMET | LPFC_IO_CMF |
         LPFC_IO_NVME_LS);
 list_add_tail(&iocbq->list, &phba->lpfc_iocb_list);
}


/**
 * __lpfc_sli_release_iocbq_s3 - Release iocb to the iocb pool
 * @phba: Pointer to HBA context object.
 * @iocbq: Pointer to driver iocb object.
 *
 * This function is called to release the driver iocb object to the
 * iocb pool. The iotag in the iocb object does not change for each
 * use of the iocb object. This function clears all other fields of
 * the iocb object when it is freed. The hbalock is asserted held in
 * the code path calling this routine.
 **/

static void
__lpfc_sli_release_iocbq_s3(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
{

 /*
 * Clean all volatile data fields, preserve iotag and node struct.
 */

 memset_startat(iocbq, 0, iocb);
 iocbq->sli4_xritag = NO_XRI;
 list_add_tail(&iocbq->list, &phba->lpfc_iocb_list);
}

/**
 * __lpfc_sli_release_iocbq - Release iocb to the iocb pool
 * @phba: Pointer to HBA context object.
 * @iocbq: Pointer to driver iocb object.
 *
 * This function is called with hbalock held to release driver
 * iocb object to the iocb pool. The iotag in the iocb object
 * does not change for each use of the iocb object. This function
 * clears all other fields of the iocb object when it is freed.
 **/

static void
__lpfc_sli_release_iocbq(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
{
 lockdep_assert_held(&phba->hbalock);

 phba->__lpfc_sli_release_iocbq(phba, iocbq);
 phba->iocb_cnt--;
}

/**
 * lpfc_sli_release_iocbq - Release iocb to the iocb pool
 * @phba: Pointer to HBA context object.
 * @iocbq: Pointer to driver iocb object.
 *
 * This function is called with no lock held to release the iocb to
 * iocb pool.
 **/

void
lpfc_sli_release_iocbq(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
{
 unsigned long iflags;

 /*
 * Clean all volatile data fields, preserve iotag and node struct.
 */

 spin_lock_irqsave(&phba->hbalock, iflags);
 __lpfc_sli_release_iocbq(phba, iocbq);
 spin_unlock_irqrestore(&phba->hbalock, iflags);
}

/**
 * lpfc_sli_cancel_iocbs - Cancel all iocbs from a list.
 * @phba: Pointer to HBA context object.
 * @iocblist: List of IOCBs.
 * @ulpstatus: ULP status in IOCB command field.
 * @ulpWord4: ULP word-4 in IOCB command field.
 *
 * This function is called with a list of IOCBs to cancel. It cancels the IOCB
 * on the list by invoking the complete callback function associated with the
 * IOCB with the provided @ulpstatus and @ulpword4 set to the IOCB commond
 * fields.
 **/

void
lpfc_sli_cancel_iocbs(struct lpfc_hba *phba, struct list_head *iocblist,
        uint32_t ulpstatus, uint32_t ulpWord4)
{
 struct lpfc_iocbq *piocb;

 while (!list_empty(iocblist)) {
  list_remove_head(iocblist, piocb, struct lpfc_iocbq, list);
  if (piocb->cmd_cmpl) {
   if (piocb->cmd_flag & LPFC_IO_NVME) {
    lpfc_nvme_cancel_iocb(phba, piocb,
            ulpstatus, ulpWord4);
   } else {
    if (phba->sli_rev == LPFC_SLI_REV4) {
     bf_set(lpfc_wcqe_c_status,
            &piocb->wcqe_cmpl, ulpstatus);
     piocb->wcqe_cmpl.parameter = ulpWord4;
    } else {
     piocb->iocb.ulpStatus = ulpstatus;
     piocb->iocb.un.ulpWord[4] = ulpWord4;
    }
    (piocb->cmd_cmpl) (phba, piocb, piocb);
   }
  } else {
   lpfc_sli_release_iocbq(phba, piocb);
  }
 }
 return;
}

/**
 * lpfc_sli_iocb_cmd_type - Get the iocb type
 * @iocb_cmnd: iocb command code.
 *
 * This function is called by ring event handler function to get the iocb type.
 * This function translates the iocb command to an iocb command type used to
 * decide the final disposition of each completed IOCB.
 * The function returns
 * LPFC_UNKNOWN_IOCB if it is an unsupported iocb
 * LPFC_SOL_IOCB     if it is a solicited iocb completion
 * LPFC_ABORT_IOCB   if it is an abort iocb
 * LPFC_UNSOL_IOCB   if it is an unsolicited iocb
 *
 * The caller is not required to hold any lock.
 **/

static lpfc_iocb_type
lpfc_sli_iocb_cmd_type(uint8_t iocb_cmnd)
{
 lpfc_iocb_type type = LPFC_UNKNOWN_IOCB;

 if (iocb_cmnd > CMD_MAX_IOCB_CMD)
  return 0;

 switch (iocb_cmnd) {
 case CMD_XMIT_SEQUENCE_CR:
 case CMD_XMIT_SEQUENCE_CX:
 case CMD_XMIT_BCAST_CN:
 case CMD_XMIT_BCAST_CX:
 case CMD_ELS_REQUEST_CR:
 case CMD_ELS_REQUEST_CX:
 case CMD_CREATE_XRI_CR:
 case CMD_CREATE_XRI_CX:
 case CMD_GET_RPI_CN:
 case CMD_XMIT_ELS_RSP_CX:
 case CMD_GET_RPI_CR:
 case CMD_FCP_IWRITE_CR:
 case CMD_FCP_IWRITE_CX:
 case CMD_FCP_IREAD_CR:
 case CMD_FCP_IREAD_CX:
 case CMD_FCP_ICMND_CR:
 case CMD_FCP_ICMND_CX:
 case CMD_FCP_TSEND_CX:
 case CMD_FCP_TRSP_CX:
 case CMD_FCP_TRECEIVE_CX:
 case CMD_FCP_AUTO_TRSP_CX:
 case CMD_ADAPTER_MSG:
 case CMD_ADAPTER_DUMP:
 case CMD_XMIT_SEQUENCE64_CR:
 case CMD_XMIT_SEQUENCE64_CX:
 case CMD_XMIT_BCAST64_CN:
 case CMD_XMIT_BCAST64_CX:
 case CMD_ELS_REQUEST64_CR:
 case CMD_ELS_REQUEST64_CX:
 case CMD_FCP_IWRITE64_CR:
 case CMD_FCP_IWRITE64_CX:
 case CMD_FCP_IREAD64_CR:
 case CMD_FCP_IREAD64_CX:
 case CMD_FCP_ICMND64_CR:
 case CMD_FCP_ICMND64_CX:
 case CMD_FCP_TSEND64_CX:
 case CMD_FCP_TRSP64_CX:
 case CMD_FCP_TRECEIVE64_CX:
 case CMD_GEN_REQUEST64_CR:
 case CMD_GEN_REQUEST64_CX:
 case CMD_XMIT_ELS_RSP64_CX:
 case DSSCMD_IWRITE64_CR:
 case DSSCMD_IWRITE64_CX:
 case DSSCMD_IREAD64_CR:
 case DSSCMD_IREAD64_CX:
 case CMD_SEND_FRAME:
  type = LPFC_SOL_IOCB;
  break;
 case CMD_ABORT_XRI_CN:
 case CMD_ABORT_XRI_CX:
 case CMD_CLOSE_XRI_CN:
 case CMD_CLOSE_XRI_CX:
 case CMD_XRI_ABORTED_CX:
 case CMD_ABORT_MXRI64_CN:
 case CMD_XMIT_BLS_RSP64_CX:
  type = LPFC_ABORT_IOCB;
  break;
 case CMD_RCV_SEQUENCE_CX:
 case CMD_RCV_ELS_REQ_CX:
 case CMD_RCV_SEQUENCE64_CX:
 case CMD_RCV_ELS_REQ64_CX:
 case CMD_ASYNC_STATUS:
 case CMD_IOCB_RCV_SEQ64_CX:
 case CMD_IOCB_RCV_ELS64_CX:
 case CMD_IOCB_RCV_CONT64_CX:
 case CMD_IOCB_RET_XRI64_CX:
  type = LPFC_UNSOL_IOCB;
  break;
 case CMD_IOCB_XMIT_MSEQ64_CR:
 case CMD_IOCB_XMIT_MSEQ64_CX:
 case CMD_IOCB_RCV_SEQ_LIST64_CX:
 case CMD_IOCB_RCV_ELS_LIST64_CX:
 case CMD_IOCB_CLOSE_EXTENDED_CN:
 case CMD_IOCB_ABORT_EXTENDED_CN:
 case CMD_IOCB_RET_HBQE64_CN:
 case CMD_IOCB_FCP_IBIDIR64_CR:
 case CMD_IOCB_FCP_IBIDIR64_CX:
 case CMD_IOCB_FCP_ITASKMGT64_CX:
 case CMD_IOCB_LOGENTRY_CN:
 case CMD_IOCB_LOGENTRY_ASYNC_CN:
  printk("%s - Unhandled SLI-3 Command x%x\n",
    __func__, iocb_cmnd);
  type = LPFC_UNKNOWN_IOCB;
  break;
 default:
  type = LPFC_UNKNOWN_IOCB;
  break;
 }

 return type;
}

/**
 * lpfc_sli_ring_map - Issue config_ring mbox for all rings
 * @phba: Pointer to HBA context object.
 *
 * This function is called from SLI initialization code
 * to configure every ring of the HBA's SLI interface. The
 * caller is not required to hold any lock. This function issues
 * a config_ring mailbox command for each ring.
 * This function returns zero if successful else returns a negative
 * error code.
 **/

static int
lpfc_sli_ring_map(struct lpfc_hba *phba)
{
 struct lpfc_sli *psli = &phba->sli;
 LPFC_MBOXQ_t *pmb;
 MAILBOX_t *pmbox;
 int i, rc, ret = 0;

 pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
 if (!pmb)
  return -ENOMEM;
 pmbox = &pmb->u.mb;
 phba->link_state = LPFC_INIT_MBX_CMDS;
 for (i = 0; i < psli->num_rings; i++) {
  lpfc_config_ring(phba, i, pmb);
  rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
  if (rc != MBX_SUCCESS) {
   lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
     "0446 Adapter failed to init (%d), "
     "mbxCmd x%x CFG_RING, mbxStatus x%x, "
     "ring %d\n",
     rc, pmbox->mbxCommand,
     pmbox->mbxStatus, i);
   phba->link_state = LPFC_HBA_ERROR;
   ret = -ENXIO;
   break;
  }
 }
 mempool_free(pmb, phba->mbox_mem_pool);
 return ret;
}

/**
 * lpfc_sli_ringtxcmpl_put - Adds new iocb to the txcmplq
 * @phba: Pointer to HBA context object.
 * @pring: Pointer to driver SLI ring object.
 * @piocb: Pointer to the driver iocb object.
 *
 * The driver calls this function with the hbalock held for SLI3 ports or
 * the ring lock held for SLI4 ports. The function adds the
 * new iocb to txcmplq of the given ring. This function always returns
 * 0. If this function is called for ELS ring, this function checks if
 * there is a vport associated with the ELS command. This function also
 * starts els_tmofunc timer if this is an ELS command.
 **/

static int
lpfc_sli_ringtxcmpl_put(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
   struct lpfc_iocbq *piocb)
{
 u32 ulp_command = 0;

 BUG_ON(!piocb);
 ulp_command = get_job_cmnd(phba, piocb);

 list_add_tail(&piocb->list, &pring->txcmplq);
 piocb->cmd_flag |= LPFC_IO_ON_TXCMPLQ;
 pring->txcmplq_cnt++;
 if ((unlikely(pring->ringno == LPFC_ELS_RING)) &&
    (ulp_command != CMD_ABORT_XRI_WQE) &&
    (ulp_command != CMD_ABORT_XRI_CN) &&
    (ulp_command != CMD_CLOSE_XRI_CN)) {
  BUG_ON(!piocb->vport);
  if (!test_bit(FC_UNLOADING, &piocb->vport->load_flag))
   mod_timer(&piocb->vport->els_tmofunc,
      jiffies + secs_to_jiffies(phba->fc_ratov << 1));
 }

 return 0;
}

/**
 * lpfc_sli_ringtx_get - Get first element of the txq
 * @phba: Pointer to HBA context object.
 * @pring: Pointer to driver SLI ring object.
 *
 * This function is called with hbalock held to get next
 * iocb in txq of the given ring. If there is any iocb in
 * the txq, the function returns first iocb in the list after
 * removing the iocb from the list, else it returns NULL.
 **/

struct lpfc_iocbq *
lpfc_sli_ringtx_get(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
{
 struct lpfc_iocbq *cmd_iocb;

 lockdep_assert_held(&phba->hbalock);

 list_remove_head((&pring->txq), cmd_iocb, struct lpfc_iocbq, list);
 return cmd_iocb;
}

/**
 * lpfc_cmf_sync_cmpl - Process a CMF_SYNC_WQE cmpl
 * @phba: Pointer to HBA context object.
 * @cmdiocb: Pointer to driver command iocb object.
 * @rspiocb: Pointer to driver response iocb object.
 *
 * This routine will inform the driver of any BW adjustments we need
 * to make. These changes will be picked up during the next CMF
 * timer interrupt. In addition, any BW changes will be logged
 * with LOG_CGN_MGMT.
 **/

static void
lpfc_cmf_sync_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
     struct lpfc_iocbq *rspiocb)
{
 union lpfc_wqe128 *wqe;
 uint32_t status, info;
 struct lpfc_wcqe_complete *wcqe = &rspiocb->wcqe_cmpl;
 uint64_t bw, bwdif, slop;
 uint64_t pcent, bwpcent;
 int asig, afpin, sigcnt, fpincnt;
 int wsigmax, wfpinmax, cg, tdp;
 char *s;

 /* First check for error */
 status = bf_get(lpfc_wcqe_c_status, wcqe);
 if (status) {
  lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
    "6211 CMF_SYNC_WQE Error "
    "req_tag x%x status x%x hwstatus x%x "
    "tdatap x%x parm x%x\n",
    bf_get(lpfc_wcqe_c_request_tag, wcqe),
    bf_get(lpfc_wcqe_c_status, wcqe),
    bf_get(lpfc_wcqe_c_hw_status, wcqe),
    wcqe->total_data_placed,
    wcqe->parameter);
  goto out;
 }

 /* Gather congestion information on a successful cmpl */
 info = wcqe->parameter;
 phba->cmf_active_info = info;

 /* See if firmware info count is valid or has changed */
 if (info > LPFC_MAX_CMF_INFO || phba->cmf_info_per_interval == info)
  info = 0;
 else
  phba->cmf_info_per_interval = info;

 tdp = bf_get(lpfc_wcqe_c_cmf_bw, wcqe);
 cg = bf_get(lpfc_wcqe_c_cmf_cg, wcqe);

 /* Get BW requirement from firmware */
 bw = (uint64_t)tdp * LPFC_CMF_BLK_SIZE;
 if (!bw) {
  lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
    "6212 CMF_SYNC_WQE x%x: NULL bw\n",
    bf_get(lpfc_wcqe_c_request_tag, wcqe));
  goto out;
 }

 /* Gather information needed for logging if a BW change is required */
 wqe = &cmdiocb->wqe;
 asig = bf_get(cmf_sync_asig, &wqe->cmf_sync);
 afpin = bf_get(cmf_sync_afpin, &wqe->cmf_sync);
 fpincnt = bf_get(cmf_sync_wfpincnt, &wqe->cmf_sync);
 sigcnt = bf_get(cmf_sync_wsigcnt, &wqe->cmf_sync);
 if (phba->cmf_max_bytes_per_interval != bw ||
     (asig || afpin || sigcnt || fpincnt)) {
  /* Are we increasing or decreasing BW */
  if (phba->cmf_max_bytes_per_interval <  bw) {
   bwdif = bw - phba->cmf_max_bytes_per_interval;
   s = "Increase";
  } else {
   bwdif = phba->cmf_max_bytes_per_interval - bw;
   s = "Decrease";
  }

  /* What is the change percentage */
  slop = div_u64(phba->cmf_link_byte_count, 200); /*For rounding*/
  pcent = div64_u64(bwdif * 100 + slop,
      phba->cmf_link_byte_count);
  bwpcent = div64_u64(bw * 100 + slop,
        phba->cmf_link_byte_count);
  /* Because of bytes adjustment due to shorter timer in
 * lpfc_cmf_timer() the cmf_link_byte_count can be shorter and
 * may seem like BW is above 100%.
 */

  if (bwpcent > 100)
   bwpcent = 100;

  if (phba->cmf_max_bytes_per_interval < bw &&
      bwpcent > 95)
   lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
     "6208 Congestion bandwidth "
     "limits removed\n");
  else if ((phba->cmf_max_bytes_per_interval > bw) &&
    ((bwpcent + pcent) <= 100) && ((bwpcent + pcent) > 95))
   lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
     "6209 Congestion bandwidth "
     "limits in effect\n");

  if (asig) {
   lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
     "6237 BW Threshold %lld%% (%lld): "
     "%lld%% %s: Signal Alarm: cg:%d "
     "Info:%u\n",
     bwpcent, bw, pcent, s, cg,
     phba->cmf_active_info);
  } else if (afpin) {
   lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
     "6238 BW Threshold %lld%% (%lld): "
     "%lld%% %s: FPIN Alarm: cg:%d "
     "Info:%u\n",
     bwpcent, bw, pcent, s, cg,
     phba->cmf_active_info);
  } else if (sigcnt) {
   wsigmax = bf_get(cmf_sync_wsigmax, &wqe->cmf_sync);
   lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
     "6239 BW Threshold %lld%% (%lld): "
     "%lld%% %s: Signal Warning: "
     "Cnt %d Max %d: cg:%d Info:%u\n",
     bwpcent, bw, pcent, s, sigcnt,
     wsigmax, cg, phba->cmf_active_info);
  } else if (fpincnt) {
   wfpinmax = bf_get(cmf_sync_wfpinmax, &wqe->cmf_sync);
   lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
     "6240 BW Threshold %lld%% (%lld): "
     "%lld%% %s: FPIN Warning: "
     "Cnt %d Max %d: cg:%d Info:%u\n",
     bwpcent, bw, pcent, s, fpincnt,
     wfpinmax, cg, phba->cmf_active_info);
  } else {
   lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
     "6241 BW Threshold %lld%% (%lld): "
     "CMF %lld%% %s: cg:%d Info:%u\n",
     bwpcent, bw, pcent, s, cg,
     phba->cmf_active_info);
  }
 } else if (info) {
  lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
    "6246 Info Threshold %u\n", info);
 }

 /* Save BW change to be picked up during next timer interrupt */
 phba->cmf_last_sync_bw = bw;
out:
 lpfc_sli_release_iocbq(phba, cmdiocb);
}

/**
 * lpfc_issue_cmf_sync_wqe - Issue a CMF_SYNC_WQE
 * @phba: Pointer to HBA context object.
 * @ms:   ms to set in WQE interval, 0 means use init op
 * @total: Total rcv bytes for this interval
 *
 * This routine is called every CMF timer interrupt. Its purpose is
 * to issue a CMF_SYNC_WQE to the firmware to inform it of any events
 * that may indicate we have congestion (FPINs or Signals). Upon
 * completion, the firmware will indicate any BW restrictions the
 * driver may need to take.
 **/

int
lpfc_issue_cmf_sync_wqe(struct lpfc_hba *phba, u32 ms, u64 total)
{
 union lpfc_wqe128 *wqe;
 struct lpfc_iocbq *sync_buf;
 unsigned long iflags;
 u32 ret_val, cgn_sig_freq;
 u32 atot, wtot, max;
 u8 warn_sync_period = 0;

 /* First address any alarm / warning activity */
 atot = atomic_xchg(&phba->cgn_sync_alarm_cnt, 0);
 wtot = atomic_xchg(&phba->cgn_sync_warn_cnt, 0);

 spin_lock_irqsave(&phba->hbalock, iflags);

 /* ONLY Managed mode will send the CMF_SYNC_WQE to the HBA */
 if (phba->cmf_active_mode != LPFC_CFG_MANAGED ||
     phba->link_state < LPFC_LINK_UP) {
  ret_val = 0;
  goto out_unlock;
 }

 sync_buf = __lpfc_sli_get_iocbq(phba);
 if (!sync_buf) {
  lpfc_printf_log(phba, KERN_ERR, LOG_CGN_MGMT,
    "6244 No available WQEs for CMF_SYNC_WQE\n");
  ret_val = ENOMEM;
  goto out_unlock;
 }

 wqe = &sync_buf->wqe;

 /* WQEs are reused.  Clear stale data and set key fields to zero */
 memset(wqe, 0, sizeof(*wqe));

 /* If this is the very first CMF_SYNC_WQE, issue an init operation */
 if (!ms) {
  lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
    "6441 CMF Init %d - CMF_SYNC_WQE\n",
    phba->fc_eventTag);
  bf_set(cmf_sync_op, &wqe->cmf_sync, 1); /* 1=init */
  bf_set(cmf_sync_interval, &wqe->cmf_sync, LPFC_CMF_INTERVAL);
  goto initpath;
 }

 bf_set(cmf_sync_op, &wqe->cmf_sync, 0); /* 0=recalc */
 bf_set(cmf_sync_interval, &wqe->cmf_sync, ms);

 /* Check for alarms / warnings */
 if (atot) {
  if (phba->cgn_reg_signal == EDC_CG_SIG_WARN_ALARM) {
   /* We hit an Signal alarm condition */
   bf_set(cmf_sync_asig, &wqe->cmf_sync, 1);
  } else {
   /* We hit a FPIN alarm condition */
   bf_set(cmf_sync_afpin, &wqe->cmf_sync, 1);
  }
 } else if (wtot) {
  if (phba->cgn_reg_signal == EDC_CG_SIG_WARN_ONLY ||
      phba->cgn_reg_signal == EDC_CG_SIG_WARN_ALARM) {
   cgn_sig_freq = phba->cgn_sig_freq ? phba->cgn_sig_freq :
     lpfc_fabric_cgn_frequency;
   /* We hit an Signal warning condition */
   max = LPFC_SEC_TO_MSEC / cgn_sig_freq *
    lpfc_acqe_cgn_frequency;
   bf_set(cmf_sync_wsigmax, &wqe->cmf_sync, max);
   bf_set(cmf_sync_wsigcnt, &wqe->cmf_sync, wtot);
   warn_sync_period = lpfc_acqe_cgn_frequency;
  } else {
   /* We hit a FPIN warning condition */
   bf_set(cmf_sync_wfpinmax, &wqe->cmf_sync, 1);
   bf_set(cmf_sync_wfpincnt, &wqe->cmf_sync, 1);
   if (phba->cgn_fpin_frequency != LPFC_FPIN_INIT_FREQ)
    warn_sync_period =
    LPFC_MSECS_TO_SECS(phba->cgn_fpin_frequency);
  }
 }

 /* Update total read blocks during previous timer interval */
 wqe->cmf_sync.read_bytes = (u32)(total / LPFC_CMF_BLK_SIZE);

initpath:
 bf_set(cmf_sync_ver, &wqe->cmf_sync, LPFC_CMF_SYNC_VER);
 wqe->cmf_sync.event_tag = phba->fc_eventTag;
 bf_set(cmf_sync_cmnd, &wqe->cmf_sync, CMD_CMF_SYNC_WQE);

 /* Setup reqtag to match the wqe completion. */
 bf_set(cmf_sync_reqtag, &wqe->cmf_sync, sync_buf->iotag);

 bf_set(cmf_sync_qosd, &wqe->cmf_sync, 1);
 bf_set(cmf_sync_period, &wqe->cmf_sync, warn_sync_period);

 bf_set(cmf_sync_cmd_type, &wqe->cmf_sync, CMF_SYNC_COMMAND);
 bf_set(cmf_sync_wqec, &wqe->cmf_sync, 1);
 bf_set(cmf_sync_cqid, &wqe->cmf_sync, LPFC_WQE_CQ_ID_DEFAULT);

 sync_buf->vport = phba->pport;
 sync_buf->cmd_cmpl = lpfc_cmf_sync_cmpl;
 sync_buf->cmd_dmabuf = NULL;
 sync_buf->rsp_dmabuf = NULL;
 sync_buf->bpl_dmabuf = NULL;
 sync_buf->sli4_xritag = NO_XRI;

 sync_buf->cmd_flag |= LPFC_IO_CMF;
 ret_val = lpfc_sli4_issue_wqe(phba, &phba->sli4_hba.hdwq[0], sync_buf);
 if (ret_val) {
  lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
    "6214 Cannot issue CMF_SYNC_WQE: x%x\n",
    ret_val);
  __lpfc_sli_release_iocbq(phba, sync_buf);
 }
out_unlock:
 spin_unlock_irqrestore(&phba->hbalock, iflags);
 return ret_val;
}

/**
 * lpfc_sli_next_iocb_slot - Get next iocb slot in the ring
 * @phba: Pointer to HBA context object.
 * @pring: Pointer to driver SLI ring object.
 *
 * This function is called with hbalock held and the caller must post the
 * iocb without releasing the lock. If the caller releases the lock,
 * iocb slot returned by the function is not guaranteed to be available.
 * The function returns pointer to the next available iocb slot if there
 * is available slot in the ring, else it returns NULL.
 * If the get index of the ring is ahead of the put index, the function
 * will post an error attention event to the worker thread to take the
 * HBA to offline state.
 **/

static IOCB_t *
lpfc_sli_next_iocb_slot (struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
{
 struct lpfc_pgp *pgp = &phba->port_gp[pring->ringno];
 uint32_t  max_cmd_idx = pring->sli.sli3.numCiocb;

 lockdep_assert_held(&phba->hbalock);

 if ((pring->sli.sli3.next_cmdidx == pring->sli.sli3.cmdidx) &&
    (++pring->sli.sli3.next_cmdidx >= max_cmd_idx))
  pring->sli.sli3.next_cmdidx = 0;

 if (unlikely(pring->sli.sli3.local_getidx ==
  pring->sli.sli3.next_cmdidx)) {

  pring->sli.sli3.local_getidx = le32_to_cpu(pgp->cmdGetInx);

  if (unlikely(pring->sli.sli3.local_getidx >= max_cmd_idx)) {
   lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
     "0315 Ring %d issue: portCmdGet %d "
     "is bigger than cmd ring %d\n",
     pring->ringno,
     pring->sli.sli3.local_getidx,
     max_cmd_idx);

   phba->link_state = LPFC_HBA_ERROR;
   /*
--> --------------------

--> maximum size reached

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

Messung V0.5
C=93 H=95 G=93

¤ Dauer der Verarbeitung: 0.50 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.