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

Quelle  qla_isr.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * QLogic Fibre Channel HBA Driver
 * Copyright (c)  2003-2014 QLogic Corporation
 */

#include "qla_def.h"
#include "qla_target.h"
#include "qla_gbl.h"

#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/cpu.h>
#include <linux/t10-pi.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_bsg_fc.h>
#include <scsi/scsi_eh.h>
#include <scsi/fc/fc_fs.h>
#include <linux/nvme-fc-driver.h>

static void qla2x00_mbx_completion(scsi_qla_host_t *, uint16_t);
static void qla2x00_status_entry(scsi_qla_host_t *, struct rsp_que *, void *);
static void qla2x00_status_cont_entry(struct rsp_que *, sts_cont_entry_t *);
static int qla2x00_error_entry(scsi_qla_host_t *, struct rsp_que *,
 sts_entry_t *);
static void qla27xx_process_purex_fpin(struct scsi_qla_host *vha,
 struct purex_item *item);
static struct purex_item *qla24xx_alloc_purex_item(scsi_qla_host_t *vha,
 uint16_t size);
static struct purex_item *qla24xx_copy_std_pkt(struct scsi_qla_host *vha,
 void *pkt);
static struct purex_item *qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha,
 void **pkt, struct rsp_que **rsp);

static void
qla27xx_process_purex_fpin(struct scsi_qla_host *vha, struct purex_item *item)
{
 void *pkt = &item->iocb;
 uint16_t pkt_size = item->size;

 ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x508d,
        "%s: Enter\n", __func__);

 ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x508e,
        "-------- ELS REQ -------\n");
 ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x508f,
         pkt, pkt_size);

 fc_host_fpin_rcv(vha->host, pkt_size, (char *)pkt, 0);
}

const char *const port_state_str[] = {
 [FCS_UNKNOWN]  = "Unknown",
 [FCS_UNCONFIGURED] = "UNCONFIGURED",
 [FCS_DEVICE_DEAD] = "DEAD",
 [FCS_DEVICE_LOST] = "LOST",
 [FCS_ONLINE]  = "ONLINE"
};

#define SFP_DISABLE_LASER_INITIATED    0x15  /* Sub code of 8070 AEN */
#define SFP_ENABLE_LASER_INITIATED     0x16  /* Sub code of 8070 AEN */

static inline void display_Laser_info(scsi_qla_host_t *vha,
          u16 mb1, u16 mb2, u16 mb3) {

 if (mb1 == SFP_DISABLE_LASER_INITIATED)
  ql_log(ql_log_warn, vha, 0xf0a2,
         "SFP temperature (%d C) reached/exceeded the threshold (%d C). Laser is disabled.\n",
         mb3, mb2);
 if (mb1 == SFP_ENABLE_LASER_INITIATED)
  ql_log(ql_log_warn, vha, 0xf0a3,
         "SFP temperature (%d C) reached normal operating level. Laser is enabled.\n",
         mb3);
}

static void
qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt)
{
 struct abts_entry_24xx *abts =
     (struct abts_entry_24xx *)&pkt->iocb;
 struct qla_hw_data *ha = vha->hw;
 struct els_entry_24xx *rsp_els;
 struct abts_entry_24xx *abts_rsp;
 dma_addr_t dma;
 uint32_t fctl;
 int rval;

 ql_dbg(ql_dbg_init, vha, 0x0286, "%s: entered.\n", __func__);

 ql_log(ql_log_warn, vha, 0x0287,
     "Processing ABTS xchg=%#x oxid=%#x rxid=%#x seqid=%#x seqcnt=%#x\n",
     abts->rx_xch_addr_to_abort, abts->ox_id, abts->rx_id,
     abts->seq_id, abts->seq_cnt);
 ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0287,
     "-------- ABTS RCV -------\n");
 ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x0287,
     (uint8_t *)abts, sizeof(*abts));

 rsp_els = dma_alloc_coherent(&ha->pdev->dev, sizeof(*rsp_els), &dma,
     GFP_KERNEL);
 if (!rsp_els) {
  ql_log(ql_log_warn, vha, 0x0287,
      "Failed allocate dma buffer ABTS/ELS RSP.\n");
  return;
 }

 /* terminate exchange */
 rsp_els->entry_type = ELS_IOCB_TYPE;
 rsp_els->entry_count = 1;
 rsp_els->nport_handle = cpu_to_le16(~0);
 rsp_els->rx_xchg_address = abts->rx_xch_addr_to_abort;
 rsp_els->control_flags = cpu_to_le16(EPD_RX_XCHG);
 ql_dbg(ql_dbg_init, vha, 0x0283,
     "Sending ELS Response to terminate exchange %#x...\n",
     abts->rx_xch_addr_to_abort);
 ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0283,
     "-------- ELS RSP -------\n");
 ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x0283,
     (uint8_t *)rsp_els, sizeof(*rsp_els));
 rval = qla2x00_issue_iocb(vha, rsp_els, dma, 0);
 if (rval) {
  ql_log(ql_log_warn, vha, 0x0288,
      "%s: iocb failed to execute -> %x\n", __func__, rval);
 } else if (rsp_els->comp_status) {
  ql_log(ql_log_warn, vha, 0x0289,
      "%s: iocb failed to complete -> completion=%#x subcode=(%#x,%#x)\n",
      __func__, rsp_els->comp_status,
      rsp_els->error_subcode_1, rsp_els->error_subcode_2);
 } else {
  ql_dbg(ql_dbg_init, vha, 0x028a,
      "%s: abort exchange done.\n", __func__);
 }

 /* send ABTS response */
 abts_rsp = (void *)rsp_els;
 memset(abts_rsp, 0, sizeof(*abts_rsp));
 abts_rsp->entry_type = ABTS_RSP_TYPE;
 abts_rsp->entry_count = 1;
 abts_rsp->nport_handle = abts->nport_handle;
 abts_rsp->vp_idx = abts->vp_idx;
 abts_rsp->sof_type = abts->sof_type & 0xf0;
 abts_rsp->rx_xch_addr = abts->rx_xch_addr;
 abts_rsp->d_id[0] = abts->s_id[0];
 abts_rsp->d_id[1] = abts->s_id[1];
 abts_rsp->d_id[2] = abts->s_id[2];
 abts_rsp->r_ctl = FC_ROUTING_BLD | FC_R_CTL_BLD_BA_ACC;
 abts_rsp->s_id[0] = abts->d_id[0];
 abts_rsp->s_id[1] = abts->d_id[1];
 abts_rsp->s_id[2] = abts->d_id[2];
 abts_rsp->cs_ctl = abts->cs_ctl;
 /* include flipping bit23 in fctl */
 fctl = ~(abts->f_ctl[2] | 0x7F) << 16 |
     FC_F_CTL_LAST_SEQ | FC_F_CTL_END_SEQ | FC_F_CTL_SEQ_INIT;
 abts_rsp->f_ctl[0] = fctl >> 0 & 0xff;
 abts_rsp->f_ctl[1] = fctl >> 8 & 0xff;
 abts_rsp->f_ctl[2] = fctl >> 16 & 0xff;
 abts_rsp->type = FC_TYPE_BLD;
 abts_rsp->rx_id = abts->rx_id;
 abts_rsp->ox_id = abts->ox_id;
 abts_rsp->payload.ba_acc.aborted_rx_id = abts->rx_id;
 abts_rsp->payload.ba_acc.aborted_ox_id = abts->ox_id;
 abts_rsp->payload.ba_acc.high_seq_cnt = cpu_to_le16(~0);
 abts_rsp->rx_xch_addr_to_abort = abts->rx_xch_addr_to_abort;
 ql_dbg(ql_dbg_init, vha, 0x028b,
     "Sending BA ACC response to ABTS %#x...\n",
     abts->rx_xch_addr_to_abort);
 ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x028b,
     "-------- ELS RSP -------\n");
 ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x028b,
     (uint8_t *)abts_rsp, sizeof(*abts_rsp));
 rval = qla2x00_issue_iocb(vha, abts_rsp, dma, 0);
 if (rval) {
  ql_log(ql_log_warn, vha, 0x028c,
      "%s: iocb failed to execute -> %x\n", __func__, rval);
 } else if (abts_rsp->comp_status) {
  ql_log(ql_log_warn, vha, 0x028d,
      "%s: iocb failed to complete -> completion=%#x subcode=(%#x,%#x)\n",
      __func__, abts_rsp->comp_status,
      abts_rsp->payload.error.subcode1,
      abts_rsp->payload.error.subcode2);
 } else {
  ql_dbg(ql_dbg_init, vha, 0x028ea,
      "%s: done.\n", __func__);
 }

 dma_free_coherent(&ha->pdev->dev, sizeof(*rsp_els), rsp_els, dma);
}

/**
 * __qla_consume_iocb - this routine is used to tell fw driver has processed
 *   or consumed the head IOCB along with the continuation IOCB's from the
 *   provided respond queue.
 * @vha: host adapter pointer
 * @pkt: pointer to current packet.  On return, this pointer shall move
 *       to the next packet.
 * @rsp: respond queue pointer.
 *
 * it is assumed pkt is the head iocb, not the continuation iocbk
 */

void __qla_consume_iocb(struct scsi_qla_host *vha,
 void **pkt, struct rsp_que **rsp)
{
 struct rsp_que *rsp_q = *rsp;
 response_t *new_pkt;
 uint16_t entry_count_remaining;
 struct purex_entry_24xx *purex = *pkt;

 entry_count_remaining = purex->entry_count;
 while (entry_count_remaining > 0) {
  new_pkt = rsp_q->ring_ptr;
  *pkt = new_pkt;

  rsp_q->ring_index++;
  if (rsp_q->ring_index == rsp_q->length) {
   rsp_q->ring_index = 0;
   rsp_q->ring_ptr = rsp_q->ring;
  } else {
   rsp_q->ring_ptr++;
  }

  new_pkt->signature = RESPONSE_PROCESSED;
  /* flush signature */
  wmb();
  --entry_count_remaining;
 }
}

/**
 * __qla_copy_purex_to_buffer - extract ELS payload from Purex IOCB
 *    and save to provided buffer
 * @vha: host adapter pointer
 * @pkt: pointer Purex IOCB
 * @rsp: respond queue
 * @buf: extracted ELS payload copy here
 * @buf_len: buffer length
 */

