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

Quelle  lpfc_init.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/delay.h>
#include <linux/dma-mapping.h>
#include <linux/idr.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/pci.h>
#include <linux/spinlock.h>
#include <linux/sched/clock.h>
#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/firmware.h>
#include <linux/miscdevice.h>
#include <linux/percpu.h>
#include <linux/irq.h>
#include <linux/bitops.h>
#include <linux/crash_dump.h>
#include <linux/cpu.h>
#include <linux/cpuhotplug.h>

#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
#include <scsi/scsi_tcq.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_disc.h"
#include "lpfc.h"
#include "lpfc_scsi.h"
#include "lpfc_nvme.h"
#include "lpfc_logmsg.h"
#include "lpfc_crtn.h"
#include "lpfc_vport.h"
#include "lpfc_version.h"
#include "lpfc_ids.h"

static enum cpuhp_state lpfc_cpuhp_state;
/* Used when mapping IRQ vectors in a driver centric manner */
static uint32_t lpfc_present_cpu;
static bool lpfc_pldv_detect;

static void __lpfc_cpuhp_remove(struct lpfc_hba *phba);
static void lpfc_cpuhp_remove(struct lpfc_hba *phba);
static void lpfc_cpuhp_add(struct lpfc_hba *phba);
static void lpfc_get_hba_model_desc(struct lpfc_hba *, uint8_t *, uint8_t *);
static int lpfc_post_rcv_buf(struct lpfc_hba *);
static int lpfc_sli4_queue_verify(struct lpfc_hba *);
static int lpfc_create_bootstrap_mbox(struct lpfc_hba *);
static int lpfc_setup_endian_order(struct lpfc_hba *);
static void lpfc_destroy_bootstrap_mbox(struct lpfc_hba *);
static void lpfc_free_els_sgl_list(struct lpfc_hba *);
static void lpfc_free_nvmet_sgl_list(struct lpfc_hba *);
static void lpfc_init_sgl_list(struct lpfc_hba *);
static int lpfc_init_active_sgl_array(struct lpfc_hba *);
static void lpfc_free_active_sgl(struct lpfc_hba *);
static int lpfc_hba_down_post_s3(struct lpfc_hba *phba);
static int lpfc_hba_down_post_s4(struct lpfc_hba *phba);
static int lpfc_sli4_cq_event_pool_create(struct lpfc_hba *);
static void lpfc_sli4_cq_event_pool_destroy(struct lpfc_hba *);
static void lpfc_sli4_cq_event_release_all(struct lpfc_hba *);
static void lpfc_sli4_disable_intr(struct lpfc_hba *);
static uint32_t lpfc_sli4_enable_intr(struct lpfc_hba *, uint32_t);
static void lpfc_sli4_oas_verify(struct lpfc_hba *phba);
static uint16_t lpfc_find_cpu_handle(struct lpfc_hba *, uint16_t, int);
static void lpfc_setup_bg(struct lpfc_hba *, struct Scsi_Host *);
static int lpfc_sli4_cgn_parm_chg_evt(struct lpfc_hba *);
static void lpfc_sli4_async_cmstat_evt(struct lpfc_hba *phba);
static void lpfc_sli4_prep_dev_for_reset(struct lpfc_hba *phba);

static struct scsi_transport_template *lpfc_transport_template = NULL;
static struct scsi_transport_template *lpfc_vport_transport_template = NULL;
static DEFINE_IDR(lpfc_hba_index);
#define LPFC_NVMET_BUF_POST 254
static int lpfc_vmid_res_alloc(struct lpfc_hba *phba, struct lpfc_vport *vport);
static void lpfc_cgn_update_tstamp(struct lpfc_hba *phba, struct lpfc_cgn_ts *ts);

/**
 * lpfc_config_port_prep - Perform lpfc initialization prior to config port
 * @phba: pointer to lpfc hba data structure.
 *
 * This routine will do LPFC initialization prior to issuing the CONFIG_PORT
 * mailbox command. It retrieves the revision information from the HBA and
 * collects the Vital Product Data (VPD) about the HBA for preparing the
 * configuration of the HBA.
 *
 * Return codes:
 *   0 - success.
 *   -ERESTART - requests the SLI layer to reset the HBA and try again.
 *   Any other value - indicates an error.
 **/

int
lpfc_config_port_prep(struct lpfc_hba *phba)
{
 lpfc_vpd_t *vp = &phba->vpd;
 int i = 0, rc;
 LPFC_MBOXQ_t *pmb;
 MAILBOX_t *mb;
 char *lpfc_vpd_data = NULL;
 uint16_t offset = 0;
 static char licensed[56] =
      "key unlock for use with gnu public licensed code only\0";
 static int init_key = 1;

 pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
 if (!pmb) {
  phba->link_state = LPFC_HBA_ERROR;
  return -ENOMEM;
 }

 mb = &pmb->u.mb;
 phba->link_state = LPFC_INIT_MBX_CMDS;

 if (lpfc_is_LC_HBA(phba->pcidev->device)) {
  if (init_key) {
   uint32_t *ptext = (uint32_t *) licensed;

   for (i = 0; i < 56; i += sizeof (uint32_t), ptext++)
    *ptext = cpu_to_be32(*ptext);
   init_key = 0;
  }

  lpfc_read_nv(phba, pmb);
  memset((char*)mb->un.varRDnvp.rsvd3, 0,
   sizeof (mb->un.varRDnvp.rsvd3));
  memcpy((char*)mb->un.varRDnvp.rsvd3, licensed,
    sizeof (licensed));

  rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);

  if (rc != MBX_SUCCESS) {
   lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
     "0324 Config Port initialization "
     "error, mbxCmd x%x READ_NVPARM, "
     "mbxStatus x%x\n",
     mb->mbxCommand, mb->mbxStatus);
   mempool_free(pmb, phba->mbox_mem_pool);
   return -ERESTART;
  }
  memcpy(phba->wwnn, (char *)mb->un.varRDnvp.nodename,
         sizeof(phba->wwnn));
  memcpy(phba->wwpn, (char *)mb->un.varRDnvp.portname,
         sizeof(phba->wwpn));
 }

 /*
 * Clear all option bits except LPFC_SLI3_BG_ENABLED,
 * which was already set in lpfc_get_cfgparam()
 */

 phba->sli3_options &= (uint32_t)LPFC_SLI3_BG_ENABLED;

 /* Setup and issue mailbox READ REV command */
 lpfc_read_rev(phba, pmb);
 rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
 if (rc != MBX_SUCCESS) {
  lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
    "0439 Adapter failed to init, mbxCmd x%x "
    "READ_REV, mbxStatus x%x\n",
    mb->mbxCommand, mb->mbxStatus);
  mempool_free( pmb, phba->mbox_mem_pool);
  return -ERESTART;
 }


 /*
 * The value of rr must be 1 since the driver set the cv field to 1.
 * This setting requires the FW to set all revision fields.
 */

 if (mb->un.varRdRev.rr == 0) {
  vp->rev.rBit = 0;
  lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
    "0440 Adapter failed to init, READ_REV has "
    "missing revision information.\n");
  mempool_free(pmb, phba->mbox_mem_pool);
  return -ERESTART;
 }

 if (phba->sli_rev == 3 && !mb->un.varRdRev.v3rsp) {
  mempool_free(pmb, phba->mbox_mem_pool);
  return -EINVAL;
 }

 /* Save information as VPD data */
 vp->rev.rBit = 1;
 memcpy(&vp->sli3Feat, &mb->un.varRdRev.sli3Feat, sizeof(uint32_t));
 vp->rev.sli1FwRev = mb->un.varRdRev.sli1FwRev;
 memcpy(vp->rev.sli1FwName, (char*) mb->un.varRdRev.sli1FwName, 16);
 vp->rev.sli2FwRev = mb->un.varRdRev.sli2FwRev;
 memcpy(vp->rev.sli2FwName, (char *) mb->un.varRdRev.sli2FwName, 16);
 vp->rev.biuRev = mb->un.varRdRev.biuRev;
 vp->rev.smRev = mb->un.varRdRev.smRev;
 vp->rev.smFwRev = mb->un.varRdRev.un.smFwRev;
 vp->rev.endecRev = mb->un.varRdRev.endecRev;
 vp->rev.fcphHigh = mb->un.varRdRev.fcphHigh;
 vp->rev.fcphLow = mb->un.varRdRev.fcphLow;
 vp->rev.feaLevelHigh = mb->un.varRdRev.feaLevelHigh;
 vp->rev.feaLevelLow = mb->un.varRdRev.feaLevelLow;
 vp->rev.postKernRev = mb->un.varRdRev.postKernRev;
 vp->rev.opFwRev = mb->un.varRdRev.opFwRev;

 /* If the sli feature level is less then 9, we must
 * tear down all RPIs and VPIs on link down if NPIV
 * is enabled.
 */

 if (vp->rev.feaLevelHigh < 9)
  phba->sli3_options |= LPFC_SLI3_VPORT_TEARDOWN;

 if (lpfc_is_LC_HBA(phba->pcidev->device))
  memcpy(phba->RandomData, (char *)&mb->un.varWords[24],
      sizeof (phba->RandomData));

 /* Get adapter VPD information */
 lpfc_vpd_data = kmalloc(DMP_VPD_SIZE, GFP_KERNEL);
 if (!lpfc_vpd_data)
  goto out_free_mbox;
 do {
  lpfc_dump_mem(phba, pmb, offset, DMP_REGION_VPD);
  rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);

  if (rc != MBX_SUCCESS) {
   lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
     "0441 VPD not present on adapter, "
     "mbxCmd x%x DUMP VPD, mbxStatus x%x\n",
     mb->mbxCommand, mb->mbxStatus);
   mb->un.varDmp.word_cnt = 0;
  }
  /* dump mem may return a zero when finished or we got a
 * mailbox error, either way we are done.
 */

  if (mb->un.varDmp.word_cnt == 0)
   break;

  if (mb->un.varDmp.word_cnt > DMP_VPD_SIZE - offset)
   mb->un.varDmp.word_cnt = DMP_VPD_SIZE - offset;
  lpfc_sli_pcimem_bcopy(((uint8_t *)mb) + DMP_RSP_OFFSET,
          lpfc_vpd_data + offset,
          mb->un.varDmp.word_cnt);
  offset += mb->un.varDmp.word_cnt;
 } while (mb->un.varDmp.word_cnt && offset < DMP_VPD_SIZE);

 lpfc_parse_vpd(phba, lpfc_vpd_data, offset);

 kfree(lpfc_vpd_data);