int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha,
 void **pkt, struct rsp_que **rsp, u8 *buf, u32 buf_len)
{
 struct purex_entry_24xx *purex = *pkt;
 struct rsp_que *rsp_q = *rsp;
 sts_cont_entry_t *new_pkt;
 uint16_t no_bytes = 0, total_bytes = 0, pending_bytes = 0;
 uint16_t buffer_copy_offset = 0;
 uint16_t entry_count_remaining;
 u16 tpad;

 entry_count_remaining = purex->entry_count;
 total_bytes = (le16_to_cpu(purex->frame_size) & 0x0FFF)
  - PURX_ELS_HEADER_SIZE;

 /*
 * end of payload may not end in 4bytes boundary.  Need to
 * round up / pad for room to swap, before saving data
 */

 tpad = roundup(total_bytes, 4);

 if (buf_len < tpad) {
  ql_dbg(ql_dbg_async, vha, 0x5084,
      "%s buffer is too small %d < %d\n",
      __func__, buf_len, tpad);
  __qla_consume_iocb(vha, pkt, rsp);
  return -EIO;
 }

 pending_bytes = total_bytes = tpad;
 no_bytes = (pending_bytes > sizeof(purex->els_frame_payload))  ?
     sizeof(purex->els_frame_payload) : pending_bytes;

 memcpy(buf, &purex->els_frame_payload[0], no_bytes);
 buffer_copy_offset += no_bytes;
 pending_bytes -= no_bytes;
 --entry_count_remaining;

 ((response_t *)purex)->signature = RESPONSE_PROCESSED;
 /* flush signature */
 wmb();

 do {
  while ((total_bytes > 0) && (entry_count_remaining > 0)) {
   new_pkt = (sts_cont_entry_t *)rsp_q->ring_ptr;
   *pkt = new_pkt;

   if (new_pkt->entry_type != STATUS_CONT_TYPE) {
    ql_log(ql_log_warn, vha, 0x507a,
        "Unexpected IOCB type, partial data 0x%x\n",
        buffer_copy_offset);
    break;
   }

   rsp_q->ring_index++;
   if (rsp_q->ring_index == rsp_q->length) {
    rsp_q->ring_index = 0;
    rsp_q->ring_ptr = rsp_q->ring;
   } else {
    rsp_q->ring_ptr++;
   }
   no_bytes = (pending_bytes > sizeof(new_pkt->data)) ?
       sizeof(new_pkt->data) : pending_bytes;
   if ((buffer_copy_offset + no_bytes) <= total_bytes) {
    memcpy((buf + buffer_copy_offset), new_pkt->data,
        no_bytes);
    buffer_copy_offset += no_bytes;
    pending_bytes -= no_bytes;
    --entry_count_remaining;
   } else {
    ql_log(ql_log_warn, vha, 0x5044,
        "Attempt to copy more that we got, optimizing..%x\n",
        buffer_copy_offset);
    memcpy((buf + buffer_copy_offset), new_pkt->data,
        total_bytes - buffer_copy_offset);
   }

   ((response_t *)new_pkt)->signature = RESPONSE_PROCESSED;
   /* flush signature */
   wmb();
  }

  if (pending_bytes != 0 || entry_count_remaining != 0) {
   ql_log(ql_log_fatal, vha, 0x508b,
       "Dropping partial Data, underrun bytes = 0x%x, entry cnts 0x%x\n",
       total_bytes, entry_count_remaining);
   return -EIO;
  }
 } while (entry_count_remaining > 0);

 be32_to_cpu_array((u32 *)buf, (__be32 *)buf, total_bytes >> 2);

 return 0;
}

/**
 * qla2100_intr_handler() - Process interrupts for the ISP2100 and ISP2200.
 * @irq: interrupt number
 * @dev_id: SCSI driver HA context
 *
 * Called by system whenever the host adapter generates an interrupt.
 *
 * Returns handled flag.
 */

irqreturn_t
qla2100_intr_handler(int irq, void *dev_id)
{
 scsi_qla_host_t *vha;
 struct qla_hw_data *ha;
 struct device_reg_2xxx __iomem *reg;
 int  status;
 unsigned long iter;
 uint16_t hccr;
 uint16_t mb[8];
 struct rsp_que *rsp;
 unsigned long flags;

 rsp = (struct rsp_que *) dev_id;
 if (!rsp) {
  ql_log(ql_log_info, NULL, 0x505d,
      "%s: NULL response queue pointer.\n", __func__);
  return (IRQ_NONE);
 }

 ha = rsp->hw;
 reg = &ha->iobase->isp;
 status = 0;

 spin_lock_irqsave(&ha->hardware_lock, flags);
 vha = pci_get_drvdata(ha->pdev);
 for (iter = 50; iter--; ) {
  hccr = rd_reg_word(®->hccr);
  if (qla2x00_check_reg16_for_disconnect(vha, hccr))
   break;
  if (hccr & HCCR_RISC_PAUSE) {
   if (pci_channel_offline(ha->pdev))
    break;

   /*
 * Issue a "HARD" reset in order for the RISC interrupt
 * bit to be cleared.  Schedule a big hammer to get
 * out of the RISC PAUSED state.
 */

   wrt_reg_word(®->hccr, HCCR_RESET_RISC);
   rd_reg_word(®->hccr);

   ha->isp_ops->fw_dump(vha);
   set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
   break;
  } else if ((rd_reg_word(®->istatus) & ISR_RISC_INT) == 0)
   break;

  if (rd_reg_word(®->semaphore) & BIT_0) {
   wrt_reg_word(®->hccr, HCCR_CLR_RISC_INT);
   rd_reg_word(®->hccr);

   /* Get mailbox data. */
   mb[0] = RD_MAILBOX_REG(ha, reg, 0);
   if (mb[0] > 0x3fff && mb[0] < 0x8000) {
    qla2x00_mbx_completion(vha, mb[0]);
    status |= MBX_INTERRUPT;
   } else if (mb[0] > 0x7fff && mb[0] < 0xc000) {
    mb[1] = RD_MAILBOX_REG(ha, reg, 1);
    mb[2] = RD_MAILBOX_REG(ha, reg, 2);
    mb[3] = RD_MAILBOX_REG(ha, reg, 3);
    qla2x00_async_event(vha, rsp, mb);
   } else {
    /*EMPTY*/
    ql_dbg(ql_dbg_async, vha, 0x5025,
        "Unrecognized interrupt type (%d).\n",
        mb[0]);
   }
   /* Release mailbox registers. */
   wrt_reg_word(®->semaphore, 0);
   rd_reg_word(®->semaphore);
  } else {
   qla2x00_process_response_queue(rsp);

   wrt_reg_word(®->hccr, HCCR_CLR_RISC_INT);
   rd_reg_word(®->hccr);
  }
 }
 qla2x00_handle_mbx_completion(ha, status);
 spin_unlock_irqrestore(&ha->hardware_lock, flags);

 return (IRQ_HANDLED);
}

bool
qla2x00_check_reg32_for_disconnect(scsi_qla_host_t *vha, uint32_t reg)
{
 /* Check for PCI disconnection */
 if (reg == 0xffffffff && !pci_channel_offline(vha->hw->pdev)) {
  if (!test_and_set_bit(PFLG_DISCONNECTED, &vha->pci_flags) &&
      !test_bit(PFLG_DRIVER_REMOVING, &vha->pci_flags) &&
      !test_bit(PFLG_DRIVER_PROBING, &vha->pci_flags)) {
   qla_schedule_eeh_work(vha);
  }
  return true;
 } else
  return false;
}

bool
qla2x00_check_reg16_for_disconnect(scsi_qla_host_t *vha, uint16_t reg)
{
 return qla2x00_check_reg32_for_disconnect(vha, 0xffff0000 | reg);
}

/**
 * qla2300_intr_handler() - Process interrupts for the ISP23xx and ISP63xx.
 * @irq: interrupt number
 * @dev_id: SCSI driver HA context
 *
 * Called by system whenever the host adapter generates an interrupt.
 *
 * Returns handled flag.
 */

irqreturn_t
qla2300_intr_handler(int irq, void *dev_id)
{
 scsi_qla_host_t *vha;
 struct device_reg_2xxx __iomem *reg;
 int  status;
 unsigned long iter;
 uint32_t stat;
 uint16_t hccr;
 uint16_t mb[8];
 struct rsp_que *rsp;
 struct qla_hw_data *ha;
 unsigned long flags;

 rsp = (struct rsp_que *) dev_id;
 if (!rsp) {
  ql_log(ql_log_info, NULL, 0x5058,
      "%s: NULL response queue pointer.\n", __func__);
  return (IRQ_NONE);
 }

 ha = rsp->hw;
 reg = &ha->iobase->isp;
 status = 0;

 spin_lock_irqsave(&ha->hardware_lock, flags);
 vha = pci_get_drvdata(ha->pdev);
 for (iter = 50; iter--; ) {
  stat = rd_reg_dword(®->u.isp2300.host_status);
  if (qla2x00_check_reg32_for_disconnect(vha, stat))
   break;
  if (stat & HSR_RISC_PAUSED) {
   if (unlikely(pci_channel_offline(ha->pdev)))
    break;

   hccr = rd_reg_word(®->hccr);

   if (hccr & (BIT_15 | BIT_13 | BIT_11 | BIT_8))
    ql_log(ql_log_warn, vha, 0x5026,
        "Parity error -- HCCR=%x, Dumping "
        "firmware.\n", hccr);
   else
    ql_log(ql_log_warn, vha, 0x5027,
        "RISC paused -- HCCR=%x, Dumping "
        "firmware.\n", hccr);

   /*
 * Issue a "HARD" reset in order for the RISC
 * interrupt bit to be cleared.  Schedule a big
 * hammer to get out of the RISC PAUSED state.
 */

   wrt_reg_word(®->hccr, HCCR_RESET_RISC);
   rd_reg_word(®->hccr);

   ha->isp_ops->fw_dump(vha);
   set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
   break;
  } else if ((stat & HSR_RISC_INT) == 0)
   break;

  switch (stat & 0xff) {
  case 0x1:
  case 0x2:
  case 0x10:
  case 0x11:
   qla2x00_mbx_completion(vha, MSW(stat));
   status |= MBX_INTERRUPT;

   /* Release mailbox registers. */
   wrt_reg_word(®->semaphore, 0);
   break;
  case 0x12:
   mb[0] = MSW(stat);
   mb[1] = RD_MAILBOX_REG(ha, reg, 1);
   mb[2] = RD_MAILBOX_REG(ha, reg, 2);
   mb[3] = RD_MAILBOX_REG(ha, reg, 3);
   qla2x00_async_event(vha, rsp, mb);
   break;
  case 0x13:
   qla2x00_process_response_queue(rsp);
   break;
  case 0x15:
   mb[0] = MBA_CMPLT_1_16BIT;
   mb[1] = MSW(stat);
   qla2x00_async_event(vha, rsp, mb);
   break;
  case 0x16:
   mb[0] = MBA_SCSI_COMPLETION;
   mb[1] = MSW(stat);
   mb[2] = RD_MAILBOX_REG(ha, reg, 2);
   qla2x00_async_event(vha, rsp, mb);
   break;
  default:
   ql_dbg(ql_dbg_async, vha, 0x5028,
       "Unrecognized interrupt type (%d).\n", stat & 0xff);
   break;
  }
  wrt_reg_word(®->hccr, HCCR_CLR_RISC_INT);
  rd_reg_word_relaxed(®->hccr);
 }
 qla2x00_handle_mbx_completion(ha, status);
 spin_unlock_irqrestore(&ha->hardware_lock, flags);

 return (IRQ_HANDLED);
}