out_free_mbox:
 mempool_free(pmb, phba->mbox_mem_pool);
 return 0;
}

/**
 * lpfc_config_async_cmpl - Completion handler for config async event mbox cmd
 * @phba: pointer to lpfc hba data structure.
 * @pmboxq: pointer to the driver internal queue element for mailbox command.
 *
 * This is the completion handler for driver's configuring asynchronous event
 * mailbox command to the device. If the mailbox command returns successfully,
 * it will set internal async event support flag to 1; otherwise, it will
 * set internal async event support flag to 0.
 **/

static void
lpfc_config_async_cmpl(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq)
{
 if (pmboxq->u.mb.mbxStatus == MBX_SUCCESS)
  phba->temp_sensor_support = 1;
 else
  phba->temp_sensor_support = 0;
 mempool_free(pmboxq, phba->mbox_mem_pool);
 return;
}

/**
 * lpfc_dump_wakeup_param_cmpl - dump memory mailbox command completion handler
 * @phba: pointer to lpfc hba data structure.
 * @pmboxq: pointer to the driver internal queue element for mailbox command.
 *
 * This is the completion handler for dump mailbox command for getting
 * wake up parameters. When this command complete, the response contain
 * Option rom version of the HBA. This function translate the version number
 * into a human readable string and store it in OptionROMVersion.
 **/

static void
lpfc_dump_wakeup_param_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
{
 struct prog_id *prg;
 uint32_t prog_id_word;
 char dist = ' ';
 /* character array used for decoding dist type. */
 char dist_char[] = "nabx";

 if (pmboxq->u.mb.mbxStatus != MBX_SUCCESS) {
  mempool_free(pmboxq, phba->mbox_mem_pool);
  return;
 }

 prg = (struct prog_id *) &prog_id_word;

 /* word 7 contain option rom version */
 prog_id_word = pmboxq->u.mb.un.varWords[7];

 /* Decode the Option rom version word to a readable string */
 dist = dist_char[prg->dist];

 if ((prg->dist == 3) && (prg->num == 0))
  snprintf(phba->OptionROMVersion, 32, "%d.%d%d",
   prg->ver, prg->rev, prg->lev);
 else
  snprintf(phba->OptionROMVersion, 32, "%d.%d%d%c%d",
   prg->ver, prg->rev, prg->lev,
   dist, prg->num);
 mempool_free(pmboxq, phba->mbox_mem_pool);
 return;
}

/**
 * lpfc_update_vport_wwn - Updates the fc_nodename, fc_portname,
 * @vport: pointer to lpfc vport data structure.
 *
 *
 * Return codes
 *   None.
 **/

void
lpfc_update_vport_wwn(struct lpfc_vport *vport)
{
 struct lpfc_hba *phba = vport->phba;

 /*
 * If the name is empty or there exists a soft name
 * then copy the service params name, otherwise use the fc name
 */

 if (vport->fc_nodename.u.wwn[0] == 0)
  memcpy(&vport->fc_nodename, &vport->fc_sparam.nodeName,
   sizeof(struct lpfc_name));
 else
  memcpy(&vport->fc_sparam.nodeName, &vport->fc_nodename,
   sizeof(struct lpfc_name));

 /*
 * If the port name has changed, then set the Param changes flag
 * to unreg the login
 */

 if (vport->fc_portname.u.wwn[0] != 0 &&
  memcmp(&vport->fc_portname, &vport->fc_sparam.portName,
         sizeof(struct lpfc_name))) {
  vport->vport_flag |= FAWWPN_PARAM_CHG;

  if (phba->sli_rev == LPFC_SLI_REV4 &&
      vport->port_type == LPFC_PHYSICAL_PORT &&
      phba->sli4_hba.fawwpn_flag & LPFC_FAWWPN_FABRIC) {
   if (!(phba->sli4_hba.fawwpn_flag & LPFC_FAWWPN_CONFIG))
    phba->sli4_hba.fawwpn_flag &=
      ~LPFC_FAWWPN_FABRIC;
   lpfc_printf_log(phba, KERN_INFO,
     LOG_SLI | LOG_DISCOVERY | LOG_ELS,
     "2701 FA-PWWN change WWPN from %llx to "
     "%llx: vflag x%x fawwpn_flag x%x\n",
     wwn_to_u64(vport->fc_portname.u.wwn),
     wwn_to_u64
        (vport->fc_sparam.portName.u.wwn),
     vport->vport_flag,
     phba->sli4_hba.fawwpn_flag);
   memcpy(&vport->fc_portname, &vport->fc_sparam.portName,
          sizeof(struct lpfc_name));
  }
 }

 if (vport->fc_portname.u.wwn[0] == 0)
  memcpy(&vport->fc_portname, &vport->fc_sparam.portName,
         sizeof(struct lpfc_name));
 else
  memcpy(&vport->fc_sparam.portName, &vport->fc_portname,
         sizeof(struct lpfc_name));
}

/**
 * lpfc_config_port_post - Perform lpfc initialization after config port
 * @phba: pointer to lpfc hba data structure.
 *
 * This routine will do LPFC initialization after the CONFIG_PORT mailbox
 * command call. It performs all internal resource and state setups on the
 * port: post IOCB buffers, enable appropriate host interrupt attentions,
 * ELS ring timers, etc.
 *
 * Return codes
 *   0 - success.
 *   Any other value - error.
 **/