/**
 * qla2x00_mbx_completion() - Process mailbox command completions.
 * @vha: SCSI driver HA context
 * @mb0: Mailbox0 register
 */

static void
qla2x00_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0)
{
 uint16_t cnt;
 uint32_t mboxes;
 __le16 __iomem *wptr;
 struct qla_hw_data *ha = vha->hw;
 struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;

 /* Read all mbox registers? */
 WARN_ON_ONCE(ha->mbx_count > 32);
 mboxes = (1ULL << ha->mbx_count) - 1;
 if (!ha->mcp)
  ql_dbg(ql_dbg_async, vha, 0x5001, "MBX pointer ERROR.\n");
 else
  mboxes = ha->mcp->in_mb;

 /* Load return mailbox registers. */
 ha->flags.mbox_int = 1;
 ha->mailbox_out[0] = mb0;
 mboxes >>= 1;
 wptr = MAILBOX_REG(ha, reg, 1);

 for (cnt = 1; cnt < ha->mbx_count; cnt++) {
  if (IS_QLA2200(ha) && cnt == 8)
   wptr = MAILBOX_REG(ha, reg, 8);
  if ((cnt == 4 || cnt == 5) && (mboxes & BIT_0))
   ha->mailbox_out[cnt] = qla2x00_debounce_register(wptr);
  else if (mboxes & BIT_0)
   ha->mailbox_out[cnt] = rd_reg_word(wptr);

  wptr++;
  mboxes >>= 1;
 }
}

static void
qla81xx_idc_event(scsi_qla_host_t *vha, uint16_t aen, uint16_t descr)
{
 static char *event[] =
  { "Complete""Request Notification""Time Extension" };
 int rval;
 struct device_reg_24xx __iomem *reg24 = &vha->hw->iobase->isp24;
 struct device_reg_82xx __iomem *reg82 = &vha->hw->iobase->isp82;
 __le16 __iomem *wptr;
 uint16_t cnt, timeout, mb[QLA_IDC_ACK_REGS];

 /* Seed data -- mailbox1 -> mailbox7. */
 if (IS_QLA81XX(vha->hw) || IS_QLA83XX(vha->hw))
  wptr = ®24->mailbox1;
 else if (IS_QLA8044(vha->hw))
  wptr = ®82->mailbox_out[1];
 else
  return;

 for (cnt = 0; cnt < QLA_IDC_ACK_REGS; cnt++, wptr++)
  mb[cnt] = rd_reg_word(wptr);

 ql_dbg(ql_dbg_async, vha, 0x5021,
     "Inter-Driver Communication %s -- "
     "%04x %04x %04x %04x %04x %04x %04x.\n",
     event[aen & 0xff], mb[0], mb[1], mb[2], mb[3],
     mb[4], mb[5], mb[6]);
 switch (aen) {
 /* Handle IDC Error completion case. */
 case MBA_IDC_COMPLETE:
  if (mb[1] >> 15) {
   vha->hw->flags.idc_compl_status = 1;
   if (vha->hw->notify_dcbx_comp && !vha->vp_idx)
    complete(&vha->hw->dcbx_comp);
  }
  break;

 case MBA_IDC_NOTIFY:
  /* Acknowledgement needed? [Notify && non-zero timeout]. */
  timeout = (descr >> 8) & 0xf;
  ql_dbg(ql_dbg_async, vha, 0x5022,
      "%lu Inter-Driver Communication %s -- ACK timeout=%d.\n",
      vha->host_no, event[aen & 0xff], timeout);

  if (!timeout)
   return;
  rval = qla2x00_post_idc_ack_work(vha, mb);
  if (rval != QLA_SUCCESS)
   ql_log(ql_log_warn, vha, 0x5023,
       "IDC failed to post ACK.\n");
  break;
 case MBA_IDC_TIME_EXT:
  vha->hw->idc_extend_tmo = descr;
  ql_dbg(ql_dbg_async, vha, 0x5087,
      "%lu Inter-Driver Communication %s -- "
      "Extend timeout by=%d.\n",
      vha->host_no, event[aen & 0xff], vha->hw->idc_extend_tmo);
  break;
 }
}

#define LS_UNKNOWN 2
const char *
qla2x00_get_link_speed_str(struct qla_hw_data *ha, uint16_t speed)
{
 static const char *const link_speeds[] = {
  "1""2""?""4""8""16""32""64""10"
 };
#define QLA_LAST_SPEED (ARRAY_SIZE(link_speeds) - 1)

 if (IS_QLA2100(ha) || IS_QLA2200(ha))
  return link_speeds[0];
 else if (speed == 0x13)
  return link_speeds[QLA_LAST_SPEED];
 else if (speed < QLA_LAST_SPEED)
  return link_speeds[speed];
 else
  return link_speeds[LS_UNKNOWN];
}

static void
qla83xx_handle_8200_aen(scsi_qla_host_t *vha, uint16_t *mb)
{
 struct qla_hw_data *ha = vha->hw;

 /*
 * 8200 AEN Interpretation:
 * mb[0] = AEN code
 * mb[1] = AEN Reason code
 * mb[2] = LSW of Peg-Halt Status-1 Register
 * mb[6] = MSW of Peg-Halt Status-1 Register
 * mb[3] = LSW of Peg-Halt Status-2 register
 * mb[7] = MSW of Peg-Halt Status-2 register
 * mb[4] = IDC Device-State Register value
 * mb[5] = IDC Driver-Presence Register value
 */

 ql_dbg(ql_dbg_async, vha, 0x506b, "AEN Code: mb[0] = 0x%x AEN reason: "
     "mb[1] = 0x%x PH-status1: mb[2] = 0x%x PH-status1: mb[6] = 0x%x.\n",
     mb[0], mb[1], mb[2], mb[6]);
 ql_dbg(ql_dbg_async, vha, 0x506c, "PH-status2: mb[3] = 0x%x "
     "PH-status2: mb[7] = 0x%x Device-State: mb[4] = 0x%x "
     "Drv-Presence: mb[5] = 0x%x.\n", mb[3], mb[7], mb[4], mb[5]);

 if (mb[1] & (IDC_PEG_HALT_STATUS_CHANGE | IDC_NIC_FW_REPORTED_FAILURE |
    IDC_HEARTBEAT_FAILURE)) {
  ha->flags.nic_core_hung = 1;
  ql_log(ql_log_warn, vha, 0x5060,
      "83XX: F/W Error Reported: Check if reset required.\n");

  if (mb[1] & IDC_PEG_HALT_STATUS_CHANGE) {
   uint32_t protocol_engine_id, fw_err_code, err_level;

   /*
 * IDC_PEG_HALT_STATUS_CHANGE interpretation:
 *  - PEG-Halt Status-1 Register:
 * (LSW = mb[2], MSW = mb[6])
 * Bits 0-7   = protocol-engine ID
 * Bits 8-28  = f/w error code
 * Bits 29-31 = Error-level
 *     Error-level 0x1 = Non-Fatal error
 *     Error-level 0x2 = Recoverable Fatal error
 *     Error-level 0x4 = UnRecoverable Fatal error
 *  - PEG-Halt Status-2 Register:
 * (LSW = mb[3], MSW = mb[7])
 */

   protocol_engine_id = (mb[2] & 0xff);
   fw_err_code = (((mb[2] & 0xff00) >> 8) |
       ((mb[6] & 0x1fff) << 8));
   err_level = ((mb[6] & 0xe000) >> 13);
   ql_log(ql_log_warn, vha, 0x5061, "PegHalt Status-1 "
       "Register: protocol_engine_id=0x%x "
       "fw_err_code=0x%x err_level=0x%x.\n",
       protocol_engine_id, fw_err_code, err_level);
   ql_log(ql_log_warn, vha, 0x5062, "PegHalt Status-2 "
       "Register: 0x%x%x.\n", mb[7], mb[3]);
   if (err_level == ERR_LEVEL_NON_FATAL) {
    ql_log(ql_log_warn, vha, 0x5063,
        "Not a fatal error, f/w has recovered itself.\n");
   } else if (err_level == ERR_LEVEL_RECOVERABLE_FATAL) {
    ql_log(ql_log_fatal, vha, 0x5064,
        "Recoverable Fatal error: Chip reset "
        "required.\n");
    qla83xx_schedule_work(vha,
        QLA83XX_NIC_CORE_RESET);
   } else if (err_level == ERR_LEVEL_UNRECOVERABLE_FATAL) {
    ql_log(ql_log_fatal, vha, 0x5065,
        "Unrecoverable Fatal error: Set FAILED "
        "state, reboot required.\n");
    qla83xx_schedule_work(vha,
        QLA83XX_NIC_CORE_UNRECOVERABLE);
   }
  }

  if (mb[1] & IDC_NIC_FW_REPORTED_FAILURE) {
   uint16_t peg_fw_state, nw_interface_link_up;
   uint16_t nw_interface_signal_detect, sfp_status;
   uint16_t htbt_counter, htbt_monitor_enable;
   uint16_t sfp_additional_info, sfp_multirate;
   uint16_t sfp_tx_fault, link_speed, dcbx_status;

   /*
 * IDC_NIC_FW_REPORTED_FAILURE interpretation:
 *  - PEG-to-FC Status Register:
 * (LSW = mb[2], MSW = mb[6])
 * Bits 0-7   = Peg-Firmware state
 * Bit 8      = N/W Interface Link-up
 * Bit 9      = N/W Interface signal detected
 * Bits 10-11 = SFP Status
 *   SFP Status 0x0 = SFP+ transceiver not expected
 *   SFP Status 0x1 = SFP+ transceiver not present
 *   SFP Status 0x2 = SFP+ transceiver invalid
 *   SFP Status 0x3 = SFP+ transceiver present and
 *   valid
 * Bits 12-14 = Heartbeat Counter
 * Bit 15     = Heartbeat Monitor Enable
 * Bits 16-17 = SFP Additional Info
 *   SFP info 0x0 = Unregocnized transceiver for
 *   Ethernet
 *   SFP info 0x1 = SFP+ brand validation failed
 *   SFP info 0x2 = SFP+ speed validation failed
 *   SFP info 0x3 = SFP+ access error
 * Bit 18     = SFP Multirate
 * Bit 19     = SFP Tx Fault
 * Bits 20-22 = Link Speed
 * Bits 23-27 = Reserved
 * Bits 28-30 = DCBX Status
 *   DCBX Status 0x0 = DCBX Disabled
 *   DCBX Status 0x1 = DCBX Enabled
 *   DCBX Status 0x2 = DCBX Exchange error
 * Bit 31     = Reserved
 */

   peg_fw_state = (mb[2] & 0x00ff);
   nw_interface_link_up = ((mb[2] & 0x0100) >> 8);
   nw_interface_signal_detect = ((mb[2] & 0x0200) >> 9);
   sfp_status = ((mb[2] & 0x0c00) >> 10);
   htbt_counter = ((mb[2] & 0x7000) >> 12);
   htbt_monitor_enable = ((mb[2] & 0x8000) >> 15);
   sfp_additional_info = (mb[6] & 0x0003);
   sfp_multirate = ((mb[6] & 0x0004) >> 2);
   sfp_tx_fault = ((mb[6] & 0x0008) >> 3);
   link_speed = ((mb[6] & 0x0070) >> 4);
   dcbx_status = ((mb[6] & 0x7000) >> 12);

   ql_log(ql_log_warn, vha, 0x5066,
       "Peg-to-Fc Status Register:\n"
       "peg_fw_state=0x%x, nw_interface_link_up=0x%x, "
       "nw_interface_signal_detect=0x%x"
       "\nsfp_statis=0x%x.\n ", peg_fw_state,
       nw_interface_link_up, nw_interface_signal_detect,
       sfp_status);
   ql_log(ql_log_warn, vha, 0x5067,
       "htbt_counter=0x%x, htbt_monitor_enable=0x%x, "
       "sfp_additional_info=0x%x, sfp_multirate=0x%x.\n ",
       htbt_counter, htbt_monitor_enable,
       sfp_additional_info, sfp_multirate);
   ql_log(ql_log_warn, vha, 0x5068,
       "sfp_tx_fault=0x%x, link_state=0x%x, "
       "dcbx_status=0x%x.\n", sfp_tx_fault, link_speed,
       dcbx_status);

   qla83xx_schedule_work(vha, QLA83XX_NIC_CORE_RESET);
  }

  if (mb[1] & IDC_HEARTBEAT_FAILURE) {
   ql_log(ql_log_warn, vha, 0x5069,
       "Heartbeat Failure encountered, chip reset "
       "required.\n");

   qla83xx_schedule_work(vha, QLA83XX_NIC_CORE_RESET);
  }
 }

 if (mb[1] & IDC_DEVICE_STATE_CHANGE) {
  ql_log(ql_log_info, vha, 0x506a,
      "IDC Device-State changed = 0x%x.\n", mb[4]);
  if (ha->flags.nic_core_reset_owner)
   return;
  qla83xx_schedule_work(vha, MBA_IDC_AEN);
 }
}