int
lpfc_config_port_post(struct lpfc_hba *phba)
{
 struct lpfc_vport *vport = phba->pport;
 struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
 LPFC_MBOXQ_t *pmb;
 MAILBOX_t *mb;
 struct lpfc_dmabuf *mp;
 struct lpfc_sli *psli = &phba->sli;
 uint32_t status, timeout;
 int i, j;
 int rc;

 spin_lock_irq(&phba->hbalock);
 /*
 * If the Config port completed correctly the HBA is not
 * over heated any more.
 */

 if (phba->over_temp_state == HBA_OVER_TEMP)
  phba->over_temp_state = HBA_NORMAL_TEMP;
 spin_unlock_irq(&phba->hbalock);

 pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
 if (!pmb) {
  phba->link_state = LPFC_HBA_ERROR;
  return -ENOMEM;
 }
 mb = &pmb->u.mb;

 /* Get login parameters for NID.  */
 rc = lpfc_read_sparam(phba, pmb, 0);
 if (rc) {
  mempool_free(pmb, phba->mbox_mem_pool);
  return -ENOMEM;
 }

 pmb->vport = vport;
 if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) {
  lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
    "0448 Adapter failed init, mbxCmd x%x "
    "READ_SPARM mbxStatus x%x\n",
    mb->mbxCommand, mb->mbxStatus);
  phba->link_state = LPFC_HBA_ERROR;
  lpfc_mbox_rsrc_cleanup(phba, pmb, MBOX_THD_UNLOCKED);
  return -EIO;
 }

 mp = pmb->ctx_buf;

 /* This dmabuf was allocated by lpfc_read_sparam. The dmabuf is no
 * longer needed.  Prevent unintended ctx_buf access as the mbox is
 * reused.
 */

 memcpy(&vport->fc_sparam, mp->virt, sizeof (struct serv_parm));
 lpfc_mbuf_free(phba, mp->virt, mp->phys);
 kfree(mp);
 pmb->ctx_buf = NULL;
 lpfc_update_vport_wwn(vport);

 /* Update the fc_host data structures with new wwn. */
 fc_host_node_name(shost) = wwn_to_u64(vport->fc_nodename.u.wwn);
 fc_host_port_name(shost) = wwn_to_u64(vport->fc_portname.u.wwn);
 fc_host_max_npiv_vports(shost) = phba->max_vpi;

 /* If no serial number in VPD data, use low 6 bytes of WWNN */
 /* This should be consolidated into parse_vpd ? - mr */
 if (phba->SerialNumber[0] == 0) {
  uint8_t *outptr;

  outptr = &vport->fc_nodename.u.s.IEEE[0];
  for (i = 0; i < 12; i++) {
   status = *outptr++;
   j = ((status & 0xf0) >> 4);
   if (j <= 9)
    phba->SerialNumber[i] =
        (char)((uint8_t) 0x30 + (uint8_t) j);
   else
    phba->SerialNumber[i] =
        (char)((uint8_t) 0x61 + (uint8_t) (j - 10));
   i++;
   j = (status & 0xf);
   if (j <= 9)
    phba->SerialNumber[i] =
        (char)((uint8_t) 0x30 + (uint8_t) j);
   else
    phba->SerialNumber[i] =
        (char)((uint8_t) 0x61 + (uint8_t) (j - 10));
  }
 }

 lpfc_read_config(phba, pmb);
 pmb->vport = vport;
 if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) {
  lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
    "0453 Adapter failed to init, mbxCmd x%x "
    "READ_CONFIG, mbxStatus x%x\n",
    mb->mbxCommand, mb->mbxStatus);
  phba->link_state = LPFC_HBA_ERROR;
  mempool_free( pmb, phba->mbox_mem_pool);
  return -EIO;
 }

 /* Check if the port is disabled */
 lpfc_sli_read_link_ste(phba);

 /* Reset the DFT_HBA_Q_DEPTH to the max xri  */
 if (phba->cfg_hba_queue_depth > mb->un.varRdConfig.max_xri) {
  lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
    "3359 HBA queue depth changed from %d to %d\n",
    phba->cfg_hba_queue_depth,
    mb->un.varRdConfig.max_xri);
  phba->cfg_hba_queue_depth = mb->un.varRdConfig.max_xri;
 }

 phba->lmt = mb->un.varRdConfig.lmt;

 /* Get the default values for Model Name and Description */
 lpfc_get_hba_model_desc(phba, phba->ModelName, phba->ModelDesc);

 phba->link_state = LPFC_LINK_DOWN;

 /* Only process IOCBs on ELS ring till hba_state is READY */
 if (psli->sli3_ring[LPFC_EXTRA_RING].sli.sli3.cmdringaddr)
  psli->sli3_ring[LPFC_EXTRA_RING].flag |= LPFC_STOP_IOCB_EVENT;
 if (psli->sli3_ring[LPFC_FCP_RING].sli.sli3.cmdringaddr)
  psli->sli3_ring[LPFC_FCP_RING].flag |= LPFC_STOP_IOCB_EVENT;

 /* Post receive buffers for desired rings */
 if (phba->sli_rev != 3)
  lpfc_post_rcv_buf(phba);

 /*
 * Configure HBA MSI-X attention conditions to messages if MSI-X mode
 */

 if (phba->intr_type == MSIX) {
  rc = lpfc_config_msi(phba, pmb);
  if (rc) {
   mempool_free(pmb, phba->mbox_mem_pool);
   return -EIO;
  }
  rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
  if (rc != MBX_SUCCESS) {
   lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
     "0352 Config MSI mailbox command "
     "failed, mbxCmd x%x, mbxStatus x%x\n",
     pmb->u.mb.mbxCommand,
     pmb->u.mb.mbxStatus);
   mempool_free(pmb, phba->mbox_mem_pool);
   return -EIO;
  }
 }

 spin_lock_irq(&phba->hbalock);
 /* Initialize ERATT handling flag */
 clear_bit(HBA_ERATT_HANDLED, &phba->hba_flag);

 /* Enable appropriate host interrupts */
 if (lpfc_readl(phba->HCregaddr, &status)) {
  spin_unlock_irq(&phba->hbalock);
  return -EIO;
 }
 status |= HC_MBINT_ENA | HC_ERINT_ENA | HC_LAINT_ENA;
 if (psli->num_rings > 0)
  status |= HC_R0INT_ENA;
 if (psli->num_rings > 1)
  status |= HC_R1INT_ENA;
 if (psli->num_rings > 2)
  status |= HC_R2INT_ENA;
 if (psli->num_rings > 3)
  status |= HC_R3INT_ENA;

 if ((phba->cfg_poll & ENABLE_FCP_RING_POLLING) &&
     (phba->cfg_poll & DISABLE_FCP_RING_INT))
  status &= ~(HC_R0INT_ENA);

 writel(status, phba->HCregaddr);
 readl(phba->HCregaddr); /* flush */
 spin_unlock_irq(&phba->hbalock);

 /* Set up ring-0 (ELS) timer */
 timeout = phba->fc_ratov * 2;
 mod_timer(&vport->els_tmofunc,
    jiffies + secs_to_jiffies(timeout));
 /* Set up heart beat (HB) timer */
 mod_timer(&phba->hb_tmofunc,
    jiffies + secs_to_jiffies(LPFC_HB_MBOX_INTERVAL));
 clear_bit(HBA_HBEAT_INP, &phba->hba_flag);
 clear_bit(HBA_HBEAT_TMO, &phba->hba_flag);
 phba->last_completion_time = jiffies;
 /* Set up error attention (ERATT) polling timer */
 mod_timer(&phba->eratt_poll,
    jiffies + secs_to_jiffies(phba->eratt_poll_interval));

 if (test_bit(LINK_DISABLED, &phba->hba_flag)) {
  lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
    "2598 Adapter Link is disabled.\n");
  lpfc_down_link(phba, pmb);
  pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
  rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
  if ((rc != MBX_SUCCESS) && (rc != MBX_BUSY)) {
   lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
     "2599 Adapter failed to issue DOWN_LINK"
     " mbox command rc 0x%x\n", rc);

   mempool_free(pmb, phba->mbox_mem_pool);
   return -EIO;
  }
 } else if (phba->cfg_suppress_link_up == LPFC_INITIALIZE_LINK) {
  mempool_free(pmb, phba->mbox_mem_pool);
  rc = phba->lpfc_hba_init_link(phba, MBX_NOWAIT);
  if (rc)
   return rc;
 }
 /* MBOX buffer will be freed in mbox compl */
 pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
 if (!pmb) {
  phba->link_state = LPFC_HBA_ERROR;
  return -ENOMEM;
 }

 lpfc_config_async(phba, pmb, LPFC_ELS_RING);
 pmb->mbox_cmpl = lpfc_config_async_cmpl;
 pmb->vport = phba->pport;
 rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);

 if ((rc != MBX_BUSY) && (rc != MBX_SUCCESS)) {
  lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
    "0456 Adapter failed to issue "
    "ASYNCEVT_ENABLE mbox status x%x\n",
    rc);
  mempool_free(pmb, phba->mbox_mem_pool);
 }

 /* Get Option rom version */
 pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
 if (!pmb) {
  phba->link_state = LPFC_HBA_ERROR;
  return -ENOMEM;
 }

 lpfc_dump_wakeup_param(phba, pmb);
 pmb->mbox_cmpl = lpfc_dump_wakeup_param_cmpl;
 pmb->vport = phba->pport;
 rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);

 if ((rc != MBX_BUSY) && (rc != MBX_SUCCESS)) {
  lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
    "0435 Adapter failed "
    "to get Option ROM version status x%x\n", rc);
  mempool_free(pmb, phba->mbox_mem_pool);
 }

 return 0;
}

/**
 * lpfc_sli4_refresh_params - update driver copy of params.
 * @phba: Pointer to HBA context object.
 *
 * This is called to refresh driver copy of dynamic fields from the
 * common_get_sli4_parameters descriptor.
 **/

int
lpfc_sli4_refresh_params(struct lpfc_hba *phba)
{
 LPFC_MBOXQ_t *mboxq;
 struct lpfc_mqe *mqe;
 struct lpfc_sli4_parameters *mbx_sli4_parameters;
 int length, rc;

 mboxq = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
 if (!mboxq)
  return -ENOMEM;

 mqe = &mboxq->u.mqe;
 /* Read the port's SLI4 Config Parameters */
 length = (sizeof(struct lpfc_mbx_get_sli4_parameters) -
    sizeof(struct lpfc_sli4_cfg_mhdr));
 lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_COMMON,
    LPFC_MBOX_OPCODE_GET_SLI4_PARAMETERS,
    length, LPFC_SLI4_MBX_EMBED);

 rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
 if (unlikely(rc)) {
  mempool_free(mboxq, phba->mbox_mem_pool);
  return rc;
 }
 mbx_sli4_parameters = &mqe->un.get_sli4_parameters.sli4_parameters;
 phba->sli4_hba.pc_sli4_params.mi_cap =
  bf_get(cfg_mi_ver, mbx_sli4_parameters);

 /* Are we forcing MI off via module parameter? */
 if (phba->cfg_enable_mi)
  phba->sli4_hba.pc_sli4_params.mi_ver =
   bf_get(cfg_mi_ver, mbx_sli4_parameters);
 else
  phba->sli4_hba.pc_sli4_params.mi_ver = 0;

 phba->sli4_hba.pc_sli4_params.cmf =
   bf_get(cfg_cmf, mbx_sli4_parameters);
 phba->sli4_hba.pc_sli4_params.pls =
   bf_get(cfg_pvl, mbx_sli4_parameters);

 mempool_free(mboxq, phba->mbox_mem_pool);
 return rc;
}

/**
 * lpfc_hba_init_link - Initialize the FC link
 * @phba: pointer to lpfc hba data structure.
 * @flag: mailbox command issue mode - either MBX_POLL or MBX_NOWAIT
 *
 * This routine will issue the INIT_LINK mailbox command call.
 * It is available to other drivers through the lpfc_hba data
 * structure for use as a delayed link up mechanism with the
 * module parameter lpfc_suppress_link_up.
 *
 * Return code
 * 0 - success
 * Any other value - error
 **/

static int
lpfc_hba_init_link(struct lpfc_hba *phba, uint32_t flag)
{
 return lpfc_hba_init_link_fc_topology(phba, phba->cfg_topology, flag);
}

/**
 * lpfc_hba_init_link_fc_topology - Initialize FC link with desired topology
 * @phba: pointer to lpfc hba data structure.
 * @fc_topology: desired fc topology.
 * @flag: mailbox command issue mode - either MBX_POLL or MBX_NOWAIT
 *
 * This routine will issue the INIT_LINK mailbox command call.
 * It is available to other drivers through the lpfc_hba data
 * structure for use as a delayed link up mechanism with the
 * module parameter lpfc_suppress_link_up.
 *
 * Return code
 *              0 - success
 *              Any other value - error
 **/

int
lpfc_hba_init_link_fc_topology(struct lpfc_hba *phba, uint32_t fc_topology,
          uint32_t flag)
{
 struct lpfc_vport *vport = phba->pport;
 LPFC_MBOXQ_t *pmb;
 MAILBOX_t *mb;
 int rc;

 pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
 if (!pmb) {
  phba->link_state = LPFC_HBA_ERROR;
  return -ENOMEM;
 }
 mb = &pmb->u.mb;
 pmb->vport = vport;

 if ((phba->cfg_link_speed > LPFC_USER_LINK_SPEED_MAX) ||
     ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_1G) &&
      !(phba->lmt & LMT_1Gb)) ||
     ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_2G) &&
      !(phba->lmt & LMT_2Gb)) ||
     ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_4G) &&
      !(phba->lmt & LMT_4Gb)) ||
     ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_8G) &&
      !(phba->lmt & LMT_8Gb)) ||
     ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_10G) &&
      !(phba->lmt & LMT_10Gb)) ||
     ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_16G) &&
      !(phba->lmt & LMT_16Gb)) ||
     ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_32G) &&
      !(phba->lmt & LMT_32Gb)) ||
     ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_64G) &&
      !(phba->lmt & LMT_64Gb))) {
  /* Reset link speed to auto */
  lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
    "1302 Invalid speed for this board:%d "
    "Reset link speed to auto.\n",
    phba->cfg_link_speed);
   phba->cfg_link_speed = LPFC_USER_LINK_SPEED_AUTO;
 }
 lpfc_init_link(phba, pmb, fc_topology, phba->cfg_link_speed);
 pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
 if (phba->sli_rev < LPFC_SLI_REV4)
  lpfc_set_loopback_flag(phba);
 rc = lpfc_sli_issue_mbox(phba, pmb, flag);
 if ((rc != MBX_BUSY) && (rc != MBX_SUCCESS)) {
  lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
    "0498 Adapter failed to init, mbxCmd x%x "
    "INIT_LINK, mbxStatus x%x\n",
    mb->mbxCommand, mb->mbxStatus);
  if (phba->sli_rev <= LPFC_SLI_REV3) {
   /* Clear all interrupt enable conditions */
   writel(0, phba->HCregaddr);
   readl(phba->HCregaddr); /* flush */
   /* Clear all pending interrupts */
   writel(0xffffffff, phba->HAregaddr);
   readl(phba->HAregaddr); /* flush */
  }
  phba->link_state = LPFC_HBA_ERROR;
  if (rc != MBX_BUSY || flag == MBX_POLL)
   mempool_free(pmb, phba->mbox_mem_pool);
  return -EIO;
 }
 phba->cfg_suppress_link_up = LPFC_INITIALIZE_LINK;
 if (flag == MBX_POLL)
  mempool_free(pmb, phba->mbox_mem_pool);

 return 0;
}

/**
 * lpfc_hba_down_link - this routine downs the FC link
 * @phba: pointer to lpfc hba data structure.
 * @flag: mailbox command issue mode - either MBX_POLL or MBX_NOWAIT
 *
 * This routine will issue the DOWN_LINK mailbox command call.
 * It is available to other drivers through the lpfc_hba data
 * structure for use to stop the link.
 *
 * Return code
 * 0 - success
 * Any other value - error
 **/

static int
lpfc_hba_down_link(struct lpfc_hba *phba, uint32_t flag)
{
 LPFC_MBOXQ_t *pmb;
 int rc;

 pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
 if (!pmb) {
  phba->link_state = LPFC_HBA_ERROR;
  return -ENOMEM;
 }

 lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
   "0491 Adapter Link is disabled.\n");
 lpfc_down_link(phba, pmb);
 pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
 rc = lpfc_sli_issue_mbox(phba, pmb, flag);
 if ((rc != MBX_SUCCESS) && (rc != MBX_BUSY)) {
  lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
    "2522 Adapter failed to issue DOWN_LINK"
    " mbox command rc 0x%x\n", rc);

  mempool_free(pmb, phba->mbox_mem_pool);
  return -EIO;
 }
 if (flag == MBX_POLL)
  mempool_free(pmb, phba->mbox_mem_pool);

 return 0;
}

/**
 * lpfc_hba_down_prep - Perform lpfc uninitialization prior to HBA reset
 * @phba: pointer to lpfc HBA data structure.
 *
 * This routine will do LPFC uninitialization before the HBA is reset when
 * bringing down the SLI Layer.
 *
 * Return codes
 *   0 - success.
 *   Any other value - error.
 **/

int
lpfc_hba_down_prep(struct lpfc_hba *phba)
{
 struct lpfc_vport **vports;
 int i;

 if (phba->sli_rev <= LPFC_SLI_REV3) {
  /* Disable interrupts */
  writel(0, phba->HCregaddr);
  readl(phba->HCregaddr); /* flush */
 }

 if (test_bit(FC_UNLOADING, &phba->pport->load_flag))
  lpfc_cleanup_discovery_resources(phba->pport);
 else {
  vports = lpfc_create_vport_work_array(phba);
  if (vports != NULL)
   for (i = 0; i <= phba->max_vports &&
    vports[i] != NULL; i++)
    lpfc_cleanup_discovery_resources(vports[i]);
  lpfc_destroy_vport_work_array(phba, vports);
 }
 return 0;
}

/**
 * lpfc_sli4_free_sp_events - Cleanup sp_queue_events to free
 * rspiocb which got deferred
 *
 * @phba: pointer to lpfc HBA data structure.
 *
 * This routine will cleanup completed slow path events after HBA is reset
 * when bringing down the SLI Layer.
 *
 *
 * Return codes
 *   void.
 **/

static void
lpfc_sli4_free_sp_events(struct lpfc_hba *phba)
{
 struct lpfc_iocbq *rspiocbq;
 struct hbq_dmabuf *dmabuf;
 struct lpfc_cq_event *cq_event;

 clear_bit(HBA_SP_QUEUE_EVT, &phba->hba_flag);

 while (!list_empty(&phba->sli4_hba.sp_queue_event)) {
  /* Get the response iocb from the head of work queue */
  spin_lock_irq(&phba->hbalock);
  list_remove_head(&phba->sli4_hba.sp_queue_event,
     cq_event, struct lpfc_cq_event, list);
  spin_unlock_irq(&phba->hbalock);

  switch (bf_get(lpfc_wcqe_c_code, &cq_event->cqe.wcqe_cmpl)) {
  case CQE_CODE_COMPL_WQE:
   rspiocbq = container_of(cq_event, struct lpfc_iocbq,
       cq_event);
   lpfc_sli_release_iocbq(phba, rspiocbq);
   break;
  case CQE_CODE_RECEIVE:
  case CQE_CODE_RECEIVE_V1:
   dmabuf = container_of(cq_event, struct hbq_dmabuf,
           cq_event);
   lpfc_in_buf_free(phba, &dmabuf->dbuf);
  }
 }
}

/**
 * lpfc_hba_free_post_buf - Perform lpfc uninitialization after HBA reset
 * @phba: pointer to lpfc HBA data structure.
 *
 * This routine will cleanup posted ELS buffers after the HBA is reset
 * when bringing down the SLI Layer.
 *
 *
 * Return codes
 *   void.
 **/

static void
lpfc_hba_free_post_buf(struct lpfc_hba *phba)
{
 struct lpfc_sli *psli = &phba->sli;
 struct lpfc_sli_ring *pring;
 struct lpfc_dmabuf *mp, *next_mp;
 LIST_HEAD(buflist);
 int count;

 if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED)
  lpfc_sli_hbqbuf_free_all(phba);
 else {
  /* Cleanup preposted buffers on the ELS ring */
  pring = &psli->sli3_ring[LPFC_ELS_RING];
  spin_lock_irq(&phba->hbalock);
  list_splice_init(&pring->postbufq, &buflist);
  spin_unlock_irq(&phba->hbalock);

  count = 0;
  list_for_each_entry_safe(mp, next_mp, &buflist, list) {
   list_del(&mp->list);
   count++;
   lpfc_mbuf_free(phba, mp->virt, mp->phys);
   kfree(mp);
  }

  spin_lock_irq(&phba->hbalock);
  pring->postbufq_cnt -= count;
  spin_unlock_irq(&phba->hbalock);
 }
}

/**
 * lpfc_hba_clean_txcmplq - Perform lpfc uninitialization after HBA reset
 * @phba: pointer to lpfc HBA data structure.
 *
 * This routine will cleanup the txcmplq after the HBA is reset when bringing
 * down the SLI Layer.
 *
 * Return codes
 *   void
 **/

static void
lpfc_hba_clean_txcmplq(struct lpfc_hba *phba)
{
 struct lpfc_sli *psli = &phba->sli;
 struct lpfc_queue *qp = NULL;
 struct lpfc_sli_ring *pring;
 LIST_HEAD(completions);
 int i;
 struct lpfc_iocbq *piocb, *next_iocb;

 if (phba->sli_rev != LPFC_SLI_REV4) {
  for (i = 0; i < psli->num_rings; i++) {
   pring = &psli->sli3_ring[i];
   spin_lock_irq(&phba->hbalock);
   /* At this point in time the HBA is either reset or DOA
 * Nothing should be on txcmplq as it will
 * NEVER complete.
 */

   list_splice_init(&pring->txcmplq, &completions);
   pring->txcmplq_cnt = 0;
   spin_unlock_irq(&phba->hbalock);

   lpfc_sli_abort_iocb_ring(phba, pring);
  }
  /* Cancel all the IOCBs from the completions list */
  lpfc_sli_cancel_iocbs(phba, &completions,
          IOSTAT_LOCAL_REJECT, IOERR_SLI_ABORTED);
  return;
 }
 list_for_each_entry(qp, &phba->sli4_hba.lpfc_wq_list, wq_list) {
  pring = qp->pring;
  if (!pring)
   continue;
  spin_lock_irq(&pring->ring_lock);
  list_for_each_entry_safe(piocb, next_iocb,
      &pring->txcmplq, list)
   piocb->cmd_flag &= ~LPFC_IO_ON_TXCMPLQ;
  list_splice_init(&pring->txcmplq, &completions);
  pring->txcmplq_cnt = 0;
  spin_unlock_irq(&pring->ring_lock);
  lpfc_sli_abort_iocb_ring(phba, pring);
 }
 /* Cancel all the IOCBs from the completions list */
 lpfc_sli_cancel_iocbs(phba, &completions,
         IOSTAT_LOCAL_REJECT, IOERR_SLI_ABORTED);
}

/**
 * lpfc_hba_down_post_s3 - Perform lpfc uninitialization after HBA reset
 * @phba: pointer to lpfc HBA data structure.
 *
 * This routine will do uninitialization after the HBA is reset when bring
 * down the SLI Layer.
 *
 * Return codes
 *   0 - success.
 *   Any other value - error.
 **/

static int
lpfc_hba_down_post_s3(struct lpfc_hba *phba)
{
 lpfc_hba_free_post_buf(phba);
 lpfc_hba_clean_txcmplq(phba);
 return 0;
}

/**
 * lpfc_hba_down_post_s4 - Perform lpfc uninitialization after HBA reset
 * @phba: pointer to lpfc HBA data structure.
 *
 * This routine will do uninitialization after the HBA is reset when bring
 * down the SLI Layer.
 *
 * Return codes
 *   0 - success.
 *   Any other value - error.
 **/