/**
 * qla27xx_copy_multiple_pkt() - Copy over purex/purls packets that can
 * span over multiple IOCBs.
 * @vha: SCSI driver HA context
 * @pkt: ELS packet
 * @rsp: Response queue
 * @is_purls: True, for Unsolicited Received FC-NVMe LS rsp IOCB
 *            false, for Unsolicited Received ELS IOCB
 * @byte_order: True, to change the byte ordering of iocb payload
 */

struct purex_item *
qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
     struct rsp_que **rsp, bool is_purls,
     bool byte_order)
{
 struct purex_entry_24xx *purex = NULL;
 struct pt_ls4_rx_unsol *purls = NULL;
 struct rsp_que *rsp_q = *rsp;
 sts_cont_entry_t *new_pkt;
 uint16_t no_bytes = 0, total_bytes = 0, pending_bytes = 0;
 uint16_t buffer_copy_offset = 0, payload_size = 0;
 uint16_t entry_count, entry_count_remaining;
 struct purex_item *item;
 void *iocb_pkt = NULL;

 if (is_purls) {
  purls = *pkt;
  total_bytes = (le16_to_cpu(purls->frame_size) & 0x0FFF) -
         PURX_ELS_HEADER_SIZE;
  entry_count = entry_count_remaining = purls->entry_count;
  payload_size = sizeof(purls->payload);
 } else {
  purex = *pkt;
  total_bytes = (le16_to_cpu(purex->frame_size) & 0x0FFF) -
         PURX_ELS_HEADER_SIZE;
  entry_count = entry_count_remaining = purex->entry_count;
  payload_size = sizeof(purex->els_frame_payload);
 }

 pending_bytes = total_bytes;
 no_bytes = (pending_bytes > payload_size) ? payload_size :
     pending_bytes;
 ql_dbg(ql_dbg_async, vha, 0x509a,
        "%s LS, frame_size 0x%x, entry count %d\n",
        (is_purls ? "PURLS" : "FPIN"), total_bytes, entry_count);

 item = qla24xx_alloc_purex_item(vha, total_bytes);
 if (!item)
  return item;

 iocb_pkt = &item->iocb;

 if (is_purls)
  memcpy(iocb_pkt, &purls->payload[0], no_bytes);
 else
  memcpy(iocb_pkt, &purex->els_frame_payload[0], no_bytes);
 buffer_copy_offset += no_bytes;
 pending_bytes -= no_bytes;
 --entry_count_remaining;

 if (is_purls)
  ((response_t *)purls)->signature = RESPONSE_PROCESSED;
 else
  ((response_t *)purex)->signature = RESPONSE_PROCESSED;
 wmb();

 do {
  while ((total_bytes > 0) && (entry_count_remaining > 0)) {
   if (rsp_q->ring_ptr->signature == RESPONSE_PROCESSED) {
    ql_dbg(ql_dbg_async, vha, 0x5084,
           "Ran out of IOCBs, partial data 0x%x\n",
           buffer_copy_offset);
    cpu_relax();
    continue;
   }

   new_pkt = (sts_cont_entry_t *)rsp_q->ring_ptr;
   *pkt = new_pkt;

   if (new_pkt->entry_type != STATUS_CONT_TYPE) {
    ql_log(ql_log_warn, vha, 0x507a,
           "Unexpected IOCB type, partial data 0x%x\n",
           buffer_copy_offset);
    break;
   }

   rsp_q->ring_index++;
   if (rsp_q->ring_index == rsp_q->length) {
    rsp_q->ring_index = 0;
    rsp_q->ring_ptr = rsp_q->ring;
   } else {
    rsp_q->ring_ptr++;
   }
   no_bytes = (pending_bytes > sizeof(new_pkt->data)) ?
    sizeof(new_pkt->data) : pending_bytes;
   if ((buffer_copy_offset + no_bytes) <= total_bytes) {
    memcpy(((uint8_t *)iocb_pkt + buffer_copy_offset),
           new_pkt->data, no_bytes);
    buffer_copy_offset += no_bytes;
    pending_bytes -= no_bytes;
    --entry_count_remaining;
   } else {
    ql_log(ql_log_warn, vha, 0x5044,
           "Attempt to copy more that we got, optimizing..%x\n",
           buffer_copy_offset);
    memcpy(((uint8_t *)iocb_pkt + buffer_copy_offset),
           new_pkt->data,
           total_bytes - buffer_copy_offset);
   }

   ((response_t *)new_pkt)->signature = RESPONSE_PROCESSED;
   wmb();
  }

  if (pending_bytes != 0 || entry_count_remaining != 0) {
   ql_log(ql_log_fatal, vha, 0x508b,
          "Dropping partial FPIN, underrun bytes = 0x%x, entry cnts 0x%x\n",
          total_bytes, entry_count_remaining);
   qla24xx_free_purex_item(item);
   return NULL;
  }
 } while (entry_count_remaining > 0);

 if (byte_order)
  host_to_fcp_swap((uint8_t *)&item->iocb, total_bytes);

 return item;
}

int
qla2x00_is_a_vp_did(scsi_qla_host_t *vha, uint32_t rscn_entry)
{
 struct qla_hw_data *ha = vha->hw;
 scsi_qla_host_t *vp;
 uint32_t vp_did;
 unsigned long flags;
 int ret = 0;

 if (!ha->num_vhosts)
  return ret;

 spin_lock_irqsave(&ha->vport_slock, flags);
 list_for_each_entry(vp, &ha->vp_list, list) {
  vp_did = vp->d_id.b24;
  if (vp_did == rscn_entry) {
   ret = 1;
   break;
  }
 }
 spin_unlock_irqrestore(&ha->vport_slock, flags);

 return ret;
}

fc_port_t *
qla2x00_find_fcport_by_loopid(scsi_qla_host_t *vha, uint16_t loop_id)
{
 fc_port_t *f, *tf;

 f = tf = NULL;
 list_for_each_entry_safe(f, tf, &vha->vp_fcports, list)
  if (f->loop_id == loop_id)
   return f;
 return NULL;
}

fc_port_t *
qla2x00_find_fcport_by_wwpn(scsi_qla_host_t *vha, u8 *wwpn, u8 incl_deleted)
{
 fc_port_t *f, *tf;

 f = tf = NULL;
 list_for_each_entry_safe(f, tf, &vha->vp_fcports, list) {
  if (memcmp(f->port_name, wwpn, WWN_SIZE) == 0) {
   if (incl_deleted)
    return f;
   else if (f->deleted == 0)
    return f;
  }
 }
 return NULL;
}

fc_port_t *
qla2x00_find_fcport_by_nportid(scsi_qla_host_t *vha, port_id_t *id,
 u8 incl_deleted)
{
 fc_port_t *f, *tf;

 f = tf = NULL;
 list_for_each_entry_safe(f, tf, &vha->vp_fcports, list) {
  if (f->d_id.b24 == id->b24) {
   if (incl_deleted)
    return f;
   else if (f->deleted == 0)
    return f;
  }
 }
 return NULL;
}

/* Shall be called only on supported adapters. */
static void
qla27xx_handle_8200_aen(scsi_qla_host_t *vha, uint16_t *mb)
{
 struct qla_hw_data *ha = vha->hw;
 bool reset_isp_needed = false;

 ql_log(ql_log_warn, vha, 0x02f0,
        "MPI Heartbeat stop. MPI reset is%s needed. "
        "MB0[%xh] MB1[%xh] MB2[%xh] MB3[%xh]\n",
        mb[1] & BIT_8 ? "" : " not",
        mb[0], mb[1], mb[2], mb[3]);

 if ((mb[1] & BIT_8) == 0)
  return;

 ql_log(ql_log_warn, vha, 0x02f1,
        "MPI Heartbeat stop. FW dump needed\n");

 if (ql2xfulldump_on_mpifail) {
  ha->isp_ops->fw_dump(vha);
  reset_isp_needed = true;
 }

 ha->isp_ops->mpi_fw_dump(vha, 1);

 if (reset_isp_needed) {
  vha->hw->flags.fw_init_done = 0;
  set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
  qla2xxx_wake_dpc(vha);
 }
}

static struct purex_item *
qla24xx_alloc_purex_item(scsi_qla_host_t *vha, uint16_t size)
{
 struct purex_item *item = NULL;
 uint8_t item_hdr_size = sizeof(*item);

 if (size > QLA_DEFAULT_PAYLOAD_SIZE) {
  item = kzalloc(item_hdr_size +
      (size - QLA_DEFAULT_PAYLOAD_SIZE), GFP_ATOMIC);
 } else {
  if (atomic_inc_return(&vha->default_item.in_use) == 1) {
   item = &vha->default_item;
   goto initialize_purex_header;
  } else {
   item = kzalloc(item_hdr_size, GFP_ATOMIC);
  }
 }
 if (!item) {
  ql_log(ql_log_warn, vha, 0x5092,
         ">> Failed allocate purex list item.\n");

  return NULL;
 }

initialize_purex_header:
 item->vha = vha;
 item->size = size;
 return item;
}