static int
lpfc_hba_down_post_s4(struct lpfc_hba *phba)
{
 struct lpfc_io_buf *psb, *psb_next;
 struct lpfc_async_xchg_ctx *ctxp, *ctxp_next;
 struct lpfc_sli4_hdw_queue *qp;
 LIST_HEAD(aborts);
 LIST_HEAD(nvme_aborts);
 LIST_HEAD(nvmet_aborts);
 struct lpfc_sglq *sglq_entry = NULL;
 int cnt, idx;


 lpfc_sli_hbqbuf_free_all(phba);
 lpfc_hba_clean_txcmplq(phba);

 /* At this point in time the HBA is either reset or DOA. Either
 * way, nothing should be on lpfc_abts_els_sgl_list, it needs to be
 * on the lpfc_els_sgl_list so that it can either be freed if the
 * driver is unloading or reposted if the driver is restarting
 * the port.
 */


 /* sgl_list_lock required because worker thread uses this
 * list.
 */

 spin_lock_irq(&phba->sli4_hba.sgl_list_lock);
 list_for_each_entry(sglq_entry,
  &phba->sli4_hba.lpfc_abts_els_sgl_list, list)
  sglq_entry->state = SGL_FREED;

 list_splice_init(&phba->sli4_hba.lpfc_abts_els_sgl_list,
   &phba->sli4_hba.lpfc_els_sgl_list);


 spin_unlock_irq(&phba->sli4_hba.sgl_list_lock);

 /* abts_xxxx_buf_list_lock required because worker thread uses this
 * list.
 */

 spin_lock_irq(&phba->hbalock);
 cnt = 0;
 for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
  qp = &phba->sli4_hba.hdwq[idx];

  spin_lock(&qp->abts_io_buf_list_lock);
  list_splice_init(&qp->lpfc_abts_io_buf_list,
     &aborts);

  list_for_each_entry_safe(psb, psb_next, &aborts, list) {
   psb->pCmd = NULL;
   psb->status = IOSTAT_SUCCESS;
   cnt++;
  }
  spin_lock(&qp->io_buf_list_put_lock);
  list_splice_init(&aborts, &qp->lpfc_io_buf_list_put);
  qp->put_io_bufs += qp->abts_scsi_io_bufs;
  qp->put_io_bufs += qp->abts_nvme_io_bufs;
  qp->abts_scsi_io_bufs = 0;
  qp->abts_nvme_io_bufs = 0;
  spin_unlock(&qp->io_buf_list_put_lock);
  spin_unlock(&qp->abts_io_buf_list_lock);
 }
 spin_unlock_irq(&phba->hbalock);

 if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
  spin_lock_irq(&phba->sli4_hba.abts_nvmet_buf_list_lock);
  list_splice_init(&phba->sli4_hba.lpfc_abts_nvmet_ctx_list,
     &nvmet_aborts);
  spin_unlock_irq(&phba->sli4_hba.abts_nvmet_buf_list_lock);
  list_for_each_entry_safe(ctxp, ctxp_next, &nvmet_aborts, list) {
   ctxp->flag &= ~(LPFC_NVME_XBUSY | LPFC_NVME_ABORT_OP);
   lpfc_nvmet_ctxbuf_post(phba, ctxp->ctxbuf);
  }
 }

 lpfc_sli4_free_sp_events(phba);
 return cnt;
}

/**
 * lpfc_hba_down_post - Wrapper func for hba down post routine
 * @phba: pointer to lpfc HBA data structure.
 *
 * This routine wraps the actual SLI3 or SLI4 routine for performing
 * uninitialization after the HBA is reset when bring down the SLI Layer.
 *
 * Return codes
 *   0 - success.
 *   Any other value - error.
 **/

int
lpfc_hba_down_post(struct lpfc_hba *phba)
{
 return (*phba->lpfc_hba_down_post)(phba);
}

/**
 * lpfc_hb_timeout - The HBA-timer timeout handler
 * @t: timer context used to obtain the pointer to lpfc hba data structure.
 *
 * This is the HBA-timer timeout handler registered to the lpfc driver. When
 * this timer fires, a HBA timeout event shall be posted to the lpfc driver
 * work-port-events bitmap and the worker thread is notified. This timeout
 * event will be used by the worker thread to invoke the actual timeout
 * handler routine, lpfc_hb_timeout_handler. Any periodical operations will
 * be performed in the timeout handler and the HBA timeout event bit shall
 * be cleared by the worker thread after it has taken the event bitmap out.
 **/

static void
lpfc_hb_timeout(struct timer_list *t)
{
 struct lpfc_hba *phba;
 uint32_t tmo_posted;
 unsigned long iflag;

 phba = timer_container_of(phba, t, hb_tmofunc);

 /* Check for heart beat timeout conditions */
 spin_lock_irqsave(&phba->pport->work_port_lock, iflag);
 tmo_posted = phba->pport->work_port_events & WORKER_HB_TMO;
 if (!tmo_posted)
  phba->pport->work_port_events |= WORKER_HB_TMO;
 spin_unlock_irqrestore(&phba->pport->work_port_lock, iflag);

 /* Tell the worker thread there is work to do */
 if (!tmo_posted)
  lpfc_worker_wake_up(phba);
 return;
}

/**
 * lpfc_rrq_timeout - The RRQ-timer timeout handler
 * @t: timer context used to obtain the pointer to lpfc hba data structure.
 *
 * This is the RRQ-timer timeout handler registered to the lpfc driver. When
 * this timer fires, a RRQ timeout event shall be posted to the lpfc driver
 * work-port-events bitmap and the worker thread is notified. This timeout
 * event will be used by the worker thread to invoke the actual timeout
 * handler routine, lpfc_rrq_handler. Any periodical operations will
 * be performed in the timeout handler and the RRQ timeout event bit shall
 * be cleared by the worker thread after it has taken the event bitmap out.
 **/

static void
lpfc_rrq_timeout(struct timer_list *t)
{
 struct lpfc_hba *phba;

 phba = timer_container_of(phba, t, rrq_tmr);
 if (test_bit(FC_UNLOADING, &phba->pport->load_flag)) {
  clear_bit(HBA_RRQ_ACTIVE, &phba->hba_flag);
  return;
 }

 set_bit(HBA_RRQ_ACTIVE, &phba->hba_flag);
 lpfc_worker_wake_up(phba);
}

/**
 * lpfc_hb_mbox_cmpl - The lpfc heart-beat mailbox command callback function
 * @phba: pointer to lpfc hba data structure.
 * @pmboxq: pointer to the driver internal queue element for mailbox command.
 *
 * This is the callback function to the lpfc heart-beat mailbox command.
 * If configured, the lpfc driver issues the heart-beat mailbox command to
 * the HBA every LPFC_HB_MBOX_INTERVAL (current 5) seconds. At the time the
 * heart-beat mailbox command is issued, the driver shall set up heart-beat
 * timeout timer to LPFC_HB_MBOX_TIMEOUT (current 30) seconds and marks
 * heart-beat outstanding state. Once the mailbox command comes back and
 * no error conditions detected, the heart-beat mailbox command timer is
 * reset to LPFC_HB_MBOX_INTERVAL seconds and the heart-beat outstanding
 * state is cleared for the next heart-beat. If the timer expired with the
 * heart-beat outstanding state set, the driver will put the HBA offline.
 **/

static void
lpfc_hb_mbox_cmpl(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq)
{
 clear_bit(HBA_HBEAT_INP, &phba->hba_flag);
 clear_bit(HBA_HBEAT_TMO, &phba->hba_flag);

 /* Check and reset heart-beat timer if necessary */
 mempool_free(pmboxq, phba->mbox_mem_pool);
 if (!test_bit(FC_OFFLINE_MODE, &phba->pport->fc_flag) &&
     !(phba->link_state == LPFC_HBA_ERROR) &&
     !test_bit(FC_UNLOADING, &phba->pport->load_flag))
  mod_timer(&phba->hb_tmofunc,
     jiffies +
     secs_to_jiffies(LPFC_HB_MBOX_INTERVAL));
 return;
}

/*
 * lpfc_idle_stat_delay_work - idle_stat tracking
 *
 * This routine tracks per-eq idle_stat and determines polling decisions.
 *
 * Return codes:
 *   None
 **/

static void
lpfc_idle_stat_delay_work(struct work_struct *work)
{
 struct lpfc_hba *phba = container_of(to_delayed_work(work),
          struct lpfc_hba,
          idle_stat_delay_work);
 struct lpfc_queue *eq;
 struct lpfc_sli4_hdw_queue *hdwq;
 struct lpfc_idle_stat *idle_stat;
 u32 i, idle_percent;
 u64 wall, wall_idle, diff_wall, diff_idle, busy_time;

 if (test_bit(FC_UNLOADING, &phba->pport->load_flag))
  return;

 if (phba->link_state == LPFC_HBA_ERROR ||
     test_bit(FC_OFFLINE_MODE, &phba->pport->fc_flag) ||
     phba->cmf_active_mode != LPFC_CFG_OFF)
  goto requeue;

 for_each_present_cpu(i) {
  hdwq = &phba->sli4_hba.hdwq[phba->sli4_hba.cpu_map[i].hdwq];
  eq = hdwq->hba_eq;

  /* Skip if we've already handled this eq's primary CPU */
  if (eq->chann != i)
   continue;

  idle_stat = &phba->sli4_hba.idle_stat[i];

  /* get_cpu_idle_time returns values as running counters. Thus,
 * to know the amount for this period, the prior counter values
 * need to be subtracted from the current counter values.
 * From there, the idle time stat can be calculated as a
 * percentage of 100 - the sum of the other consumption times.
 */

  wall_idle = get_cpu_idle_time(i, &wall, 1);
  diff_idle = wall_idle - idle_stat->prev_idle;
  diff_wall = wall - idle_stat->prev_wall;

  if (diff_wall <= diff_idle)
   busy_time = 0;
  else
   busy_time = diff_wall - diff_idle;

  idle_percent = div64_u64(100 * busy_time, diff_wall);
  idle_percent = 100 - idle_percent;

  if (idle_percent < 15)
   eq->poll_mode = LPFC_QUEUE_WORK;
  else
   eq->poll_mode = LPFC_THREADED_IRQ;

  idle_stat->prev_idle = wall_idle;
  idle_stat->prev_wall = wall;
 }

requeue:
 schedule_delayed_work(&phba->idle_stat_delay_work,
         msecs_to_jiffies(LPFC_IDLE_STAT_DELAY));
}

static void
lpfc_hb_eq_delay_work(struct work_struct *work)
{
 struct lpfc_hba *phba = container_of(to_delayed_work(work),
          struct lpfc_hba, eq_delay_work);
 struct lpfc_eq_intr_info *eqi, *eqi_new;
 struct lpfc_queue *eq, *eq_next;
 unsigned char *ena_delay = NULL;
 uint32_t usdelay;
 int i;

 if (!phba->cfg_auto_imax ||
     test_bit(FC_UNLOADING, &phba->pport->load_flag))
  return;

 if (phba->link_state == LPFC_HBA_ERROR ||
     test_bit(FC_OFFLINE_MODE, &phba->pport->fc_flag))
  goto requeue;

 ena_delay = kcalloc(phba->sli4_hba.num_possible_cpu, sizeof(*ena_delay),
       GFP_KERNEL);
 if (!ena_delay)
  goto requeue;

 for (i = 0; i < phba->cfg_irq_chann; i++) {
  /* Get the EQ corresponding to the IRQ vector */
  eq = phba->sli4_hba.hba_eq_hdl[i].eq;
  if (!eq)
   continue;
  if (eq->q_mode || eq->q_flag & HBA_EQ_DELAY_CHK) {
   eq->q_flag &= ~HBA_EQ_DELAY_CHK;
   ena_delay[eq->last_cpu] = 1;
  }
 }

 for_each_present_cpu(i) {
  eqi = per_cpu_ptr(phba->sli4_hba.eq_info, i);
  if (ena_delay[i]) {
   usdelay = (eqi->icnt >> 10) * LPFC_EQ_DELAY_STEP;
   if (usdelay > LPFC_MAX_AUTO_EQ_DELAY)
    usdelay = LPFC_MAX_AUTO_EQ_DELAY;
  } else {
   usdelay = 0;
  }

  eqi->icnt = 0;

  list_for_each_entry_safe(eq, eq_next, &eqi->list, cpu_list) {
   if (unlikely(eq->last_cpu != i)) {
    eqi_new = per_cpu_ptr(phba->sli4_hba.eq_info,
            eq->last_cpu);
    list_move_tail(&eq->cpu_list, &eqi_new->list);
    continue;
   }
   if (usdelay != eq->q_mode)
    lpfc_modify_hba_eq_delay(phba, eq->hdwq, 1,
        usdelay);
  }
 }

 kfree(ena_delay);

requeue:
 queue_delayed_work(phba->wq, &phba->eq_delay_work,
      msecs_to_jiffies(LPFC_EQ_DELAY_MSECS));
}

/**
 * lpfc_hb_mxp_handler - Multi-XRI pools handler to adjust XRI distribution
 * @phba: pointer to lpfc hba data structure.
 *
 * For each heartbeat, this routine does some heuristic methods to adjust
 * XRI distribution. The goal is to fully utilize free XRIs.
 **/

static void lpfc_hb_mxp_handler(struct lpfc_hba *phba)
{
 u32 i;
 u32 hwq_count;

 hwq_count = phba->cfg_hdw_queue;
 for (i = 0; i < hwq_count; i++) {
  /* Adjust XRIs in private pool */
  lpfc_adjust_pvt_pool_count(phba, i);

  /* Adjust high watermark */
  lpfc_adjust_high_watermark(phba, i);

#ifdef LPFC_MXP_STAT
  /* Snapshot pbl, pvt and busy count */
  lpfc_snapshot_mxp(phba, i);
#endif
 }
}

/**
 * lpfc_issue_hb_mbox - Issues heart-beat mailbox command
 * @phba: pointer to lpfc hba data structure.
 *
 * If a HB mbox is not already in progrees, this routine will allocate
 * a LPFC_MBOXQ_t, populate it with a MBX_HEARTBEAT (0x31) command,
 * and issue it. The HBA_HBEAT_INP flag means the command is in progress.
 **/

int
lpfc_issue_hb_mbox(struct lpfc_hba *phba)
{
 LPFC_MBOXQ_t *pmboxq;
 int retval;

 /* Is a Heartbeat mbox already in progress */
 if (test_bit(HBA_HBEAT_INP, &phba->hba_flag))
  return 0;

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

 lpfc_heart_beat(phba, pmboxq);
 pmboxq->mbox_cmpl = lpfc_hb_mbox_cmpl;
 pmboxq->vport = phba->pport;
 retval = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT);

 if (retval != MBX_BUSY && retval != MBX_SUCCESS) {
  mempool_free(pmboxq, phba->mbox_mem_pool);
  return -ENXIO;
 }
 set_bit(HBA_HBEAT_INP, &phba->hba_flag);

 return 0;
}

/**
 * lpfc_issue_hb_tmo - Signals heartbeat timer to issue mbox command
 * @phba: pointer to lpfc hba data structure.
 *
 * The heartbeat timer (every 5 sec) will fire. If the HBA_HBEAT_TMO
 * flag is set, it will force a MBX_HEARTBEAT mbox command, regardless
 * of the value of lpfc_enable_hba_heartbeat.
 * If lpfc_enable_hba_heartbeat is set, the timeout routine will always
 * try to issue a MBX_HEARTBEAT mbox command.
 **/

void
lpfc_issue_hb_tmo(struct lpfc_hba *phba)
{
 if (phba->cfg_enable_hba_heartbeat)
  return;
 set_bit(HBA_HBEAT_TMO, &phba->hba_flag);
}

/**
 * lpfc_hb_timeout_handler - The HBA-timer timeout handler
 * @phba: pointer to lpfc hba data structure.
 *
 * This is the actual HBA-timer timeout handler to be invoked by the worker
 * thread whenever the HBA timer fired and HBA-timeout event posted. This
 * handler performs any periodic operations needed for the device. If such
 * periodic event has already been attended to either in the interrupt handler
 * or by processing slow-ring or fast-ring events within the HBA-timer
 * timeout window (LPFC_HB_MBOX_INTERVAL), this handler just simply resets
 * the timer for the next timeout period. If lpfc heart-beat mailbox command
 * is configured and there is no heart-beat mailbox command outstanding, a
 * heart-beat mailbox is issued and timer set properly. Otherwise, if there
 * has been a heart-beat mailbox command outstanding, the HBA shall be put
 * to offline.
 **/

void
lpfc_hb_timeout_handler(struct lpfc_hba *phba)
{
 struct lpfc_vport **vports;
 struct lpfc_dmabuf *buf_ptr;
 int retval = 0;
 int i, tmo;
 struct lpfc_sli *psli = &phba->sli;
 LIST_HEAD(completions);

 if (phba->cfg_xri_rebalancing) {
  /* Multi-XRI pools handler */
  lpfc_hb_mxp_handler(phba);
 }

 vports = lpfc_create_vport_work_array(phba);
 if (vports != NULL)
  for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
   lpfc_rcv_seq_check_edtov(vports[i]);
   lpfc_fdmi_change_check(vports[i]);
  }
 lpfc_destroy_vport_work_array(phba, vports);

 if (phba->link_state == LPFC_HBA_ERROR ||
     test_bit(FC_UNLOADING, &phba->pport->load_flag) ||
     test_bit(FC_OFFLINE_MODE, &phba->pport->fc_flag))
  return;

 if (phba->elsbuf_cnt &&
  (phba->elsbuf_cnt == phba->elsbuf_prev_cnt)) {
  spin_lock_irq(&phba->hbalock);
  list_splice_init(&phba->elsbuf, &completions);
  phba->elsbuf_cnt = 0;
  phba->elsbuf_prev_cnt = 0;
  spin_unlock_irq(&phba->hbalock);

  while (!list_empty(&completions)) {
   list_remove_head(&completions, buf_ptr,
    struct lpfc_dmabuf, list);
   lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys);
   kfree(buf_ptr);
  }
 }
 phba->elsbuf_prev_cnt = phba->elsbuf_cnt;

 /* If there is no heart beat outstanding, issue a heartbeat command */
 if (phba->cfg_enable_hba_heartbeat) {
  /* If IOs are completing, no need to issue a MBX_HEARTBEAT */
  spin_lock_irq(&phba->pport->work_port_lock);
  if (time_after(phba->last_completion_time +
    secs_to_jiffies(LPFC_HB_MBOX_INTERVAL),
    jiffies)) {
   spin_unlock_irq(&phba->pport->work_port_lock);
   if (test_bit(HBA_HBEAT_INP, &phba->hba_flag))
    tmo = (1000 * LPFC_HB_MBOX_TIMEOUT);
   else
    tmo = (1000 * LPFC_HB_MBOX_INTERVAL);
   goto out;
  }
  spin_unlock_irq(&phba->pport->work_port_lock);

  /* Check if a MBX_HEARTBEAT is already in progress */
  if (test_bit(HBA_HBEAT_INP, &phba->hba_flag)) {
   /*
 * If heart beat timeout called with HBA_HBEAT_INP set
 * we need to give the hb mailbox cmd a chance to
 * complete or TMO.
 */

   lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
    "0459 Adapter heartbeat still outstanding: "
    "last compl time was %d ms.\n",
    jiffies_to_msecs(jiffies
      - phba->last_completion_time));
   tmo = (1000 * LPFC_HB_MBOX_TIMEOUT);
  } else {
   if ((!(psli->sli_flag & LPFC_SLI_MBOX_ACTIVE)) &&
    (list_empty(&psli->mboxq))) {

    retval = lpfc_issue_hb_mbox(phba);
    if (retval) {
     tmo = (1000 * LPFC_HB_MBOX_INTERVAL);
     goto out;
    }
    phba->skipped_hb = 0;
   } else if (time_before_eq(phba->last_completion_time,
     phba->skipped_hb)) {
    lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
     "2857 Last completion time not "
     " updated in %d ms\n",
     jiffies_to_msecs(jiffies
       - phba->last_completion_time));
   } else
    phba->skipped_hb = jiffies;

   tmo = (1000 * LPFC_HB_MBOX_TIMEOUT);
   goto out;
  }
 } else {
  /* Check to see if we want to force a MBX_HEARTBEAT */
  if (test_bit(HBA_HBEAT_TMO, &phba->hba_flag)) {
   retval = lpfc_issue_hb_mbox(phba);
   if (retval)
    tmo = (1000 * LPFC_HB_MBOX_INTERVAL);
   else
    tmo = (1000 * LPFC_HB_MBOX_TIMEOUT);
   goto out;
  }
  tmo = (1000 * LPFC_HB_MBOX_INTERVAL);
 }