void
qla24xx_queue_purex_item(scsi_qla_host_t *vha, struct purex_item *pkt,
    void (*process_item)(struct scsi_qla_host *vha,
           struct purex_item *pkt))
{
 struct purex_list *list = &vha->purex_list;
 ulong flags;

 pkt->process_item = process_item;

 spin_lock_irqsave(&list->lock, flags);
 list_add_tail(&pkt->list, &list->head);
 spin_unlock_irqrestore(&list->lock, flags);

 set_bit(PROCESS_PUREX_IOCB, &vha->dpc_flags);
}

/**
 * qla24xx_copy_std_pkt() - Copy over purex ELS which is
 * contained in a single IOCB.
 * purex packet.
 * @vha: SCSI driver HA context
 * @pkt: ELS packet
 */

static struct purex_item
*qla24xx_copy_std_pkt(struct scsi_qla_host *vha, void *pkt)
{
 struct purex_item *item;

 item = qla24xx_alloc_purex_item(vha,
     QLA_DEFAULT_PAYLOAD_SIZE);
 if (!item)
  return item;

 memcpy(&item->iocb, pkt, sizeof(item->iocb));
 return item;
}

/**
 * qla27xx_copy_fpin_pkt() - Copy over fpin packets that can
 * span over multiple IOCBs.
 * @vha: SCSI driver HA context
 * @pkt: ELS packet
 * @rsp: Response queue
 */

static struct purex_item *
qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt,
        struct rsp_que **rsp)
{
 struct purex_entry_24xx *purex = *pkt;
 struct rsp_que *rsp_q = *rsp;
 sts_cont_entry_t *new_pkt;
 uint16_t no_bytes = 0, total_bytes = 0, pending_bytes = 0;
 uint16_t buffer_copy_offset = 0;
 uint16_t entry_count, entry_count_remaining;
 struct purex_item *item;
 void *fpin_pkt = NULL;

 total_bytes = (le16_to_cpu(purex->frame_size) & 0x0FFF)
     - PURX_ELS_HEADER_SIZE;
 pending_bytes = total_bytes;
 entry_count = entry_count_remaining = purex->entry_count;
 no_bytes = (pending_bytes > sizeof(purex->els_frame_payload))  ?
     sizeof(purex->els_frame_payload) : pending_bytes;
 ql_log(ql_log_info, vha, 0x509a,
        "FPIN ELS, frame_size 0x%x, entry count %d\n",
        total_bytes, entry_count);

 item = qla24xx_alloc_purex_item(vha, total_bytes);
 if (!item)
  return item;

 fpin_pkt = &item->iocb;

 memcpy(fpin_pkt, &purex->els_frame_payload[0], no_bytes);
 buffer_copy_offset += no_bytes;
 pending_bytes -= no_bytes;
 --entry_count_remaining;

 ((response_t *)purex)->signature = RESPONSE_PROCESSED;
 wmb();

 do {
  while ((total_bytes > 0) && (entry_count_remaining > 0)) {
   if (rsp_q->ring_ptr->signature == RESPONSE_PROCESSED) {
    ql_dbg(ql_dbg_async, vha, 0x5084,
           "Ran out of IOCBs, partial data 0x%x\n",
           buffer_copy_offset);
    cpu_relax();
    continue;
   }

   new_pkt = (sts_cont_entry_t *)rsp_q->ring_ptr;
   *pkt = new_pkt;

   if (new_pkt->entry_type != STATUS_CONT_TYPE) {
    ql_log(ql_log_warn, vha, 0x507a,
           "Unexpected IOCB type, partial data 0x%x\n",
           buffer_copy_offset);
    break;
   }

   rsp_q->ring_index++;
   if (rsp_q->ring_index == rsp_q->length) {
    rsp_q->ring_index = 0;
    rsp_q->ring_ptr = rsp_q->ring;
   } else {
    rsp_q->ring_ptr++;
   }
   no_bytes = (pending_bytes > sizeof(new_pkt->data)) ?
       sizeof(new_pkt->data) : pending_bytes;
   if ((buffer_copy_offset + no_bytes) <= total_bytes) {
    memcpy(((uint8_t *)fpin_pkt +
        buffer_copy_offset), new_pkt->data,
        no_bytes);
    buffer_copy_offset += no_bytes;
    pending_bytes -= no_bytes;
    --entry_count_remaining;
   } else {
    ql_log(ql_log_warn, vha, 0x5044,
           "Attempt to copy more that we got, optimizing..%x\n",
           buffer_copy_offset);
    memcpy(((uint8_t *)fpin_pkt +
        buffer_copy_offset), new_pkt->data,
        total_bytes - buffer_copy_offset);
   }

   ((response_t *)new_pkt)->signature = RESPONSE_PROCESSED;
   wmb();
  }

  if (pending_bytes != 0 || entry_count_remaining != 0) {
   ql_log(ql_log_fatal, vha, 0x508b,
          "Dropping partial FPIN, underrun bytes = 0x%x, entry cnts 0x%x\n",
          total_bytes, entry_count_remaining);
   qla24xx_free_purex_item(item);
   return NULL;
  }
 } while (entry_count_remaining > 0);
 host_to_fcp_swap((uint8_t *)&item->iocb, total_bytes);
 return item;
}

/**
 * qla2x00_async_event() - Process aynchronous events.
 * @vha: SCSI driver HA context
 * @rsp: response queue
 * @mb: Mailbox registers (0 - 3)
 */