out:
 mod_timer(&phba->hb_tmofunc, jiffies + msecs_to_jiffies(tmo));
}

/**
 * lpfc_offline_eratt - Bring lpfc offline on hardware error attention
 * @phba: pointer to lpfc hba data structure.
 *
 * This routine is called to bring the HBA offline when HBA hardware error
 * other than Port Error 6 has been detected.
 **/

static void
lpfc_offline_eratt(struct lpfc_hba *phba)
{
 struct lpfc_sli   *psli = &phba->sli;

 spin_lock_irq(&phba->hbalock);
 psli->sli_flag &= ~LPFC_SLI_ACTIVE;
 spin_unlock_irq(&phba->hbalock);
 lpfc_offline_prep(phba, LPFC_MBX_NO_WAIT);

 lpfc_offline(phba);
 lpfc_reset_barrier(phba);
 spin_lock_irq(&phba->hbalock);
 lpfc_sli_brdreset(phba);
 spin_unlock_irq(&phba->hbalock);
 lpfc_hba_down_post(phba);
 lpfc_sli_brdready(phba, HS_MBRDY);
 lpfc_unblock_mgmt_io(phba);
 phba->link_state = LPFC_HBA_ERROR;
 return;
}

/**
 * lpfc_sli4_offline_eratt - Bring lpfc offline on SLI4 hardware error attention
 * @phba: pointer to lpfc hba data structure.
 *
 * This routine is called to bring a SLI4 HBA offline when HBA hardware error
 * other than Port Error 6 has been detected.
 **/

void
lpfc_sli4_offline_eratt(struct lpfc_hba *phba)
{
 spin_lock_irq(&phba->hbalock);
 if (phba->link_state == LPFC_HBA_ERROR &&
  test_bit(HBA_PCI_ERR, &phba->bit_flags)) {
  spin_unlock_irq(&phba->hbalock);
  return;
 }
 phba->link_state = LPFC_HBA_ERROR;
 spin_unlock_irq(&phba->hbalock);

 lpfc_offline_prep(phba, LPFC_MBX_NO_WAIT);
 lpfc_sli_flush_io_rings(phba);
 lpfc_offline(phba);
 lpfc_hba_down_post(phba);
 lpfc_unblock_mgmt_io(phba);
}

/**
 * lpfc_handle_deferred_eratt - The HBA hardware deferred error handler
 * @phba: pointer to lpfc hba data structure.
 *
 * This routine is invoked to handle the deferred HBA hardware error
 * conditions. This type of error is indicated by HBA by setting ER1
 * and another ER bit in the host status register. The driver will
 * wait until the ER1 bit clears before handling the error condition.
 **/

static void
lpfc_handle_deferred_eratt(struct lpfc_hba *phba)
{
 uint32_t old_host_status = phba->work_hs;
 struct lpfc_sli *psli = &phba->sli;

 /* If the pci channel is offline, ignore possible errors,
 * since we cannot communicate with the pci card anyway.
 */

 if (pci_channel_offline(phba->pcidev)) {
  clear_bit(DEFER_ERATT, &phba->hba_flag);
  return;
 }

 lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
   "0479 Deferred Adapter Hardware Error "
   "Data: x%x x%x x%x\n",
   phba->work_hs, phba->work_status[0],
   phba->work_status[1]);

 spin_lock_irq(&phba->hbalock);
 psli->sli_flag &= ~LPFC_SLI_ACTIVE;
 spin_unlock_irq(&phba->hbalock);


 /*
 * Firmware stops when it triggred erratt. That could cause the I/Os
 * dropped by the firmware. Error iocb (I/O) on txcmplq and let the
 * SCSI layer retry it after re-establishing link.
 */

 lpfc_sli_abort_fcp_rings(phba);

 /*
 * There was a firmware error. Take the hba offline and then
 * attempt to restart it.
 */

 lpfc_offline_prep(phba, LPFC_MBX_WAIT);
 lpfc_offline(phba);

 /* Wait for the ER1 bit to clear.*/
 while (phba->work_hs & HS_FFER1) {
  msleep(100);
  if (lpfc_readl(phba->HSregaddr, &phba->work_hs)) {
   phba->work_hs = UNPLUG_ERR ;
   break;
  }
  /* If driver is unloading let the worker thread continue */
  if (test_bit(FC_UNLOADING, &phba->pport->load_flag)) {
   phba->work_hs = 0;
   break;
  }
 }

 /*
 * This is to ptrotect against a race condition in which
 * first write to the host attention register clear the
 * host status register.
 */

 if (!phba->work_hs && !test_bit(FC_UNLOADING, &phba->pport->load_flag))
  phba->work_hs = old_host_status & ~HS_FFER1;

 clear_bit(DEFER_ERATT, &phba->hba_flag);
 phba->work_status[0] = readl(phba->MBslimaddr + 0xa8);
 phba->work_status[1] = readl(phba->MBslimaddr + 0xac);
}

static void
lpfc_board_errevt_to_mgmt(struct lpfc_hba *phba)
{
 struct lpfc_board_event_header board_event;
 struct Scsi_Host *shost;

 board_event.event_type = FC_REG_BOARD_EVENT;
 board_event.subcategory = LPFC_EVENT_PORTINTERR;
 shost = lpfc_shost_from_vport(phba->pport);
 fc_host_post_vendor_event(shost, fc_get_event_number(),
      sizeof(board_event),
      (char *) &board_event,
      LPFC_NL_VENDOR_ID);
}

/**
 * lpfc_handle_eratt_s3 - The SLI3 HBA hardware error handler
 * @phba: pointer to lpfc hba data structure.
 *
 * This routine is invoked to handle the following HBA hardware error
 * conditions:
 * 1 - HBA error attention interrupt
 * 2 - DMA ring index out of range
 * 3 - Mailbox command came back as unknown
 **/

static void
lpfc_handle_eratt_s3(struct lpfc_hba *phba)
{
 struct lpfc_vport *vport = phba->pport;
 struct lpfc_sli   *psli = &phba->sli;
 uint32_t event_data;
 unsigned long temperature;
 struct temp_event temp_event_data;
 struct Scsi_Host  *shost;

 /* If the pci channel is offline, ignore possible errors,
 * since we cannot communicate with the pci card anyway.
 */

 if (pci_channel_offline(phba->pcidev)) {
  clear_bit(DEFER_ERATT, &phba->hba_flag);
  return;
 }

 /* If resets are disabled then leave the HBA alone and return */
 if (!phba->cfg_enable_hba_reset)
  return;

 /* Send an internal error event to mgmt application */
 lpfc_board_errevt_to_mgmt(phba);

 if (test_bit(DEFER_ERATT, &phba->hba_flag))
  lpfc_handle_deferred_eratt(phba);

 if ((phba->work_hs & HS_FFER6) || (phba->work_hs & HS_FFER8)) {
  if (phba->work_hs & HS_FFER6)
   /* Re-establishing Link */
   lpfc_printf_log(phba, KERN_INFO, LOG_LINK_EVENT,
     "1301 Re-establishing Link "
     "Data: x%x x%x x%x\n",
     phba->work_hs, phba->work_status[0],
     phba->work_status[1]);
  if (phba->work_hs & HS_FFER8)
   /* Device Zeroization */
   lpfc_printf_log(phba, KERN_INFO, LOG_LINK_EVENT,
     "2861 Host Authentication device "
     "zeroization Data:x%x x%x x%x\n",
     phba->work_hs, phba->work_status[0],
     phba->work_status[1]);

  spin_lock_irq(&phba->hbalock);
  psli->sli_flag &= ~LPFC_SLI_ACTIVE;
  spin_unlock_irq(&phba->hbalock);

  /*
* Firmware stops when it triggled erratt with HS_FFER6.
* That could cause the I/Os dropped by the firmware.
* Error iocb (I/O) on txcmplq and let the SCSI layer
* retry it after re-establishing link.
*/

  lpfc_sli_abort_fcp_rings(phba);

  /*
 * There was a firmware error.  Take the hba offline and then
 * attempt to restart it.
 */

  lpfc_offline_prep(phba, LPFC_MBX_NO_WAIT);
  lpfc_offline(phba);
  lpfc_sli_brdrestart(phba);
  if (lpfc_online(phba) == 0) { /* Initialize the HBA */
   lpfc_unblock_mgmt_io(phba);
   return;
  }
  lpfc_unblock_mgmt_io(phba);
 } else if (phba->work_hs & HS_CRIT_TEMP) {
  temperature = readl(phba->MBslimaddr + TEMPERATURE_OFFSET);
  temp_event_data.event_type = FC_REG_TEMPERATURE_EVENT;
  temp_event_data.event_code = LPFC_CRIT_TEMP;
  temp_event_data.data = (uint32_t)temperature;

  lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
    "0406 Adapter maximum temperature exceeded "
    "(%ld), taking this port offline "
    "Data: x%x x%x x%x\n",
    temperature, phba->work_hs,
    phba->work_status[0], phba->work_status[1]);

  shost = lpfc_shost_from_vport(phba->pport);
  fc_host_post_vendor_event(shost, fc_get_event_number(),
       sizeof(temp_event_data),
       (char *) &temp_event_data,
       SCSI_NL_VID_TYPE_PCI
       | PCI_VENDOR_ID_EMULEX);

  spin_lock_irq(&phba->hbalock);
  phba->over_temp_state = HBA_OVER_TEMP;
  spin_unlock_irq(&phba->hbalock);
  lpfc_offline_eratt(phba);

 } else {
  /* The if clause above forces this code path when the status
 * failure is a value other than FFER6. Do not call the offline
 * twice. This is the adapter hardware error path.
 */

  lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
    "0457 Adapter Hardware Error "
    "Data: x%x x%x x%x\n",
    phba->work_hs,
    phba->work_status[0], phba->work_status[1]);

  event_data = FC_REG_DUMP_EVENT;
  shost = lpfc_shost_from_vport(vport);
  fc_host_post_vendor_event(shost, fc_get_event_number(),
    sizeof(event_data), (char *) &event_data,
    SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);

  lpfc_offline_eratt(phba);
 }
 return;
}