void
qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb)
{
 uint16_t handle_cnt;
 uint16_t cnt, mbx;
 uint32_t handles[5];
 struct qla_hw_data *ha = vha->hw;
 struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
 struct device_reg_24xx __iomem *reg24 = &ha->iobase->isp24;
 struct device_reg_82xx __iomem *reg82 = &ha->iobase->isp82;
 uint32_t rscn_entry, host_pid;
 unsigned long flags;
 fc_port_t *fcport = NULL;

 if (!vha->hw->flags.fw_started) {
  ql_log(ql_log_warn, vha, 0x50ff,
      "Dropping AEN - %04x %04x %04x %04x.\n",
      mb[0], mb[1], mb[2], mb[3]);
  return;
 }

 /* Setup to process RIO completion. */
 handle_cnt = 0;
 if (IS_CNA_CAPABLE(ha))
  goto skip_rio;
 switch (mb[0]) {
 case MBA_SCSI_COMPLETION:
  handles[0] = make_handle(mb[2], mb[1]);
  handle_cnt = 1;
  break;
 case MBA_CMPLT_1_16BIT:
  handles[0] = mb[1];
  handle_cnt = 1;
  mb[0] = MBA_SCSI_COMPLETION;
  break;
 case MBA_CMPLT_2_16BIT:
  handles[0] = mb[1];
  handles[1] = mb[2];
  handle_cnt = 2;
  mb[0] = MBA_SCSI_COMPLETION;
  break;
 case MBA_CMPLT_3_16BIT:
  handles[0] = mb[1];
  handles[1] = mb[2];
  handles[2] = mb[3];
  handle_cnt = 3;
  mb[0] = MBA_SCSI_COMPLETION;
  break;
 case MBA_CMPLT_4_16BIT:
  handles[0] = mb[1];
  handles[1] = mb[2];
  handles[2] = mb[3];
  handles[3] = (uint32_t)RD_MAILBOX_REG(ha, reg, 6);
  handle_cnt = 4;
  mb[0] = MBA_SCSI_COMPLETION;
  break;
 case MBA_CMPLT_5_16BIT:
  handles[0] = mb[1];
  handles[1] = mb[2];
  handles[2] = mb[3];
  handles[3] = (uint32_t)RD_MAILBOX_REG(ha, reg, 6);
  handles[4] = (uint32_t)RD_MAILBOX_REG(ha, reg, 7);
  handle_cnt = 5;
  mb[0] = MBA_SCSI_COMPLETION;
  break;
 case MBA_CMPLT_2_32BIT:
  handles[0] = make_handle(mb[2], mb[1]);
  handles[1] = make_handle(RD_MAILBOX_REG(ha, reg, 7),
      RD_MAILBOX_REG(ha, reg, 6));
  handle_cnt = 2;
  mb[0] = MBA_SCSI_COMPLETION;
  break;
 default:
  break;
 }
skip_rio:
 switch (mb[0]) {
 case MBA_SCSI_COMPLETION: /* Fast Post */
  if (!vha->flags.online)
   break;

  for (cnt = 0; cnt < handle_cnt; cnt++)
   qla2x00_process_completed_request(vha, rsp->req,
    handles[cnt]);
  break;

 case MBA_RESET:   /* Reset */
  ql_dbg(ql_dbg_async, vha, 0x5002,
      "Asynchronous RESET.\n");

  set_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
  break;

 case MBA_SYSTEM_ERR:  /* System Error */
  mbx = 0;

  vha->hw_err_cnt++;

  if (IS_QLA81XX(ha) || IS_QLA83XX(ha) ||
      IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
   u16 m[4];

   m[0] = rd_reg_word(®24->mailbox4);
   m[1] = rd_reg_word(®24->mailbox5);
   m[2] = rd_reg_word(®24->mailbox6);
   mbx = m[3] = rd_reg_word(®24->mailbox7);

   ql_log(ql_log_warn, vha, 0x5003,
       "ISP System Error - mbx1=%xh mbx2=%xh mbx3=%xh mbx4=%xh mbx5=%xh mbx6=%xh mbx7=%xh.\n",
       mb[1], mb[2], mb[3], m[0], m[1], m[2], m[3]);
  } else
   ql_log(ql_log_warn, vha, 0x5003,
       "ISP System Error - mbx1=%xh mbx2=%xh mbx3=%xh.\n ",
       mb[1], mb[2], mb[3]);

  if ((IS_QLA27XX(ha) || IS_QLA28XX(ha)) &&
      rd_reg_word(®24->mailbox7) & BIT_8)
   ha->isp_ops->mpi_fw_dump(vha, 1);
  ha->isp_ops->fw_dump(vha);
  ha->flags.fw_init_done = 0;
  QLA_FW_STOPPED(ha);

  if (IS_FWI2_CAPABLE(ha)) {
   if (mb[1] == 0 && mb[2] == 0) {
    ql_log(ql_log_fatal, vha, 0x5004,
        "Unrecoverable Hardware Error: adapter "
        "marked OFFLINE!\n");
    vha->flags.online = 0;
    vha->device_flags |= DFLG_DEV_FAILED;
   } else {
    /* Check to see if MPI timeout occurred */
    if ((mbx & MBX_3) && (ha->port_no == 0))
     set_bit(MPI_RESET_NEEDED,
         &vha->dpc_flags);

    set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
   }
  } else if (mb[1] == 0) {
   ql_log(ql_log_fatal, vha, 0x5005,
       "Unrecoverable Hardware Error: adapter marked "
       "OFFLINE!\n");
   vha->flags.online = 0;
   vha->device_flags |= DFLG_DEV_FAILED;
  } else
   set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
  break;

 case MBA_REQ_TRANSFER_ERR: /* Request Transfer Error */
  ql_log(ql_log_warn, vha, 0x5006,
      "ISP Request Transfer Error (%x).\n",  mb[1]);

  vha->hw_err_cnt++;

  set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
  break;

 case MBA_RSP_TRANSFER_ERR: /* Response Transfer Error */
  ql_log(ql_log_warn, vha, 0x5007,
      "ISP Response Transfer Error (%x).\n", mb[1]);

  vha->hw_err_cnt++;

  set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
  break;

 case MBA_WAKEUP_THRES:  /* Request Queue Wake-up */
  ql_dbg(ql_dbg_async, vha, 0x5008,
      "Asynchronous WAKEUP_THRES (%x).\n", mb[1]);
  break;

 case MBA_LOOP_INIT_ERR:
  ql_log(ql_log_warn, vha, 0x5090,
      "LOOP INIT ERROR (%x).\n", mb[1]);
  set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
  break;

 case MBA_LIP_OCCURRED:  /* Loop Initialization Procedure */
  ha->flags.lip_ae = 1;

  ql_dbg(ql_dbg_async, vha, 0x5009,
      "LIP occurred (%x).\n", mb[1]);

  if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
   atomic_set(&vha->loop_state, LOOP_DOWN);
   atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
   qla2x00_mark_all_devices_lost(vha);
  }

  if (vha->vp_idx) {
   atomic_set(&vha->vp_state, VP_FAILED);
   fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED);
  }

  set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags);
  set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);

  vha->flags.management_server_logged_in = 0;
  qla2x00_post_aen_work(vha, FCH_EVT_LIP, mb[1]);
  break;

 case MBA_LOOP_UP:  /* Loop Up Event */
  if (IS_QLA2100(ha) || IS_QLA2200(ha))
   ha->link_data_rate = PORT_SPEED_1GB;
  else
   ha->link_data_rate = mb[1];

  ql_log(ql_log_info, vha, 0x500a,
      "LOOP UP detected (%s Gbps).\n",
      qla2x00_get_link_speed_str(ha, ha->link_data_rate));

  if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
   if (mb[2] & BIT_0)
    ql_log(ql_log_info, vha, 0x11a0,
        "FEC=enabled (link up).\n");
  }

  vha->flags.management_server_logged_in = 0;
  qla2x00_post_aen_work(vha, FCH_EVT_LINKUP, ha->link_data_rate);

  if (vha->link_down_time < vha->hw->port_down_retry_count) {
   vha->short_link_down_cnt++;
   vha->link_down_time = QLA2XX_MAX_LINK_DOWN_TIME;
  }

  break;

 case MBA_LOOP_DOWN:  /* Loop Down Event */
  SAVE_TOPO(ha);
  ha->flags.lip_ae = 0;
  ha->current_topology = 0;
  vha->link_down_time = 0;

  mbx = (IS_QLA81XX(ha) || IS_QLA8031(ha))
   ? rd_reg_word(®24->mailbox4) : 0;
  mbx = (IS_P3P_TYPE(ha)) ? rd_reg_word(®82->mailbox_out[4])
   : mbx;
  ql_log(ql_log_info, vha, 0x500b,
      "LOOP DOWN detected (%x %x %x %x).\n",
      mb[1], mb[2], mb[3], mbx);

  if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
   atomic_set(&vha->loop_state, LOOP_DOWN);
   atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
   /*
 * In case of loop down, restore WWPN from
 * NVRAM in case of FA-WWPN capable ISP
 * Restore for Physical Port only
 */

   if (!vha->vp_idx) {
    if (ha->flags.fawwpn_enabled &&
        (ha->current_topology == ISP_CFG_F)) {
     memcpy(vha->port_name, ha->port_name, WWN_SIZE);
     fc_host_port_name(vha->host) =
         wwn_to_u64(vha->port_name);
     ql_dbg(ql_dbg_init + ql_dbg_verbose,
         vha, 0x00d8, "LOOP DOWN detected,"
         "restore WWPN %016llx\n",
         wwn_to_u64(vha->port_name));
    }

    clear_bit(VP_CONFIG_OK, &vha->vp_flags);
   }

   vha->device_flags |= DFLG_NO_CABLE;
   qla2x00_mark_all_devices_lost(vha);
  }

  if (vha->vp_idx) {
   atomic_set(&vha->vp_state, VP_FAILED);
   fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED);
  }

  vha->flags.management_server_logged_in = 0;
  ha->link_data_rate = PORT_SPEED_UNKNOWN;
  qla2x00_post_aen_work(vha, FCH_EVT_LINKDOWN, 0);
  break;

 case MBA_LIP_RESET:  /* LIP reset occurred */
  ql_dbg(ql_dbg_async, vha, 0x500c,
      "LIP reset occurred (%x).\n", mb[1]);

  if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
   atomic_set(&vha->loop_state, LOOP_DOWN);
   atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
   qla2x00_mark_all_devices_lost(vha);
  }

  if (vha->vp_idx) {
   atomic_set(&vha->vp_state, VP_FAILED);
   fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED);
  }

  set_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);

  ha->operating_mode = LOOP;
  vha->flags.management_server_logged_in = 0;
  qla2x00_post_aen_work(vha, FCH_EVT_LIPRESET, mb[1]);
  break;

 /* case MBA_DCBX_COMPLETE: */
 case MBA_POINT_TO_POINT: /* Point-to-Point */
  ha->flags.lip_ae = 0;

  if (IS_QLA2100(ha))
   break;

  if (IS_CNA_CAPABLE(ha)) {
   ql_dbg(ql_dbg_async, vha, 0x500d,
       "DCBX Completed -- %04x %04x %04x.\n",
       mb[1], mb[2], mb[3]);
   if (ha->notify_dcbx_comp && !vha->vp_idx)
    complete(&ha->dcbx_comp);

  } else
   ql_dbg(ql_dbg_async, vha, 0x500e,
       "Asynchronous P2P MODE received.\n");

  /*
 * Until there's a transition from loop down to loop up, treat
 * this as loop down only.
 */

  if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
   atomic_set(&vha->loop_state, LOOP_DOWN);
   if (!atomic_read(&vha->loop_down_timer))
    atomic_set(&vha->loop_down_timer,
        LOOP_DOWN_TIME);
   if (!N2N_TOPO(ha))
    qla2x00_mark_all_devices_lost(vha);
  }

  if (vha->vp_idx) {
   atomic_set(&vha->vp_state, VP_FAILED);
   fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED);
  }

  if (!(test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)))
   set_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);

  set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags);
  set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);

  vha->flags.management_server_logged_in = 0;
  break;

 case MBA_CHG_IN_CONNECTION: /* Change in connection mode */
  if (IS_QLA2100(ha))
   break;

  ql_dbg(ql_dbg_async, vha, 0x500f,
      "Configuration change detected: value=%x.\n", mb[1]);

  if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
   atomic_set(&vha->loop_state, LOOP_DOWN);
   if (!atomic_read(&vha->loop_down_timer))
    atomic_set(&vha->loop_down_timer,
        LOOP_DOWN_TIME);
   qla2x00_mark_all_devices_lost(vha);
  }

  if (vha->vp_idx) {
   atomic_set(&vha->vp_state, VP_FAILED);
   fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED);
  }

  set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
  set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
  break;

 case MBA_PORT_UPDATE:  /* Port database update */
  /*
 * Handle only global and vn-port update events
 *
 * Relevant inputs:
 * mb[1] = N_Port handle of changed port
 * OR 0xffff for global event
 * mb[2] = New login state
 * 7 = Port logged out
 * mb[3] = LSB is vp_idx, 0xff = all vps
 *
 * Skip processing if:
 *       Event is global, vp_idx is NOT all vps,
 *           vp_idx does not match
 *       Event is not global, vp_idx does not match
 */

  if (IS_QLA2XXX_MIDTYPE(ha) &&
      ((mb[1] == 0xffff && (mb[3] & 0xff) != 0xff) ||
   (mb[1] != 0xffff)) && vha->vp_idx != (mb[3] & 0xff))
   break;

  if (mb[2] == 0x7) {
   ql_dbg(ql_dbg_async, vha, 0x5010,
       "Port %s %04x %04x %04x.\n",
       mb[1] == 0xffff ? "unavailable" : "logout",
       mb[1], mb[2], mb[3]);

   if (mb[1] == 0xffff)
    goto global_port_update;

   if (mb[1] == NPH_SNS_LID(ha)) {
    set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
    set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
    break;
   }

   /* use handle_cnt for loop id/nport handle */
   if (IS_FWI2_CAPABLE(ha))
    handle_cnt = NPH_SNS;
   else
    handle_cnt = SIMPLE_NAME_SERVER;
   if (mb[1] == handle_cnt) {
    set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
    set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
    break;
   }

   /* Port logout */
   fcport = qla2x00_find_fcport_by_loopid(vha, mb[1]);
   if (!fcport)
    break;
   if (atomic_read(&fcport->state) != FCS_ONLINE)
    break;
   ql_dbg(ql_dbg_async, vha, 0x508a,
       "Marking port lost loopid=%04x portid=%06x.\n",
       fcport->loop_id, fcport->d_id.b24);
   if (qla_ini_mode_enabled(vha)) {
    fcport->logout_on_delete = 0;
    qlt_schedule_sess_for_deletion(fcport);
   }
   break;

global_port_update:
   if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
    atomic_set(&vha->loop_state, LOOP_DOWN);
    atomic_set(&vha->loop_down_timer,
        LOOP_DOWN_TIME);
    vha->device_flags |= DFLG_NO_CABLE;
    qla2x00_mark_all_devices_lost(vha);
   }

   if (vha->vp_idx) {
    atomic_set(&vha->vp_state, VP_FAILED);
    fc_vport_set_state(vha->fc_vport,
        FC_VPORT_FAILED);
    qla2x00_mark_all_devices_lost(vha);
   }

   vha->flags.management_server_logged_in = 0;
   ha->link_data_rate = PORT_SPEED_UNKNOWN;
   break;
  }

  /*
 * If PORT UPDATE is global (received LIP_OCCURRED/LIP_RESET
 * event etc. earlier indicating loop is down) then process
 * it.  Otherwise ignore it and Wait for RSCN to come in.
 */

  atomic_set(&vha->loop_down_timer, 0);
  if (atomic_read(&vha->loop_state) != LOOP_DOWN &&
   !ha->flags.n2n_ae  &&
      atomic_read(&vha->loop_state) != LOOP_DEAD) {
   ql_dbg(ql_dbg_async, vha, 0x5011,
       "Asynchronous PORT UPDATE ignored %04x/%04x/%04x.\n",
       mb[1], mb[2], mb[3]);
   break;
  }

  ql_dbg(ql_dbg_async, vha, 0x5012,
      "Port database changed %04x %04x %04x.\n",
      mb[1], mb[2], mb[3]);

  /*
 * Mark all devices as missing so we will login again.
 */

  atomic_set(&vha->loop_state, LOOP_UP);
  vha->scan.scan_retry = 0;

  set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
  set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
  set_bit(VP_CONFIG_OK, &vha->vp_flags);
  break;

 case MBA_RSCN_UPDATE:  /* State Change Registration */
  /* Check if the Vport has issued a SCR */
  if (vha->vp_idx && test_bit(VP_SCR_NEEDED, &vha->vp_flags))
   break;
  /* Only handle SCNs for our Vport index. */
  if (ha->flags.npiv_supported && vha->vp_idx != (mb[3] & 0xff))
   break;

  ql_log(ql_log_warn, vha, 0x5013,
         "RSCN database changed -- %04x %04x %04x.\n",
         mb[1], mb[2], mb[3]);

  rscn_entry = ((mb[1] & 0xff) << 16) | mb[2];
  host_pid = (vha->d_id.b.domain << 16) | (vha->d_id.b.area << 8)
    | vha->d_id.b.al_pa;
  if (rscn_entry == host_pid) {
   ql_dbg(ql_dbg_async, vha, 0x5014,
       "Ignoring RSCN update to local host "
       "port ID (%06x).\n", host_pid);
   break;
  }

  /* Ignore reserved bits from RSCN-payload. */
  rscn_entry = ((mb[1] & 0x3ff) << 16) | mb[2];

  /* Skip RSCNs for virtual ports on the same physical port */
  if (qla2x00_is_a_vp_did(vha, rscn_entry))
   break;

  atomic_set(&vha->loop_down_timer, 0);
  vha->flags.management_server_logged_in = 0;
  {
   struct event_arg ea;

   memset(&ea, 0, sizeof(ea));
   ea.id.b24 = rscn_entry;
   ea.id.b.rsvd_1 = rscn_entry >> 24;
   qla2x00_handle_rscn(vha, &ea);
   qla2x00_post_aen_work(vha, FCH_EVT_RSCN, rscn_entry);
  }
  break;
 case MBA_CONGN_NOTI_RECV:
  if (!ha->flags.scm_enabled ||
      mb[1] != QLA_CON_PRIMITIVE_RECEIVED)
   break;

  if (mb[2] == QLA_CONGESTION_ARB_WARNING) {
   ql_dbg(ql_dbg_async, vha, 0x509b,
          "Congestion Warning %04x %04x.\n", mb[1], mb[2]);
  } else if (mb[2] == QLA_CONGESTION_ARB_ALARM) {
   ql_log(ql_log_warn, vha, 0x509b,
          "Congestion Alarm %04x %04x.\n", mb[1], mb[2]);
  }
  break;
 /* case MBA_RIO_RESPONSE: */
 case MBA_ZIO_RESPONSE:
  ql_dbg(ql_dbg_async, vha, 0x5015,
      "[R|Z]IO update completion.\n");

  if (IS_FWI2_CAPABLE(ha))
   qla24xx_process_response_queue(vha, rsp);
  else
   qla2x00_process_response_queue(rsp);
  break;

 case MBA_DISCARD_RND_FRAME:
  ql_dbg(ql_dbg_async, vha, 0x5016,
      "Discard RND Frame -- %04x %04x %04x.\n",
      mb[1], mb[2], mb[3]);
  vha->interface_err_cnt++;
  break;

 case MBA_TRACE_NOTIFICATION:
  ql_dbg(ql_dbg_async, vha, 0x5017,
      "Trace Notification -- %04x %04x.\n", mb[1], mb[2]);
  break;

 case MBA_ISP84XX_ALERT:
  ql_dbg(ql_dbg_async, vha, 0x5018,
      "ISP84XX Alert Notification -- %04x %04x %04x.\n",
      mb[1], mb[2], mb[3]);

  spin_lock_irqsave(&ha->cs84xx->access_lock, flags);
  switch (mb[1]) {
  case A84_PANIC_RECOVERY:
   ql_log(ql_log_info, vha, 0x5019,
       "Alert 84XX: panic recovery %04x %04x.\n",
       mb[2], mb[3]);
   break;
  case A84_OP_LOGIN_COMPLETE:
   ha->cs84xx->op_fw_version = mb[3] << 16 | mb[2];
   ql_log(ql_log_info, vha, 0x501a,
       "Alert 84XX: firmware version %x.\n",
       ha->cs84xx->op_fw_version);
   break;
  case A84_DIAG_LOGIN_COMPLETE:
   ha->cs84xx->diag_fw_version = mb[3] << 16 | mb[2];
   ql_log(ql_log_info, vha, 0x501b,
       "Alert 84XX: diagnostic firmware version %x.\n",
       ha->cs84xx->diag_fw_version);
   break;
  case A84_GOLD_LOGIN_COMPLETE:
   ha->cs84xx->diag_fw_version = mb[3] << 16 | mb[2];
   ha->cs84xx->fw_update = 1;
   ql_log(ql_log_info, vha, 0x501c,
       "Alert 84XX: gold firmware version %x.\n",
       ha->cs84xx->gold_fw_version);
   break;
  default:
   ql_log(ql_log_warn, vha, 0x501d,
       "Alert 84xx: Invalid Alert %04x %04x %04x.\n",
       mb[1], mb[2], mb[3]);
  }
  spin_unlock_irqrestore(&ha->cs84xx->access_lock, flags);
  break;
 case MBA_DCBX_START:
  ql_dbg(ql_dbg_async, vha, 0x501e,
      "DCBX Started -- %04x %04x %04x.\n",
      mb[1], mb[2], mb[3]);
  break;
 case MBA_DCBX_PARAM_UPDATE:
  ql_dbg(ql_dbg_async, vha, 0x501f,
      "DCBX Parameters Updated -- %04x %04x %04x.\n",
      mb[1], mb[2], mb[3]);
  break;
 case MBA_FCF_CONF_ERR:
  ql_dbg(ql_dbg_async, vha, 0x5020,
      "FCF Configuration Error -- %04x %04x %04x.\n",
      mb[1], mb[2], mb[3]);
  break;
 case MBA_IDC_NOTIFY:
  if (IS_QLA8031(vha->hw) || IS_QLA8044(ha)) {
   mb[4] = rd_reg_word(®24->mailbox4);
   if (((mb[2] & 0x7fff) == MBC_PORT_RESET ||
       (mb[2] & 0x7fff) == MBC_SET_PORT_CONFIG) &&
       (mb[4] & INTERNAL_LOOPBACK_MASK) != 0) {
    set_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags);
    /*
 * Extend loop down timer since port is active.
 */

    if (atomic_read(&vha->loop_state) == LOOP_DOWN)
     atomic_set(&vha->loop_down_timer,
         LOOP_DOWN_TIME);
    qla2xxx_wake_dpc(vha);
   }
  }
  fallthrough;
 case MBA_IDC_COMPLETE:
  if (ha->notify_lb_portup_comp && !vha->vp_idx)
   complete(&ha->lb_portup_comp);
  fallthrough;
 case MBA_IDC_TIME_EXT:
  if (IS_QLA81XX(vha->hw) || IS_QLA8031(vha->hw) ||
      IS_QLA8044(ha))
   qla81xx_idc_event(vha, mb[0], mb[1]);
  break;

 case MBA_IDC_AEN:
  if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
   vha->hw_err_cnt++;
   qla27xx_handle_8200_aen(vha, mb);
  } else if (IS_QLA83XX(ha)) {
   mb[4] = rd_reg_word(®24->mailbox4);
   mb[5] = rd_reg_word(®24->mailbox5);
   mb[6] = rd_reg_word(®24->mailbox6);
   mb[7] = rd_reg_word(®24->mailbox7);
   qla83xx_handle_8200_aen(vha, mb);
  } else {
   ql_dbg(ql_dbg_async, vha, 0x5052,
       "skip Heartbeat processing mb0-3=[0x%04x] [0x%04x] [0x%04x] [0x%04x]\n",
       mb[0], mb[1], mb[2], mb[3]);
  }
  break;

 case MBA_DPORT_DIAGNOSTICS:
  if ((mb[1] & 0xF) == AEN_DONE_DIAG_TEST_WITH_NOERR ||
      (mb[1] & 0xF) == AEN_DONE_DIAG_TEST_WITH_ERR)
   vha->dport_status &= ~DPORT_DIAG_IN_PROGRESS;
  ql_dbg(ql_dbg_async, vha, 0x5052,
      "D-Port Diagnostics: %04x %04x %04x %04x\n",
      mb[0], mb[1], mb[2], mb[3]);
  memcpy(vha->dport_data, mb, sizeof(vha->dport_data));
  if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
   static char *results[] = {
       "start""done(pass)""done(error)""undefined" };
   static char *types[] = {
       "none""dynamic""static""other" };
   uint result = mb[1] >> 0 & 0x3;
   uint type = mb[1] >> 6 & 0x3;
   uint sw = mb[1] >> 15 & 0x1;
   ql_dbg(ql_dbg_async, vha, 0x5052,
       "D-Port Diagnostics: result=%s type=%s [sw=%u]\n",
       results[result], types[type], sw);
   if (result == 2) {
    static char *reasons[] = {
        "reserved""unexpected reject",
        "unexpected phase""retry exceeded",
        "timed out""not supported",
        "user stopped" };
    uint reason = mb[2] >> 0 & 0xf;
    uint phase = mb[2] >> 12 & 0xf;
    ql_dbg(ql_dbg_async, vha, 0x5052,
        "D-Port Diagnostics: reason=%s phase=%u \n",
        reason < 7 ? reasons[reason] : "other",
        phase >> 1);
   }
  }
  break;

 case MBA_TEMPERATURE_ALERT:
  if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
   display_Laser_info(vha, mb[1], mb[2], mb[3]);
  ql_dbg(ql_dbg_async, vha, 0x505e,
      "TEMPERATURE ALERT: %04x %04x %04x\n", mb[1], mb[2], mb[3]);
  break;

 case MBA_TRANS_INSERT:
  ql_dbg(ql_dbg_async, vha, 0x5091,
      "Transceiver Insertion: %04x\n", mb[1]);
  set_bit(DETECT_SFP_CHANGE, &vha->dpc_flags);
  break;

 case MBA_TRANS_REMOVE:
  ql_dbg(ql_dbg_async, vha, 0x5091, "Transceiver Removal\n");
  break;

 default:
  ql_dbg(ql_dbg_async, vha, 0x5057,
      "Unknown AEN:%04x %04x %04x %04x\n",
      mb[0], mb[1], mb[2], mb[3]);
 }

 qlt_async_event(mb[0], vha, mb);

 if (!vha->vp_idx && ha->num_vhosts)
  qla2x00_alert_all_vps(rsp, mb);
}

/**
 * qla2x00_process_completed_request() - Process a Fast Post response.
 * @vha: SCSI driver HA context
 * @req: request queue
 * @index: SRB index
 */

void
qla2x00_process_completed_request(struct scsi_qla_host *vha,
      struct req_que *req, uint32_t index)
{
 srb_t *sp;
 struct qla_hw_data *ha = vha->hw;

 /* Validate handle. */
 if (index >= req->num_outstanding_cmds) {
  ql_log(ql_log_warn, vha, 0x3014,
      "Invalid SCSI command index (%x).\n", index);

  if (IS_P3P_TYPE(ha))
   set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
  else
   set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
  return;
 }

 sp = req->outstanding_cmds[index];
 if (sp) {
  /* Free outstanding command slot. */
  req->outstanding_cmds[index] = NULL;

  /* Save ISP completion status */
  sp->done(sp, DID_OK << 16);
 } else {
  ql_log(ql_log_warn, vha, 0x3016, "Invalid SCSI SRB.\n");

  if (IS_P3P_TYPE(ha))
   set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
  else
   set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
 }
}

static srb_t *
qla_get_sp_from_handle(scsi_qla_host_t *vha, const char *func,
         struct req_que *req, void *iocb, u16 *ret_index)
{
 struct qla_hw_data *ha = vha->hw;
 sts_entry_t *pkt = iocb;
 srb_t *sp;
 uint16_t index;