/**
 * lpfc_sli4_port_sta_fn_reset - The SLI4 function reset due to port status reg
 * @phba: pointer to lpfc hba data structure.
 * @mbx_action: flag for mailbox shutdown action.
 * @en_rn_msg: send reset/port recovery message.
 * This routine is invoked to perform an SLI4 port PCI function reset in
 * response to port status register polling attention. It waits for port
 * status register (ERR, RDY, RN) bits before proceeding with function reset.
 * During this process, interrupt vectors are freed and later requested
 * for handling possible port resource change.
 **/

static int
lpfc_sli4_port_sta_fn_reset(struct lpfc_hba *phba, int mbx_action,
       bool en_rn_msg)
{
 int rc;
 uint32_t intr_mode;
 LPFC_MBOXQ_t *mboxq;

 /* Notifying the transport that the targets are going offline. */
 lpfc_scsi_dev_block(phba);

 if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) >=
     LPFC_SLI_INTF_IF_TYPE_2) {
  /*
 * On error status condition, driver need to wait for port
 * ready before performing reset.
 */

  rc = lpfc_sli4_pdev_status_reg_wait(phba);
  if (rc)
   return rc;
 }

 /* need reset: attempt for port recovery */
 if (en_rn_msg)
  lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
    "2887 Reset Needed: Attempting Port "
    "Recovery...\n");

 /* If we are no wait, the HBA has been reset and is not
 * functional, thus we should clear
 * (LPFC_SLI_ACTIVE | LPFC_SLI_MBOX_ACTIVE) flags.
 */

 if (mbx_action == LPFC_MBX_NO_WAIT) {
  spin_lock_irq(&phba->hbalock);
  phba->sli.sli_flag &= ~LPFC_SLI_ACTIVE;
  if (phba->sli.mbox_active) {
   mboxq = phba->sli.mbox_active;
   mboxq->u.mb.mbxStatus = MBX_NOT_FINISHED;
   __lpfc_mbox_cmpl_put(phba, mboxq);
   phba->sli.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
   phba->sli.mbox_active = NULL;
  }
  spin_unlock_irq(&phba->hbalock);
 }

 lpfc_offline_prep(phba, mbx_action);
 lpfc_sli_flush_io_rings(phba);
 lpfc_nvmels_flush_cmd(phba);
 lpfc_offline(phba);
 /* release interrupt for possible resource change */
 lpfc_sli4_disable_intr(phba);
 rc = lpfc_sli_brdrestart(phba);
 if (rc) {
  lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
    "6309 Failed to restart board\n");
  return rc;
 }
 /* request and enable interrupt */
 intr_mode = lpfc_sli4_enable_intr(phba, phba->intr_mode);
 if (intr_mode == LPFC_INTR_ERROR) {
  lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
    "3175 Failed to enable interrupt\n");
  return -EIO;
 }
 phba->intr_mode = intr_mode;
 rc = lpfc_online(phba);
 if (rc == 0)
  lpfc_unblock_mgmt_io(phba);

 return rc;
}

/**
 * lpfc_handle_eratt_s4 - The SLI4 HBA hardware error handler
 * @phba: pointer to lpfc hba data structure.
 *
 * This routine is invoked to handle the SLI4 HBA hardware error attention
 * conditions.
 **/

static void
lpfc_handle_eratt_s4(struct lpfc_hba *phba)
{
 struct lpfc_vport *vport = phba->pport;
 uint32_t event_data;
 struct Scsi_Host *shost;
 uint32_t if_type;
 struct lpfc_register portstat_reg = {0};
 uint32_t reg_err1, reg_err2;
 uint32_t uerrlo_reg, uemasklo_reg;
 uint32_t smphr_port_status = 0, pci_rd_rc1, pci_rd_rc2;
 bool en_rn_msg = true;
 struct temp_event temp_event_data;
 struct lpfc_register portsmphr_reg;
 int rc, i;

 /* If the pci channel is offline, ignore possible errors, since
 * we cannot communicate with the pci card anyway.
 */

 if (pci_channel_offline(phba->pcidev)) {
  lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
    "3166 pci channel is offline\n");
  lpfc_sli_flush_io_rings(phba);
  return;
 }

 memset(&portsmphr_reg, 0, sizeof(portsmphr_reg));
 if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf);
 switch (if_type) {
 case LPFC_SLI_INTF_IF_TYPE_0:
  pci_rd_rc1 = lpfc_readl(
    phba->sli4_hba.u.if_type0.UERRLOregaddr,
    &uerrlo_reg);
  pci_rd_rc2 = lpfc_readl(
    phba->sli4_hba.u.if_type0.UEMASKLOregaddr,
    &uemasklo_reg);
  /* consider PCI bus read error as pci_channel_offline */
  if (pci_rd_rc1 == -EIO && pci_rd_rc2 == -EIO)
   return;
  if (!test_bit(HBA_RECOVERABLE_UE, &phba->hba_flag)) {
   lpfc_sli4_offline_eratt(phba);
   return;
  }
  lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
    "7623 Checking UE recoverable");

  for (i = 0; i < phba->sli4_hba.ue_to_sr / 1000; i++) {
   if (lpfc_readl(phba->sli4_hba.PSMPHRregaddr,
           &portsmphr_reg.word0))
    continue;

   smphr_port_status = bf_get(lpfc_port_smphr_port_status,
         &portsmphr_reg);
   if ((smphr_port_status & LPFC_PORT_SEM_MASK) ==
       LPFC_PORT_SEM_UE_RECOVERABLE)
    break;
   /*Sleep for 1Sec, before checking SEMAPHORE */
   msleep(1000);
  }

  lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
    "4827 smphr_port_status x%x : Waited %dSec",
    smphr_port_status, i);

  /* Recoverable UE, reset the HBA device */
  if ((smphr_port_status & LPFC_PORT_SEM_MASK) ==
      LPFC_PORT_SEM_UE_RECOVERABLE) {
   for (i = 0; i < 20; i++) {
    msleep(1000);
    if (!lpfc_readl(phba->sli4_hba.PSMPHRregaddr,
        &portsmphr_reg.word0) &&
        (LPFC_POST_STAGE_PORT_READY ==
         bf_get(lpfc_port_smphr_port_status,
         &portsmphr_reg))) {
     rc = lpfc_sli4_port_sta_fn_reset(phba,
      LPFC_MBX_NO_WAIT, en_rn_msg);
     if (rc == 0)
      return;
     lpfc_printf_log(phba, KERN_ERR,
      LOG_TRACE_EVENT,
      "4215 Failed to recover UE");
     break;
    }
   }
  }
  lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
    "7624 Firmware not ready: Failing UE recovery,"
    " waited %dSec", i);
  phba->link_state = LPFC_HBA_ERROR;
  break;

 case LPFC_SLI_INTF_IF_TYPE_2:
 case LPFC_SLI_INTF_IF_TYPE_6:
  pci_rd_rc1 = lpfc_readl(
    phba->sli4_hba.u.if_type2.STATUSregaddr,
    &portstat_reg.word0);
  /* consider PCI bus read error as pci_channel_offline */
  if (pci_rd_rc1 == -EIO) {
   lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
    "3151 PCI bus read access failure: x%x\n",
    readl(phba->sli4_hba.u.if_type2.STATUSregaddr));
   lpfc_sli4_offline_eratt(phba);
   return;
  }
  reg_err1 = readl(phba->sli4_hba.u.if_type2.ERR1regaddr);
  reg_err2 = readl(phba->sli4_hba.u.if_type2.ERR2regaddr);
  if (bf_get(lpfc_sliport_status_oti, &portstat_reg)) {
   lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
     "2889 Port Overtemperature event, "
     "taking port offline Data: x%x x%x\n",
     reg_err1, reg_err2);

   phba->sfp_alarm |= LPFC_TRANSGRESSION_HIGH_TEMPERATURE;
   temp_event_data.event_type = FC_REG_TEMPERATURE_EVENT;
   temp_event_data.event_code = LPFC_CRIT_TEMP;
   temp_event_data.data = 0xFFFFFFFF;

   shost = lpfc_shost_from_vport(phba->pport);
   fc_host_post_vendor_event(shost, fc_get_event_number(),
        sizeof(temp_event_data),
        (char *)&temp_event_data,
        SCSI_NL_VID_TYPE_PCI
        | PCI_VENDOR_ID_EMULEX);

   spin_lock_irq(&phba->hbalock);
   phba->over_temp_state = HBA_OVER_TEMP;
   spin_unlock_irq(&phba->hbalock);
   lpfc_sli4_offline_eratt(phba);
   return;
  }
  if (reg_err1 == SLIPORT_ERR1_REG_ERR_CODE_2 &&
      reg_err2 == SLIPORT_ERR2_REG_FW_RESTART) {
   lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
     "3143 Port Down: Firmware Update "
     "Detected\n");
   en_rn_msg = false;
  } else if (reg_err1 == SLIPORT_ERR1_REG_ERR_CODE_2 &&
    reg_err2 == SLIPORT_ERR2_REG_FORCED_DUMP)
   lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
     "3144 Port Down: Debug Dump\n");
  else if (reg_err1 == SLIPORT_ERR1_REG_ERR_CODE_2 &&
    reg_err2 == SLIPORT_ERR2_REG_FUNC_PROVISON)
   lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT,
     "3145 Port Down: Provisioning\n");

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

--> maximum size reached

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

Messung V0.5
C=96 H=94 G=94

¤ Dauer der Verarbeitung: 0.26 Sekunden  ¤

*© 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.