 if (pkt->handle == QLA_SKIP_HANDLE)
  return NULL;

 index = LSW(pkt->handle);
 if (index >= req->num_outstanding_cmds) {
  ql_log(ql_log_warn, vha, 0x5031,
      "%s: Invalid command index (%x) type %8ph.\n",
      func, index, iocb);
  if (IS_P3P_TYPE(ha))
   set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
  else
   set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
  return NULL;
 }
 sp = req->outstanding_cmds[index];
 if (!sp) {
  ql_log(ql_log_warn, vha, 0x5032,
   "%s: Invalid completion handle (%x) -- timed-out.\n",
   func, index);
  return NULL;
 }
 if (sp->handle != index) {
  ql_log(ql_log_warn, vha, 0x5033,
   "%s: SRB handle (%x) mismatch %x.\n", func,
   sp->handle, index);
  return NULL;
 }

 *ret_index = index;
 qla_put_fw_resources(sp->qpair, &sp->iores);
 return sp;
}

srb_t *
qla2x00_get_sp_from_handle(scsi_qla_host_t *vha, const char *func,
      struct req_que *req, void *iocb)
{
 uint16_t index;
 srb_t *sp;

 sp = qla_get_sp_from_handle(vha, func, req, iocb, &index);
 if (sp)
  req->outstanding_cmds[index] = NULL;

 return sp;
}

static void
qla2x00_mbx_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
    struct mbx_entry *mbx)
{
 const char func[] = "MBX-IOCB";
 const char *type;
 fc_port_t *fcport;
 srb_t *sp;
 struct srb_iocb *lio;
 uint16_t *data;
 uint16_t status;

 sp = qla2x00_get_sp_from_handle(vha, func, req, mbx);
 if (!sp)
  return;

 lio = &sp->u.iocb_cmd;
 type = sp->name;
 fcport = sp->fcport;
 data = lio->u.logio.data;

 data[0] = MBS_COMMAND_ERROR;
 data[1] = lio->u.logio.flags & SRB_LOGIN_RETRIED ?
     QLA_LOGIO_LOGIN_RETRIED : 0;
 if (mbx->entry_status) {
  ql_dbg(ql_dbg_async, vha, 0x5043,
      "Async-%s error entry - hdl=%x portid=%02x%02x%02x "
      "entry-status=%x status=%x state-flag=%x "
      "status-flags=%x.\n", type, sp->handle,
      fcport->d_id.b.domain, fcport->d_id.b.area,
      fcport->d_id.b.al_pa, mbx->entry_status,
      le16_to_cpu(mbx->status), le16_to_cpu(mbx->state_flags),
      le16_to_cpu(mbx->status_flags));

  ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x5029,
      mbx, sizeof(*mbx));

  goto logio_done;
 }

 status = le16_to_cpu(mbx->status);
 if (status == 0x30 && sp->type == SRB_LOGIN_CMD &&
     le16_to_cpu(mbx->mb0) == MBS_COMMAND_COMPLETE)
  status = 0;
 if (!status && le16_to_cpu(mbx->mb0) == MBS_COMMAND_COMPLETE) {
  ql_dbg(ql_dbg_async, vha, 0x5045,
      "Async-%s complete - hdl=%x portid=%02x%02x%02x mbx1=%x.\n",
      type, sp->handle, fcport->d_id.b.domain,
      fcport->d_id.b.area, fcport->d_id.b.al_pa,
      le16_to_cpu(mbx->mb1));

  data[0] = MBS_COMMAND_COMPLETE;
  if (sp->type == SRB_LOGIN_CMD) {
   fcport->port_type = FCT_TARGET;
   if (le16_to_cpu(mbx->mb1) & BIT_0)
    fcport->port_type = FCT_INITIATOR;
   else if (le16_to_cpu(mbx->mb1) & BIT_1)
    fcport->flags |= FCF_FCP2_DEVICE;
  }
  goto logio_done;
 }

 data[0] = le16_to_cpu(mbx->mb0);
 switch (data[0]) {
 case MBS_PORT_ID_USED:
  data[1] = le16_to_cpu(mbx->mb1);
  break;
 case MBS_LOOP_ID_USED:
  break;
 default:
  data[0] = MBS_COMMAND_ERROR;
  break;
 }

 ql_log(ql_log_warn, vha, 0x5046,
     "Async-%s failed - hdl=%x portid=%02x%02x%02x status=%x "
     "mb0=%x mb1=%x mb2=%x mb6=%x mb7=%x.\n", type, sp->handle,
     fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa,
     status, le16_to_cpu(mbx->mb0), le16_to_cpu(mbx->mb1),
     le16_to_cpu(mbx->mb2), le16_to_cpu(mbx->mb6),
     le16_to_cpu(mbx->mb7));

logio_done:
 sp->done(sp, 0);
}

static void
qla24xx_mbx_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
    struct mbx_24xx_entry *pkt)
{
 const char func[] = "MBX-IOCB2";
 struct qla_hw_data *ha = vha->hw;
 srb_t *sp;
 struct srb_iocb *si;
 u16 sz, i;
 int res;

 sp = qla2x00_get_sp_from_handle(vha, func, req, pkt);
 if (!sp)
  return;

 if (sp->type == SRB_SCSI_CMD ||
     sp->type == SRB_NVME_CMD ||
     sp->type == SRB_TM_CMD) {
  ql_log(ql_log_warn, vha, 0x509d,
   "Inconsistent event entry type %d\n", sp->type);
  if (IS_P3P_TYPE(ha))
   set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
  else
   set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
  return;
 }

 si = &sp->u.iocb_cmd;
 sz = min(ARRAY_SIZE(pkt->mb), ARRAY_SIZE(sp->u.iocb_cmd.u.mbx.in_mb));

 for (i = 0; i < sz; i++)
  si->u.mbx.in_mb[i] = pkt->mb[i];

 res = (si->u.mbx.in_mb[0] & MBS_MASK);

 sp->done(sp, res);
}

static void
qla24xxx_nack_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
    struct nack_to_isp *pkt)
{
 const char func[] = "nack";
 srb_t *sp;
 int res = 0;

 sp = qla2x00_get_sp_from_handle(vha, func, req, pkt);
 if (!sp)
  return;

 if (pkt->u.isp2x.status != cpu_to_le16(NOTIFY_ACK_SUCCESS))
  res = QLA_FUNCTION_FAILED;

 sp->done(sp, res);
}

static void
qla2x00_ct_entry(scsi_qla_host_t *vha, struct req_que *req,
    sts_entry_t *pkt, int iocb_type)
{
 const char func[] = "CT_IOCB";
 const char *type;
 srb_t *sp;
 struct bsg_job *bsg_job;
 struct fc_bsg_reply *bsg_reply;
 uint16_t comp_status;
 int res = 0;

 sp = qla2x00_get_sp_from_handle(vha, func, req, pkt);
 if (!sp)
  return;

 switch (sp->type) {
 case SRB_CT_CMD:
     bsg_job = sp->u.bsg_job;
     bsg_reply = bsg_job->reply;

     type = "ct pass-through";

     comp_status = le16_to_cpu(pkt->comp_status);

     /*
     * return FC_CTELS_STATUS_OK and leave the decoding of the ELS/CT
     * fc payload  to the caller
     */

     bsg_reply->reply_data.ctels_reply.status = FC_CTELS_STATUS_OK;
     bsg_job->reply_len = sizeof(struct fc_bsg_reply);

     if (comp_status != CS_COMPLETE) {
      if (comp_status == CS_DATA_UNDERRUN) {
       res = DID_OK << 16;
       bsg_reply->reply_payload_rcv_len =
    le16_to_cpu(pkt->rsp_info_len);

       ql_log(ql_log_warn, vha, 0x5048,
    "CT pass-through-%s error comp_status=0x%x total_byte=0x%x.\n",
    type, comp_status,
    bsg_reply->reply_payload_rcv_len);
      } else {
       ql_log(ql_log_warn, vha, 0x5049,
    "CT pass-through-%s error comp_status=0x%x.\n",
    type, comp_status);
       res = DID_ERROR << 16;
       bsg_reply->reply_payload_rcv_len = 0;
      }
      ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x5035,
   pkt, sizeof(*pkt));
     } else {
      res = DID_OK << 16;
      bsg_reply->reply_payload_rcv_len =
   bsg_job->reply_payload.payload_len;
      bsg_job->reply_len = 0;
     }
     break;
 case SRB_CT_PTHRU_CMD:
     /*
     * borrowing sts_entry_24xx.comp_status.
     * same location as ct_entry_24xx.comp_status
     */

      res = qla2x00_chk_ms_status(vha, (ms_iocb_entry_t *)pkt,
   (struct ct_sns_rsp *)sp->u.iocb_cmd.u.ctarg.rsp,
   sp->name);
      break;
 }

 sp->done(sp, res);
}

static void
qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
    struct sts_entry_24xx *pkt, int iocb_type)
{
 struct els_sts_entry_24xx *ese = (struct els_sts_entry_24xx *)pkt;
 const char func[] = "ELS_CT_IOCB";
 const char *type;
 srb_t *sp;
 struct bsg_job *bsg_job;
 struct fc_bsg_reply *bsg_reply;
 uint16_t comp_status;
 uint32_t fw_status[3];
 int res, logit = 1;
 struct srb_iocb *els;
 uint n;
 scsi_qla_host_t *vha;
 struct els_sts_entry_24xx *e = (struct els_sts_entry_24xx *)pkt;

 sp = qla2x00_get_sp_from_handle(v, func, req, pkt);
 if (!sp)
  return;
 bsg_job = sp->u.bsg_job;
 vha = sp->vha;

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

--> maximum size reached

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

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

¤ 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.0.22Bemerkung:  ¤

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