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

Quelle  mpi3mr_fw.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Driver for Broadcom MPI3 Storage Controllers
 *
 * Copyright (C) 2017-2023 Broadcom Inc.
 *  (mailto: mpi3mr-linuxdrv.pdl@broadcom.com)
 *
 */


#include "mpi3mr.h"
#include <linux/io-64-nonatomic-lo-hi.h>

static int
mpi3mr_issue_reset(struct mpi3mr_ioc *mrioc, u16 reset_type, u16 reset_reason);
static int mpi3mr_setup_admin_qpair(struct mpi3mr_ioc *mrioc);
static void mpi3mr_process_factsdata(struct mpi3mr_ioc *mrioc,
 struct mpi3_ioc_facts_data *facts_data);
static void mpi3mr_pel_wait_complete(struct mpi3mr_ioc *mrioc,
 struct mpi3mr_drv_cmd *drv_cmd);
static int mpi3mr_check_op_admin_proc(struct mpi3mr_ioc *mrioc);
static int poll_queues;
module_param(poll_queues, int, 0444);
MODULE_PARM_DESC(poll_queues, "Number of queues for io_uring poll mode. (Range 1 - 126)");

#if defined(writeq) && defined(CONFIG_64BIT)
static inline void mpi3mr_writeq(__u64 b, void __iomem *addr,
 spinlock_t *write_queue_lock)
{
 writeq(b, addr);
}
#else
static inline void mpi3mr_writeq(__u64 b, void __iomem *addr,
 spinlock_t *write_queue_lock)
{
 __u64 data_out = b;
 unsigned long flags;

 spin_lock_irqsave(write_queue_lock, flags);
 writel((u32)(data_out), addr);
 writel((u32)(data_out >> 32), (addr + 4));
 spin_unlock_irqrestore(write_queue_lock, flags);
}
#endif

static inline bool
mpi3mr_check_req_qfull(struct op_req_qinfo *op_req_q)
{
 u16 pi, ci, max_entries;
 bool is_qfull = false;

 pi = op_req_q->pi;
 ci = READ_ONCE(op_req_q->ci);
 max_entries = op_req_q->num_requests;

 if ((ci == (pi + 1)) || ((!ci) && (pi == (max_entries - 1))))
  is_qfull = true;

 return is_qfull;
}

static void mpi3mr_sync_irqs(struct mpi3mr_ioc *mrioc)
{
 u16 i, max_vectors;

 max_vectors = mrioc->intr_info_count;

 for (i = 0; i < max_vectors; i++)
  synchronize_irq(pci_irq_vector(mrioc->pdev, i));
}

void mpi3mr_ioc_disable_intr(struct mpi3mr_ioc *mrioc)
{
 mrioc->intr_enabled = 0;
 mpi3mr_sync_irqs(mrioc);
}

void mpi3mr_ioc_enable_intr(struct mpi3mr_ioc *mrioc)
{
 mrioc->intr_enabled = 1;
}

static void mpi3mr_cleanup_isr(struct mpi3mr_ioc *mrioc)
{
 u16 i;

 mpi3mr_ioc_disable_intr(mrioc);

 if (!mrioc->intr_info)
  return;

 for (i = 0; i < mrioc->intr_info_count; i++)
  free_irq(pci_irq_vector(mrioc->pdev, i),
      (mrioc->intr_info + i));

 kfree(mrioc->intr_info);
 mrioc->intr_info = NULL;
 mrioc->intr_info_count = 0;
 mrioc->is_intr_info_set = false;
 pci_free_irq_vectors(mrioc->pdev);
}

void mpi3mr_add_sg_single(void *paddr, u8 flags, u32 length,
 dma_addr_t dma_addr)
{
 struct mpi3_sge_common *sgel = paddr;

 sgel->flags = flags;
 sgel->length = cpu_to_le32(length);
 sgel->address = cpu_to_le64(dma_addr);
}

void mpi3mr_build_zero_len_sge(void *paddr)
{
 u8 sgl_flags = MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST;

 mpi3mr_add_sg_single(paddr, sgl_flags, 0, -1);
}

void *mpi3mr_get_reply_virt_addr(struct mpi3mr_ioc *mrioc,
 dma_addr_t phys_addr)
{
 if (!phys_addr)
  return NULL;

 if ((phys_addr < mrioc->reply_buf_dma) ||
     (phys_addr > mrioc->reply_buf_dma_max_address))
  return NULL;

 return mrioc->reply_buf + (phys_addr - mrioc->reply_buf_dma);
}

void *mpi3mr_get_sensebuf_virt_addr(struct mpi3mr_ioc *mrioc,
 dma_addr_t phys_addr)
{
 if (!phys_addr)
  return NULL;

 return mrioc->sense_buf + (phys_addr - mrioc->sense_buf_dma);
}

static void mpi3mr_repost_reply_buf(struct mpi3mr_ioc *mrioc,
 u64 reply_dma)
{
 u32 old_idx = 0;
 unsigned long flags;

 spin_lock_irqsave(&mrioc->reply_free_queue_lock, flags);
 old_idx  =  mrioc->reply_free_queue_host_index;
 mrioc->reply_free_queue_host_index = (
     (mrioc->reply_free_queue_host_index ==
     (mrioc->reply_free_qsz - 1)) ? 0 :
     (mrioc->reply_free_queue_host_index + 1));
 mrioc->reply_free_q[old_idx] = cpu_to_le64(reply_dma);
 writel(mrioc->reply_free_queue_host_index,
     &mrioc->sysif_regs->reply_free_host_index);
 spin_unlock_irqrestore(&mrioc->reply_free_queue_lock, flags);
}

void mpi3mr_repost_sense_buf(struct mpi3mr_ioc *mrioc,
 u64 sense_buf_dma)
{
 u32 old_idx = 0;
 unsigned long flags;

 spin_lock_irqsave(&mrioc->sbq_lock, flags);
 old_idx  =  mrioc->sbq_host_index;
 mrioc->sbq_host_index = ((mrioc->sbq_host_index ==
     (mrioc->sense_buf_q_sz - 1)) ? 0 :
     (mrioc->sbq_host_index + 1));
 mrioc->sense_buf_q[old_idx] = cpu_to_le64(sense_buf_dma);
 writel(mrioc->sbq_host_index,
     &mrioc->sysif_regs->sense_buffer_free_host_index);
 spin_unlock_irqrestore(&mrioc->sbq_lock, flags);
}

static void mpi3mr_print_event_data(struct mpi3mr_ioc *mrioc,
 struct mpi3_event_notification_reply *event_reply)
{
 char *desc = NULL;
 u16 event;

 if (!(mrioc->logging_level & MPI3_DEBUG_EVENT))
  return;

 event = event_reply->event;

 switch (event) {
 case MPI3_EVENT_LOG_DATA:
  desc = "Log Data";
  break;
 case MPI3_EVENT_CHANGE:
  desc = "Event Change";
  break;
 case MPI3_EVENT_GPIO_INTERRUPT:
  desc = "GPIO Interrupt";
  break;
 case MPI3_EVENT_CABLE_MGMT:
  desc = "Cable Management";
  break;
 case MPI3_EVENT_ENERGY_PACK_CHANGE:
  desc = "Energy Pack Change";
  break;
 case MPI3_EVENT_DEVICE_ADDED:
 {
  struct mpi3_device_page0 *event_data =
      (struct mpi3_device_page0 *)event_reply->event_data;
  ioc_info(mrioc, "Device Added: dev=0x%04x Form=0x%x\n",
      event_data->dev_handle, event_data->device_form);
  return;
 }
 case MPI3_EVENT_DEVICE_INFO_CHANGED:
 {
  struct mpi3_device_page0 *event_data =
      (struct mpi3_device_page0 *)event_reply->event_data;
  ioc_info(mrioc, "Device Info Changed: dev=0x%04x Form=0x%x\n",
      event_data->dev_handle, event_data->device_form);
  return;
 }
 case MPI3_EVENT_DEVICE_STATUS_CHANGE:
 {
  struct mpi3_event_data_device_status_change *event_data =
      (struct mpi3_event_data_device_status_change *)event_reply->event_data;
  ioc_info(mrioc, "Device status Change: dev=0x%04x RC=0x%x\n",
      event_data->dev_handle, event_data->reason_code);
  return;
 }
 case MPI3_EVENT_SAS_DISCOVERY:
 {
  struct mpi3_event_data_sas_discovery *event_data =
      (struct mpi3_event_data_sas_discovery *)event_reply->event_data;
  ioc_info(mrioc, "SAS Discovery: (%s) status (0x%08x)\n",
      (event_data->reason_code == MPI3_EVENT_SAS_DISC_RC_STARTED) ?
      "start" : "stop",
      le32_to_cpu(event_data->discovery_status));
  return;
 }
 case MPI3_EVENT_SAS_BROADCAST_PRIMITIVE:
  desc = "SAS Broadcast Primitive";
  break;
 case MPI3_EVENT_SAS_NOTIFY_PRIMITIVE:
  desc = "SAS Notify Primitive";
  break;
 case MPI3_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE:
  desc = "SAS Init Device Status Change";
  break;
 case MPI3_EVENT_SAS_INIT_TABLE_OVERFLOW:
  desc = "SAS Init Table Overflow";
  break;
 case MPI3_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
  desc = "SAS Topology Change List";
  break;
 case MPI3_EVENT_ENCL_DEVICE_STATUS_CHANGE:
  desc = "Enclosure Device Status Change";
  break;
 case MPI3_EVENT_ENCL_DEVICE_ADDED:
  desc = "Enclosure Added";
  break;
 case MPI3_EVENT_HARD_RESET_RECEIVED:
  desc = "Hard Reset Received";
  break;
 case MPI3_EVENT_SAS_PHY_COUNTER:
  desc = "SAS PHY Counter";
  break;
 case MPI3_EVENT_SAS_DEVICE_DISCOVERY_ERROR:
  desc = "SAS Device Discovery Error";
  break;
 case MPI3_EVENT_PCIE_TOPOLOGY_CHANGE_LIST:
  desc = "PCIE Topology Change List";
  break;
 case MPI3_EVENT_PCIE_ENUMERATION:
 {
  struct mpi3_event_data_pcie_enumeration *event_data =
      (struct mpi3_event_data_pcie_enumeration *)event_reply->event_data;
  ioc_info(mrioc, "PCIE Enumeration: (%s)",
      (event_data->reason_code ==
      MPI3_EVENT_PCIE_ENUM_RC_STARTED) ? "start" : "stop");
  if (event_data->enumeration_status)
   ioc_info(mrioc, "enumeration_status(0x%08x)\n",
       le32_to_cpu(event_data->enumeration_status));
  return;
 }
 case MPI3_EVENT_PREPARE_FOR_RESET:
  desc = "Prepare For Reset";
  break;
 case MPI3_EVENT_DIAGNOSTIC_BUFFER_STATUS_CHANGE:
  desc = "Diagnostic Buffer Status Change";
  break;
 }

 if (!desc)
  return;

 ioc_info(mrioc, "%s\n", desc);
}

static void mpi3mr_handle_events(struct mpi3mr_ioc *mrioc,
 struct mpi3_default_reply *def_reply)
{
 struct mpi3_event_notification_reply *event_reply =
     (struct mpi3_event_notification_reply *)def_reply;

 mrioc->change_count = le16_to_cpu(event_reply->ioc_change_count);
 mpi3mr_print_event_data(mrioc, event_reply);
 mpi3mr_os_handle_events(mrioc, event_reply);
}

static struct mpi3mr_drv_cmd *
mpi3mr_get_drv_cmd(struct mpi3mr_ioc *mrioc, u16 host_tag,
 struct mpi3_default_reply *def_reply)
{
 u16 idx;

 switch (host_tag) {
 case MPI3MR_HOSTTAG_INITCMDS:
  return &mrioc->init_cmds;
 case MPI3MR_HOSTTAG_CFG_CMDS:
  return &mrioc->cfg_cmds;
 case MPI3MR_HOSTTAG_BSG_CMDS:
  return &mrioc->bsg_cmds;
 case MPI3MR_HOSTTAG_BLK_TMS:
  return &mrioc->host_tm_cmds;
 case MPI3MR_HOSTTAG_PEL_ABORT:
  return &mrioc->pel_abort_cmd;
 case MPI3MR_HOSTTAG_PEL_WAIT:
  return &mrioc->pel_cmds;
 case MPI3MR_HOSTTAG_TRANSPORT_CMDS:
  return &mrioc->transport_cmds;
 case MPI3MR_HOSTTAG_INVALID:
  if (def_reply && def_reply->function ==
      MPI3_FUNCTION_EVENT_NOTIFICATION)
   mpi3mr_handle_events(mrioc, def_reply);
  return NULL;
 default:
  break;
 }
 if (host_tag >= MPI3MR_HOSTTAG_DEVRMCMD_MIN &&
     host_tag <= MPI3MR_HOSTTAG_DEVRMCMD_MAX) {
  idx = host_tag - MPI3MR_HOSTTAG_DEVRMCMD_MIN;
  return &mrioc->dev_rmhs_cmds[idx];
 }

 if (host_tag >= MPI3MR_HOSTTAG_EVTACKCMD_MIN &&
     host_tag <= MPI3MR_HOSTTAG_EVTACKCMD_MAX) {
  idx = host_tag - MPI3MR_HOSTTAG_EVTACKCMD_MIN;
  return &mrioc->evtack_cmds[idx];
 }

 return NULL;
}

static void mpi3mr_process_admin_reply_desc(struct mpi3mr_ioc *mrioc,
 struct mpi3_default_reply_descriptor *reply_desc, u64 *reply_dma)
{
 u16 reply_desc_type, host_tag = 0;
 u16 ioc_status = MPI3_IOCSTATUS_SUCCESS;
 u16 masked_ioc_status = MPI3_IOCSTATUS_SUCCESS;
 u32 ioc_loginfo = 0, sense_count = 0;
 struct mpi3_status_reply_descriptor *status_desc;
 struct mpi3_address_reply_descriptor *addr_desc;
 struct mpi3_success_reply_descriptor *success_desc;
 struct mpi3_default_reply *def_reply = NULL;
 struct mpi3mr_drv_cmd *cmdptr = NULL;
 struct mpi3_scsi_io_reply *scsi_reply;
 struct scsi_sense_hdr sshdr;
 u8 *sense_buf = NULL;

 *reply_dma = 0;
 reply_desc_type = le16_to_cpu(reply_desc->reply_flags) &
     MPI3_REPLY_DESCRIPT_FLAGS_TYPE_MASK;
 switch (reply_desc_type) {
 case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_STATUS:
  status_desc = (struct mpi3_status_reply_descriptor *)reply_desc;
  host_tag = le16_to_cpu(status_desc->host_tag);
  ioc_status = le16_to_cpu(status_desc->ioc_status);
  if (ioc_status &
      MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_LOGINFOAVAIL)
   ioc_loginfo = le32_to_cpu(status_desc->ioc_log_info);
  masked_ioc_status = ioc_status & MPI3_IOCSTATUS_STATUS_MASK;
  mpi3mr_reply_trigger(mrioc, masked_ioc_status, ioc_loginfo);
  break;
 case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_ADDRESS_REPLY:
  addr_desc = (struct mpi3_address_reply_descriptor *)reply_desc;
  *reply_dma = le64_to_cpu(addr_desc->reply_frame_address);
  def_reply = mpi3mr_get_reply_virt_addr(mrioc, *reply_dma);
  if (!def_reply)
   goto out;
  host_tag = le16_to_cpu(def_reply->host_tag);
  ioc_status = le16_to_cpu(def_reply->ioc_status);
  if (ioc_status &
      MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_LOGINFOAVAIL)
   ioc_loginfo = le32_to_cpu(def_reply->ioc_log_info);
  masked_ioc_status = ioc_status & MPI3_IOCSTATUS_STATUS_MASK;
  if (def_reply->function == MPI3_FUNCTION_SCSI_IO) {
   scsi_reply = (struct mpi3_scsi_io_reply *)def_reply;
   sense_buf = mpi3mr_get_sensebuf_virt_addr(mrioc,
       le64_to_cpu(scsi_reply->sense_data_buffer_address));
   sense_count = le32_to_cpu(scsi_reply->sense_count);
   if (sense_buf) {
    scsi_normalize_sense(sense_buf, sense_count,
        &sshdr);
    mpi3mr_scsisense_trigger(mrioc, sshdr.sense_key,
        sshdr.asc, sshdr.ascq);
   }
  }
  mpi3mr_reply_trigger(mrioc, masked_ioc_status, ioc_loginfo);
  break;
 case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_SUCCESS:
  success_desc = (struct mpi3_success_reply_descriptor *)reply_desc;
  host_tag = le16_to_cpu(success_desc->host_tag);
  break;
 default:
  break;
 }

 cmdptr = mpi3mr_get_drv_cmd(mrioc, host_tag, def_reply);
 if (cmdptr) {
  if (cmdptr->state & MPI3MR_CMD_PENDING) {
   cmdptr->state |= MPI3MR_CMD_COMPLETE;
   cmdptr->ioc_loginfo = ioc_loginfo;
   if (host_tag == MPI3MR_HOSTTAG_BSG_CMDS)
    cmdptr->ioc_status = ioc_status;
   else
    cmdptr->ioc_status = masked_ioc_status;
   cmdptr->state &= ~MPI3MR_CMD_PENDING;
   if (def_reply) {
    cmdptr->state |= MPI3MR_CMD_REPLY_VALID;
    memcpy((u8 *)cmdptr->reply, (u8 *)def_reply,
        mrioc->reply_sz);
   }
   if (sense_buf && cmdptr->sensebuf) {
    cmdptr->is_sense = 1;
    memcpy(cmdptr->sensebuf, sense_buf,
           MPI3MR_SENSE_BUF_SZ);
   }
   if (cmdptr->is_waiting) {
    cmdptr->is_waiting = 0;
    complete(&cmdptr->done);
   } else if (cmdptr->callback)
    cmdptr->callback(mrioc, cmdptr);
  }
 }
out:
 if (sense_buf)
  mpi3mr_repost_sense_buf(mrioc,
      le64_to_cpu(scsi_reply->sense_data_buffer_address));
}

int mpi3mr_process_admin_reply_q(struct mpi3mr_ioc *mrioc)
{
 u32 exp_phase = mrioc->admin_reply_ephase;
 u32 admin_reply_ci = mrioc->admin_reply_ci;
 u32 num_admin_replies = 0;
 u64 reply_dma = 0;
 u16 threshold_comps = 0;
 struct mpi3_default_reply_descriptor *reply_desc;

 if (!atomic_add_unless(&mrioc->admin_reply_q_in_use, 1, 1)) {
  atomic_inc(&mrioc->admin_pend_isr);
  return 0;
 }

 atomic_set(&mrioc->admin_pend_isr, 0);
 reply_desc = (struct mpi3_default_reply_descriptor *)mrioc->admin_reply_base +
     admin_reply_ci;

 if ((le16_to_cpu(reply_desc->reply_flags) &
     MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase) {
  atomic_dec(&mrioc->admin_reply_q_in_use);
  return 0;
 }

 do {
  if (mrioc->unrecoverable || mrioc->io_admin_reset_sync)
   break;

  mrioc->admin_req_ci = le16_to_cpu(reply_desc->request_queue_ci);
  mpi3mr_process_admin_reply_desc(mrioc, reply_desc, &reply_dma);
  if (reply_dma)
   mpi3mr_repost_reply_buf(mrioc, reply_dma);
  num_admin_replies++;
  threshold_comps++;
  if (++admin_reply_ci == mrioc->num_admin_replies) {
   admin_reply_ci = 0;
   exp_phase ^= 1;
  }
  reply_desc =
      (struct mpi3_default_reply_descriptor *)mrioc->admin_reply_base +
      admin_reply_ci;
  if ((le16_to_cpu(reply_desc->reply_flags) &
      MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase)
   break;
  if (threshold_comps == MPI3MR_THRESHOLD_REPLY_COUNT) {
   writel(admin_reply_ci,
       &mrioc->sysif_regs->admin_reply_queue_ci);
   threshold_comps = 0;
  }
 } while (1);

 writel(admin_reply_ci, &mrioc->sysif_regs->admin_reply_queue_ci);
 mrioc->admin_reply_ci = admin_reply_ci;
 mrioc->admin_reply_ephase = exp_phase;
 atomic_dec(&mrioc->admin_reply_q_in_use);

 return num_admin_replies;
}

/**
 * mpi3mr_get_reply_desc - get reply descriptor frame corresponding to
 * queue's consumer index from operational reply descriptor queue.
 * @op_reply_q: op_reply_qinfo object
 * @reply_ci: operational reply descriptor's queue consumer index
 *
 * Returns: reply descriptor frame address
 */

static inline struct mpi3_default_reply_descriptor *
mpi3mr_get_reply_desc(struct op_reply_qinfo *op_reply_q, u32 reply_ci)
{
 void *segment_base_addr;
 struct segments *segments = op_reply_q->q_segments;
 struct mpi3_default_reply_descriptor *reply_desc = NULL;

 segment_base_addr =
     segments[reply_ci / op_reply_q->segment_qd].segment;
 reply_desc = (struct mpi3_default_reply_descriptor *)segment_base_addr +
     (reply_ci % op_reply_q->segment_qd);
 return reply_desc;
}

/**
 * mpi3mr_process_op_reply_q - Operational reply queue handler
 * @mrioc: Adapter instance reference
 * @op_reply_q: Operational reply queue info
 *
 * Checks the specific operational reply queue and drains the
 * reply queue entries until the queue is empty and process the
 * individual reply descriptors.
 *
 * Return: 0 if queue is already processed,or number of reply
 *     descriptors processed.
 */

int mpi3mr_process_op_reply_q(struct mpi3mr_ioc *mrioc,
 struct op_reply_qinfo *op_reply_q)
{
 struct op_req_qinfo *op_req_q;
 u32 exp_phase;
 u32 reply_ci;
 u32 num_op_reply = 0;
 u64 reply_dma = 0;
 struct mpi3_default_reply_descriptor *reply_desc;
 u16 req_q_idx = 0, reply_qidx, threshold_comps = 0;

 reply_qidx = op_reply_q->qid - 1;

 if (!atomic_add_unless(&op_reply_q->in_use, 1, 1))
  return 0;

 exp_phase = op_reply_q->ephase;
 reply_ci = op_reply_q->ci;

 reply_desc = mpi3mr_get_reply_desc(op_reply_q, reply_ci);
 if ((le16_to_cpu(reply_desc->reply_flags) &
     MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase) {
  atomic_dec(&op_reply_q->in_use);
  return 0;
 }

 do {
  if (mrioc->unrecoverable || mrioc->io_admin_reset_sync)
   break;

  req_q_idx = le16_to_cpu(reply_desc->request_queue_id) - 1;
  op_req_q = &mrioc->req_qinfo[req_q_idx];

  WRITE_ONCE(op_req_q->ci, le16_to_cpu(reply_desc->request_queue_ci));
  mpi3mr_process_op_reply_desc(mrioc, reply_desc, &reply_dma,
      reply_qidx);

  if (reply_dma)
   mpi3mr_repost_reply_buf(mrioc, reply_dma);
  num_op_reply++;
  threshold_comps++;

  if (++reply_ci == op_reply_q->num_replies) {
   reply_ci = 0;
   exp_phase ^= 1;
  }

  reply_desc = mpi3mr_get_reply_desc(op_reply_q, reply_ci);

  if ((le16_to_cpu(reply_desc->reply_flags) &
      MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase)
   break;
#ifndef CONFIG_PREEMPT_RT
  /*
 * Exit completion loop to avoid CPU lockup
 * Ensure remaining completion happens from threaded ISR.
 */

  if (num_op_reply > mrioc->max_host_ios) {
   op_reply_q->enable_irq_poll = true;
   break;
  }
#endif
  if (threshold_comps == MPI3MR_THRESHOLD_REPLY_COUNT) {
   writel(reply_ci,
       &mrioc->sysif_regs->oper_queue_indexes[reply_qidx].consumer_index);
   atomic_sub(threshold_comps, &op_reply_q->pend_ios);
   threshold_comps = 0;
  }
 } while (1);

 writel(reply_ci,
     &mrioc->sysif_regs->oper_queue_indexes[reply_qidx].consumer_index);
 op_reply_q->ci = reply_ci;
 op_reply_q->ephase = exp_phase;
 atomic_sub(threshold_comps, &op_reply_q->pend_ios);
 atomic_dec(&op_reply_q->in_use);
 return num_op_reply;
}

/**
 * mpi3mr_blk_mq_poll - Operational reply queue handler
 * @shost: SCSI Host reference
 * @queue_num: Request queue number (w.r.t OS it is hardware context number)
 *
 * Checks the specific operational reply queue and drains the
 * reply queue entries until the queue is empty and process the
 * individual reply descriptors.
 *
 * Return: 0 if queue is already processed,or number of reply
 *     descriptors processed.
 */

int mpi3mr_blk_mq_poll(struct Scsi_Host *shost, unsigned int queue_num)
{
 int num_entries = 0;
 struct mpi3mr_ioc *mrioc;

 mrioc = (struct mpi3mr_ioc *)shost->hostdata;

 if ((mrioc->reset_in_progress || mrioc->prepare_for_reset ||
     mrioc->unrecoverable || mrioc->pci_err_recovery))
  return 0;

 num_entries = mpi3mr_process_op_reply_q(mrioc,
   &mrioc->op_reply_qinfo[queue_num]);

 return num_entries;
}

static irqreturn_t mpi3mr_isr_primary(int irq, void *privdata)
{
 struct mpi3mr_intr_info *intr_info = privdata;
 struct mpi3mr_ioc *mrioc;
 u16 midx;
 u32 num_admin_replies = 0, num_op_reply = 0;

 if (!intr_info)
  return IRQ_NONE;

 mrioc = intr_info->mrioc;

 if (!mrioc->intr_enabled)
  return IRQ_NONE;

 midx = intr_info->msix_index;

 if (!midx)
  num_admin_replies = mpi3mr_process_admin_reply_q(mrioc);
 if (intr_info->op_reply_q)
  num_op_reply = mpi3mr_process_op_reply_q(mrioc,
      intr_info->op_reply_q);

 if (num_admin_replies || num_op_reply)
  return IRQ_HANDLED;
 else
  return IRQ_NONE;
}

#ifndef CONFIG_PREEMPT_RT

static irqreturn_t mpi3mr_isr(int irq, void *privdata)
{
 struct mpi3mr_intr_info *intr_info = privdata;
 int ret;

 if (!intr_info)
  return IRQ_NONE;

 /* Call primary ISR routine */
 ret = mpi3mr_isr_primary(irq, privdata);

 /*
 * If more IOs are expected, schedule IRQ polling thread.
 * Otherwise exit from ISR.
 */

 if (!intr_info->op_reply_q)
  return ret;

 if (!intr_info->op_reply_q->enable_irq_poll ||
     !atomic_read(&intr_info->op_reply_q->pend_ios))
  return ret;

 disable_irq_nosync(intr_info->os_irq);

 return IRQ_WAKE_THREAD;
}

/**
 * mpi3mr_isr_poll - Reply queue polling routine
 * @irq: IRQ
 * @privdata: Interrupt info
 *
 * poll for pending I/O completions in a loop until pending I/Os
 * present or controller queue depth I/Os are processed.
 *
 * Return: IRQ_NONE or IRQ_HANDLED
 */

static irqreturn_t mpi3mr_isr_poll(int irq, void *privdata)
{
 struct mpi3mr_intr_info *intr_info = privdata;
 struct mpi3mr_ioc *mrioc;
 u16 midx;
 u32 num_op_reply = 0;

 if (!intr_info || !intr_info->op_reply_q)
  return IRQ_NONE;

 mrioc = intr_info->mrioc;
 midx = intr_info->msix_index;

 /* Poll for pending IOs completions */
 do {
  if (!mrioc->intr_enabled || mrioc->unrecoverable)
   break;

  if (!midx)
   mpi3mr_process_admin_reply_q(mrioc);
  if (intr_info->op_reply_q)
   num_op_reply +=
       mpi3mr_process_op_reply_q(mrioc,
    intr_info->op_reply_q);

  usleep_range(MPI3MR_IRQ_POLL_SLEEP, MPI3MR_IRQ_POLL_SLEEP + 1);

 } while (atomic_read(&intr_info->op_reply_q->pend_ios) &&
     (num_op_reply < mrioc->max_host_ios));

 intr_info->op_reply_q->enable_irq_poll = false;
 enable_irq(intr_info->os_irq);

 return IRQ_HANDLED;
}

#endif

/**
 * mpi3mr_request_irq - Request IRQ and register ISR
 * @mrioc: Adapter instance reference
 * @index: IRQ vector index
 *
 * Request threaded ISR with primary ISR and secondary
 *
 * Return: 0 on success and non zero on failures.
 */

static inline int mpi3mr_request_irq(struct mpi3mr_ioc *mrioc, u16 index)
{
 struct pci_dev *pdev = mrioc->pdev;
 struct mpi3mr_intr_info *intr_info = mrioc->intr_info + index;
 int retval = 0;

 intr_info->mrioc = mrioc;
 intr_info->msix_index = index;
 intr_info->op_reply_q = NULL;

 snprintf(intr_info->name, MPI3MR_NAME_LENGTH, "%s%d-msix%d",
     mrioc->driver_name, mrioc->id, index);

#ifndef CONFIG_PREEMPT_RT
 retval = request_threaded_irq(pci_irq_vector(pdev, index), mpi3mr_isr,
     mpi3mr_isr_poll, IRQF_SHARED, intr_info->name, intr_info);
#else
 retval = request_threaded_irq(pci_irq_vector(pdev, index), mpi3mr_isr_primary,
     NULL, IRQF_SHARED, intr_info->name, intr_info);
#endif
 if (retval) {
  ioc_err(mrioc, "%s: Unable to allocate interrupt %d!\n",
      intr_info->name, pci_irq_vector(pdev, index));
  return retval;
 }

 intr_info->os_irq = pci_irq_vector(pdev, index);
 return retval;
}

static void mpi3mr_calc_poll_queues(struct mpi3mr_ioc *mrioc, u16 max_vectors)
{
 if (!mrioc->requested_poll_qcount)
  return;

 /* Reserved for Admin and Default Queue */
 if (max_vectors > 2 &&
  (mrioc->requested_poll_qcount < max_vectors - 2)) {
  ioc_info(mrioc,
      "enabled polled queues (%d) msix (%d)\n",
      mrioc->requested_poll_qcount, max_vectors);
 } else {
  ioc_info(mrioc,
      "disabled polled queues (%d) msix (%d) because of no resources for default queue\n",
      mrioc->requested_poll_qcount, max_vectors);
  mrioc->requested_poll_qcount = 0;
 }
}

/**
 * mpi3mr_setup_isr - Setup ISR for the controller
 * @mrioc: Adapter instance reference
 * @setup_one: Request one IRQ or more
 *
 * Allocate IRQ vectors and call mpi3mr_request_irq to setup ISR
 *
 * Return: 0 on success and non zero on failures.
 */

static int mpi3mr_setup_isr(struct mpi3mr_ioc *mrioc, u8 setup_one)
{
 unsigned int irq_flags = PCI_IRQ_MSIX;
 int max_vectors, min_vec;
 int retval;
 int i;
 struct irq_affinity desc = { .pre_vectors =  1, .post_vectors = 1 };

 if (mrioc->is_intr_info_set)
  return 0;

 mpi3mr_cleanup_isr(mrioc);

 if (setup_one || reset_devices) {
  max_vectors = 1;
  retval = pci_alloc_irq_vectors(mrioc->pdev,
      1, max_vectors, irq_flags);
  if (retval < 0) {
   ioc_err(mrioc, "cannot allocate irq vectors, ret %d\n",
       retval);
   goto out_failed;
  }
 } else {
  max_vectors =
      min_t(int, mrioc->cpu_count + 1 +
   mrioc->requested_poll_qcount, mrioc->msix_count);

  mpi3mr_calc_poll_queues(mrioc, max_vectors);

  ioc_info(mrioc,
      "MSI-X vectors supported: %d, no of cores: %d,",
      mrioc->msix_count, mrioc->cpu_count);
  ioc_info(mrioc,
      "MSI-x vectors requested: %d poll_queues %d\n",
      max_vectors, mrioc->requested_poll_qcount);

  desc.post_vectors = mrioc->requested_poll_qcount;
  min_vec = desc.pre_vectors + desc.post_vectors;
  irq_flags |= PCI_IRQ_AFFINITY | PCI_IRQ_ALL_TYPES;

  retval = pci_alloc_irq_vectors_affinity(mrioc->pdev,
   min_vec, max_vectors, irq_flags, &desc);

  if (retval < 0) {
   ioc_err(mrioc, "cannot allocate irq vectors, ret %d\n",
       retval);
   goto out_failed;
  }


  /*
 * If only one MSI-x is allocated, then MSI-x 0 will be shared
 * between Admin queue and operational queue
 */

  if (retval == min_vec)
   mrioc->op_reply_q_offset = 0;
  else if (retval != (max_vectors)) {
   ioc_info(mrioc,
       "allocated vectors (%d) are less than configured (%d)\n",
       retval, max_vectors);
  }

  max_vectors = retval;
  mrioc->op_reply_q_offset = (max_vectors > 1) ? 1 : 0;

  mpi3mr_calc_poll_queues(mrioc, max_vectors);

 }

 mrioc->intr_info = kzalloc(sizeof(struct mpi3mr_intr_info) * max_vectors,
     GFP_KERNEL);
 if (!mrioc->intr_info) {
  retval = -ENOMEM;
  pci_free_irq_vectors(mrioc->pdev);
  goto out_failed;
 }
 for (i = 0; i < max_vectors; i++) {
  retval = mpi3mr_request_irq(mrioc, i);
  if (retval) {
   mrioc->intr_info_count = i;
   goto out_failed;
  }
 }
 if (reset_devices || !setup_one)
  mrioc->is_intr_info_set = true;
 mrioc->intr_info_count = max_vectors;
 mpi3mr_ioc_enable_intr(mrioc);
 return 0;

out_failed:
 mpi3mr_cleanup_isr(mrioc);

 return retval;
}

static const struct {
 enum mpi3mr_iocstate value;
 char *name;
} mrioc_states[] = {
 { MRIOC_STATE_READY, "ready" },
 { MRIOC_STATE_FAULT, "fault" },
 { MRIOC_STATE_RESET, "reset" },
 { MRIOC_STATE_BECOMING_READY, "becoming ready" },
 { MRIOC_STATE_RESET_REQUESTED, "reset requested" },
 { MRIOC_STATE_UNRECOVERABLE, "unrecoverable error" },
};

static const char *mpi3mr_iocstate_name(enum mpi3mr_iocstate mrioc_state)
{
 int i;
 char *name = NULL;

 for (i = 0; i < ARRAY_SIZE(mrioc_states); i++) {
  if (mrioc_states[i].value == mrioc_state) {
   name = mrioc_states[i].name;
   break;
  }
 }
 return name;
}

/* Reset reason to name mapper structure*/
static const struct {
 enum mpi3mr_reset_reason value;
 char *name;
} mpi3mr_reset_reason_codes[] = {
 { MPI3MR_RESET_FROM_BRINGUP, "timeout in bringup" },
 { MPI3MR_RESET_FROM_FAULT_WATCH, "fault" },
 { MPI3MR_RESET_FROM_APP, "application invocation" },
 { MPI3MR_RESET_FROM_EH_HOS, "error handling" },
 { MPI3MR_RESET_FROM_TM_TIMEOUT, "TM timeout" },
 { MPI3MR_RESET_FROM_APP_TIMEOUT, "application command timeout" },
 { MPI3MR_RESET_FROM_MUR_FAILURE, "MUR failure" },
 { MPI3MR_RESET_FROM_CTLR_CLEANUP, "timeout in controller cleanup" },
 { MPI3MR_RESET_FROM_CIACTIV_FAULT, "component image activation fault" },
 { MPI3MR_RESET_FROM_PE_TIMEOUT, "port enable timeout" },
 { MPI3MR_RESET_FROM_TSU_TIMEOUT, "time stamp update timeout" },
 { MPI3MR_RESET_FROM_DELREQQ_TIMEOUT, "delete request queue timeout" },
 { MPI3MR_RESET_FROM_DELREPQ_TIMEOUT, "delete reply queue timeout" },
 {
  MPI3MR_RESET_FROM_CREATEREPQ_TIMEOUT,
  "create request queue timeout"
 },
 {
  MPI3MR_RESET_FROM_CREATEREQQ_TIMEOUT,
  "create reply queue timeout"
 },
 { MPI3MR_RESET_FROM_IOCFACTS_TIMEOUT, "IOC facts timeout" },
 { MPI3MR_RESET_FROM_IOCINIT_TIMEOUT, "IOC init timeout" },
 { MPI3MR_RESET_FROM_EVTNOTIFY_TIMEOUT, "event notify timeout" },
 { MPI3MR_RESET_FROM_EVTACK_TIMEOUT, "event acknowledgment timeout" },
 {
  MPI3MR_RESET_FROM_CIACTVRST_TIMER,
  "component image activation timeout"
 },
 {
  MPI3MR_RESET_FROM_GETPKGVER_TIMEOUT,
  "get package version timeout"
 },
 { MPI3MR_RESET_FROM_SYSFS, "sysfs invocation" },
 { MPI3MR_RESET_FROM_SYSFS_TIMEOUT, "sysfs TM timeout" },
 {
  MPI3MR_RESET_FROM_DIAG_BUFFER_POST_TIMEOUT,
  "diagnostic buffer post timeout"
 },
 {
  MPI3MR_RESET_FROM_DIAG_BUFFER_RELEASE_TIMEOUT,
  "diagnostic buffer release timeout"
 },
 { MPI3MR_RESET_FROM_FIRMWARE, "firmware asynchronous reset" },
 { MPI3MR_RESET_FROM_CFG_REQ_TIMEOUT, "configuration request timeout"},
 { MPI3MR_RESET_FROM_SAS_TRANSPORT_TIMEOUT, "timeout of a SAS transport layer request" },
};

/**
 * mpi3mr_reset_rc_name - get reset reason code name
 * @reason_code: reset reason code value
 *
 * Map reset reason to an NULL terminated ASCII string
 *
 * Return: name corresponding to reset reason value or NULL.
 */

static const char *mpi3mr_reset_rc_name(enum mpi3mr_reset_reason reason_code)
{
 int i;
 char *name = NULL;

 for (i = 0; i < ARRAY_SIZE(mpi3mr_reset_reason_codes); i++) {
  if (mpi3mr_reset_reason_codes[i].value == reason_code) {
   name = mpi3mr_reset_reason_codes[i].name;
   break;
  }
 }
 return name;
}

/* Reset type to name mapper structure*/
static const struct {
 u16 reset_type;
 char *name;
} mpi3mr_reset_types[] = {
 { MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET, "soft" },
 { MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT, "diag fault" },
};

/**
 * mpi3mr_reset_type_name - get reset type name
 * @reset_type: reset type value
 *
 * Map reset type to an NULL terminated ASCII string
 *
 * Return: name corresponding to reset type value or NULL.
 */

static const char *mpi3mr_reset_type_name(u16 reset_type)
{
 int i;
 char *name = NULL;

 for (i = 0; i < ARRAY_SIZE(mpi3mr_reset_types); i++) {
  if (mpi3mr_reset_types[i].reset_type == reset_type) {
   name = mpi3mr_reset_types[i].name;
   break;
  }
 }
 return name;
}

/**
 * mpi3mr_is_fault_recoverable - Read fault code and decide
 * whether the controller can be recoverable
 * @mrioc: Adapter instance reference
 * Return: true if fault is recoverable, false otherwise.
 */

static inline bool mpi3mr_is_fault_recoverable(struct mpi3mr_ioc *mrioc)
{
 u32 fault;

 fault = (readl(&mrioc->sysif_regs->fault) &
        MPI3_SYSIF_FAULT_CODE_MASK);

 switch (fault) {
 case MPI3_SYSIF_FAULT_CODE_COMPLETE_RESET_NEEDED:
 case MPI3_SYSIF_FAULT_CODE_POWER_CYCLE_REQUIRED:
  ioc_warn(mrioc,
      "controller requires system power cycle, marking controller as unrecoverable\n");
  return false;
 case MPI3_SYSIF_FAULT_CODE_INSUFFICIENT_PCI_SLOT_POWER:
  ioc_warn(mrioc,
      "controller faulted due to insufficient power,\n"
      " try by connecting it to a different slot\n");
  return false;
 default:
  break;
 }
 return true;
}

/**
 * mpi3mr_print_fault_info - Display fault information
 * @mrioc: Adapter instance reference
 *
 * Display the controller fault information if there is a
 * controller fault.
 *
 * Return: Nothing.
 */

void mpi3mr_print_fault_info(struct mpi3mr_ioc *mrioc)
{
 u32 ioc_status, code, code1, code2, code3;

 ioc_status = readl(&mrioc->sysif_regs->ioc_status);

 if (ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT) {
  code = readl(&mrioc->sysif_regs->fault);
  code1 = readl(&mrioc->sysif_regs->fault_info[0]);
  code2 = readl(&mrioc->sysif_regs->fault_info[1]);
  code3 = readl(&mrioc->sysif_regs->fault_info[2]);

  ioc_info(mrioc,
      "fault code(0x%08X): Additional code: (0x%08X:0x%08X:0x%08X)\n",
      code, code1, code2, code3);
 }
}

/**
 * mpi3mr_get_iocstate - Get IOC State
 * @mrioc: Adapter instance reference
 *
 * Return a proper IOC state enum based on the IOC status and
 * IOC configuration and unrcoverable state of the controller.
 *
 * Return: Current IOC state.
 */

enum mpi3mr_iocstate mpi3mr_get_iocstate(struct mpi3mr_ioc *mrioc)
{
 u32 ioc_status, ioc_config;
 u8 ready, enabled;

 ioc_status = readl(&mrioc->sysif_regs->ioc_status);
 ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);

 if (mrioc->unrecoverable)
  return MRIOC_STATE_UNRECOVERABLE;
 if (ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT)
  return MRIOC_STATE_FAULT;

 ready = (ioc_status & MPI3_SYSIF_IOC_STATUS_READY);
 enabled = (ioc_config & MPI3_SYSIF_IOC_CONFIG_ENABLE_IOC);

 if (ready && enabled)
  return MRIOC_STATE_READY;
 if ((!ready) && (!enabled))
  return MRIOC_STATE_RESET;
 if ((!ready) && (enabled))
  return MRIOC_STATE_BECOMING_READY;

 return MRIOC_STATE_RESET_REQUESTED;
}

/**
 * mpi3mr_free_ioctl_dma_memory - free memory for ioctl dma
 * @mrioc: Adapter instance reference
 *
 * Free the DMA memory allocated for IOCTL handling purpose.
 *
 * Return: None
 */

static void mpi3mr_free_ioctl_dma_memory(struct mpi3mr_ioc *mrioc)
{
 struct dma_memory_desc *mem_desc;
 u16 i;

 if (!mrioc->ioctl_dma_pool)
  return;

 for (i = 0; i < MPI3MR_NUM_IOCTL_SGE; i++) {
  mem_desc = &mrioc->ioctl_sge[i];
  if (mem_desc->addr) {
   dma_pool_free(mrioc->ioctl_dma_pool,
          mem_desc->addr,
          mem_desc->dma_addr);
   mem_desc->addr = NULL;
  }
 }
 dma_pool_destroy(mrioc->ioctl_dma_pool);
 mrioc->ioctl_dma_pool = NULL;
 mem_desc = &mrioc->ioctl_chain_sge;

 if (mem_desc->addr) {
  dma_free_coherent(&mrioc->pdev->dev, mem_desc->size,
      mem_desc->addr, mem_desc->dma_addr);
  mem_desc->addr = NULL;
 }
 mem_desc = &mrioc->ioctl_resp_sge;
 if (mem_desc->addr) {
  dma_free_coherent(&mrioc->pdev->dev, mem_desc->size,
      mem_desc->addr, mem_desc->dma_addr);
  mem_desc->addr = NULL;
 }

 mrioc->ioctl_sges_allocated = false;
}

/**
 * mpi3mr_alloc_ioctl_dma_memory - Alloc memory for ioctl dma
 * @mrioc: Adapter instance reference
 *
 * This function allocates dmaable memory required to handle the
 * application issued MPI3 IOCTL requests.
 *
 * Return: None
 */

static void mpi3mr_alloc_ioctl_dma_memory(struct mpi3mr_ioc *mrioc)

{
 struct dma_memory_desc *mem_desc;
 u16 i;

 mrioc->ioctl_dma_pool = dma_pool_create("ioctl dma pool",
      &mrioc->pdev->dev,
      MPI3MR_IOCTL_SGE_SIZE,
      MPI3MR_PAGE_SIZE_4K, 0);

 if (!mrioc->ioctl_dma_pool) {
  ioc_err(mrioc, "ioctl_dma_pool: dma_pool_create failed\n");
  goto out_failed;
 }

 for (i = 0; i < MPI3MR_NUM_IOCTL_SGE; i++) {
  mem_desc = &mrioc->ioctl_sge[i];
  mem_desc->size = MPI3MR_IOCTL_SGE_SIZE;
  mem_desc->addr = dma_pool_zalloc(mrioc->ioctl_dma_pool,
       GFP_KERNEL,
       &mem_desc->dma_addr);
  if (!mem_desc->addr)
   goto out_failed;
 }

 mem_desc = &mrioc->ioctl_chain_sge;
 mem_desc->size = MPI3MR_PAGE_SIZE_4K;
 mem_desc->addr = dma_alloc_coherent(&mrioc->pdev->dev,
         mem_desc->size,
         &mem_desc->dma_addr,
         GFP_KERNEL);
 if (!mem_desc->addr)
  goto out_failed;

 mem_desc = &mrioc->ioctl_resp_sge;
 mem_desc->size = MPI3MR_PAGE_SIZE_4K;
 mem_desc->addr = dma_alloc_coherent(&mrioc->pdev->dev,
         mem_desc->size,
         &mem_desc->dma_addr,
         GFP_KERNEL);
 if (!mem_desc->addr)
  goto out_failed;

 mrioc->ioctl_sges_allocated = true;

 return;
out_failed:
 ioc_warn(mrioc, "cannot allocate DMA memory for the mpt commands\n"
   "from the applications, application interface for MPT command is disabled\n");
 mpi3mr_free_ioctl_dma_memory(mrioc);
}

/**
 * mpi3mr_clear_reset_history - clear reset history
 * @mrioc: Adapter instance reference
 *
 * Write the reset history bit in IOC status to clear the bit,
 * if it is already set.
 *
 * Return: Nothing.
 */

static inline void mpi3mr_clear_reset_history(struct mpi3mr_ioc *mrioc)
{
 u32 ioc_status;

 ioc_status = readl(&mrioc->sysif_regs->ioc_status);
 if (ioc_status & MPI3_SYSIF_IOC_STATUS_RESET_HISTORY)
  writel(ioc_status, &mrioc->sysif_regs->ioc_status);
}

/**
 * mpi3mr_issue_and_process_mur - Message unit Reset handler
 * @mrioc: Adapter instance reference
 * @reset_reason: Reset reason code
 *
 * Issue Message unit Reset to the controller and wait for it to
 * be complete.
 *
 * Return: 0 on success, -1 on failure.
 */

static int mpi3mr_issue_and_process_mur(struct mpi3mr_ioc *mrioc,
 u32 reset_reason)
{
 u32 ioc_config, timeout, ioc_status, scratch_pad0;
 int retval = -1;

 ioc_info(mrioc, "Issuing Message unit Reset(MUR)\n");
 if (mrioc->unrecoverable) {
  ioc_info(mrioc, "IOC is unrecoverable MUR not issued\n");
  return retval;
 }
 mpi3mr_clear_reset_history(mrioc);
 scratch_pad0 = ((MPI3MR_RESET_REASON_OSTYPE_LINUX <<
    MPI3MR_RESET_REASON_OSTYPE_SHIFT) |
   (mrioc->facts.ioc_num <<
    MPI3MR_RESET_REASON_IOCNUM_SHIFT) | reset_reason);
 writel(scratch_pad0, &mrioc->sysif_regs->scratchpad[0]);
 ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
 ioc_config &= ~MPI3_SYSIF_IOC_CONFIG_ENABLE_IOC;
 writel(ioc_config, &mrioc->sysif_regs->ioc_configuration);

 timeout = MPI3MR_MUR_TIMEOUT * 10;
 do {
  ioc_status = readl(&mrioc->sysif_regs->ioc_status);
  if ((ioc_status & MPI3_SYSIF_IOC_STATUS_RESET_HISTORY)) {
   mpi3mr_clear_reset_history(mrioc);
   break;
  }
  if (ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT) {
   mpi3mr_print_fault_info(mrioc);
   break;
  }
  msleep(100);
 } while (--timeout);

 ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
 if (timeout && !((ioc_status & MPI3_SYSIF_IOC_STATUS_READY) ||
       (ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT) ||
       (ioc_config & MPI3_SYSIF_IOC_CONFIG_ENABLE_IOC)))
  retval = 0;

 ioc_info(mrioc, "Base IOC Sts/Config after %s MUR is (0x%08x)/(0x%08x)\n",
     (!retval) ? "successful" : "failed", ioc_status, ioc_config);
 return retval;
}

/**
 * mpi3mr_revalidate_factsdata - validate IOCFacts parameters
 * during reset/resume
 * @mrioc: Adapter instance reference
 *
 * Return: zero if the new IOCFacts parameters value is compatible with
 * older values else return -EPERM
 */

static int
mpi3mr_revalidate_factsdata(struct mpi3mr_ioc *mrioc)
{
 unsigned long *removepend_bitmap;

 if (mrioc->facts.reply_sz > mrioc->reply_sz) {
  ioc_err(mrioc,
      "cannot increase reply size from %d to %d\n",
      mrioc->reply_sz, mrioc->facts.reply_sz);
  return -EPERM;
 }

 if (mrioc->facts.max_op_reply_q < mrioc->num_op_reply_q) {
  ioc_err(mrioc,
      "cannot reduce number of operational reply queues from %d to %d\n",
      mrioc->num_op_reply_q,
      mrioc->facts.max_op_reply_q);
  return -EPERM;
 }

 if (mrioc->facts.max_op_req_q < mrioc->num_op_req_q) {
  ioc_err(mrioc,
      "cannot reduce number of operational request queues from %d to %d\n",
      mrioc->num_op_req_q, mrioc->facts.max_op_req_q);
  return -EPERM;
 }

 if (mrioc->shost->max_sectors != (mrioc->facts.max_data_length / 512))
  ioc_err(mrioc, "Warning: The maximum data transfer length\n"
       "\tchanged after reset: previous(%d), new(%d),\n"
       "the driver cannot change this at run time\n",
       mrioc->shost->max_sectors * 512, mrioc->facts.max_data_length);

 if ((mrioc->sas_transport_enabled) && (mrioc->facts.ioc_capabilities &
     MPI3_IOCFACTS_CAPABILITY_MULTIPATH_SUPPORTED))
  ioc_err(mrioc,
      "critical error: multipath capability is enabled at the\n"
      "\tcontroller while sas transport support is enabled at the\n"
      "\tdriver, please reboot the system or reload the driver\n");

 if (mrioc->seg_tb_support) {
  if (!(mrioc->facts.ioc_capabilities &
       MPI3_IOCFACTS_CAPABILITY_SEG_DIAG_TRACE_SUPPORTED)) {
   ioc_err(mrioc,
       "critical error: previously enabled segmented trace\n"
       " buffer capability is disabled after reset. Please\n"
       " update the firmware or reboot the system or\n"
       " reload the driver to enable trace diag buffer\n");
   mrioc->diag_buffers[0].disabled_after_reset = true;
  } else
   mrioc->diag_buffers[0].disabled_after_reset = false;
 }

 if (mrioc->facts.max_devhandle > mrioc->dev_handle_bitmap_bits) {
  removepend_bitmap = bitmap_zalloc(mrioc->facts.max_devhandle,
        GFP_KERNEL);
  if (!removepend_bitmap) {
   ioc_err(mrioc,
    "failed to increase removepend_bitmap bits from %d to %d\n",
    mrioc->dev_handle_bitmap_bits,
    mrioc->facts.max_devhandle);
   return -EPERM;
  }
  bitmap_free(mrioc->removepend_bitmap);
  mrioc->removepend_bitmap = removepend_bitmap;
  ioc_info(mrioc,
    "increased bits of dev_handle_bitmap from %d to %d\n",
    mrioc->dev_handle_bitmap_bits,
    mrioc->facts.max_devhandle);
  mrioc->dev_handle_bitmap_bits = mrioc->facts.max_devhandle;
 }

 return 0;
}

/**
 * mpi3mr_bring_ioc_ready - Bring controller to ready state
 * @mrioc: Adapter instance reference
 *
 * Set Enable IOC bit in IOC configuration register and wait for
 * the controller to become ready.
 *
 * Return: 0 on success, appropriate error on failure.
 */

static int mpi3mr_bring_ioc_ready(struct mpi3mr_ioc *mrioc)
{
 u32 ioc_config, ioc_status, timeout, host_diagnostic;
 int retval = 0;
 enum mpi3mr_iocstate ioc_state;
 u64 base_info;
 u8 retry = 0;
 u64 start_time, elapsed_time_sec;

retry_bring_ioc_ready:

 ioc_status = readl(&mrioc->sysif_regs->ioc_status);
 ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
 base_info = lo_hi_readq(&mrioc->sysif_regs->ioc_information);
 ioc_info(mrioc, "ioc_status(0x%08x), ioc_config(0x%08x), ioc_info(0x%016llx) at the bringup\n",
     ioc_status, ioc_config, base_info);

 if (!mpi3mr_is_fault_recoverable(mrioc)) {
  mrioc->unrecoverable = 1;
  goto out_device_not_present;
 }

 /*The timeout value is in 2sec unit, changing it to seconds*/
 mrioc->ready_timeout =
     ((base_info & MPI3_SYSIF_IOC_INFO_LOW_TIMEOUT_MASK) >>
     MPI3_SYSIF_IOC_INFO_LOW_TIMEOUT_SHIFT) * 2;

 ioc_info(mrioc, "ready timeout: %d seconds\n", mrioc->ready_timeout);

 ioc_state = mpi3mr_get_iocstate(mrioc);
 ioc_info(mrioc, "controller is in %s state during detection\n",
     mpi3mr_iocstate_name(ioc_state));

 timeout = mrioc->ready_timeout * 10;

 do {
  ioc_state = mpi3mr_get_iocstate(mrioc);

  if (ioc_state != MRIOC_STATE_BECOMING_READY &&
      ioc_state != MRIOC_STATE_RESET_REQUESTED)
   break;

  if (!pci_device_is_present(mrioc->pdev)) {
   mrioc->unrecoverable = 1;
   ioc_err(mrioc, "controller is not present while waiting to reset\n");
   goto out_device_not_present;
  }

  msleep(100);
 } while (--timeout);

 if (ioc_state == MRIOC_STATE_READY) {
  ioc_info(mrioc, "issuing message unit reset (MUR) to bring to reset state\n");
  retval = mpi3mr_issue_and_process_mur(mrioc,
      MPI3MR_RESET_FROM_BRINGUP);
  ioc_state = mpi3mr_get_iocstate(mrioc);
  if (retval)
   ioc_err(mrioc,
       "message unit reset failed with error %d current state %s\n",
       retval, mpi3mr_iocstate_name(ioc_state));
 }
 if (ioc_state != MRIOC_STATE_RESET) {
  if (ioc_state == MRIOC_STATE_FAULT) {
   timeout = MPI3_SYSIF_DIAG_SAVE_TIMEOUT * 10;
   mpi3mr_print_fault_info(mrioc);
   do {
    host_diagnostic =
     readl(&mrioc->sysif_regs->host_diagnostic);
    if (!(host_diagnostic &
          MPI3_SYSIF_HOST_DIAG_SAVE_IN_PROGRESS))
     break;
    if (!pci_device_is_present(mrioc->pdev)) {
     mrioc->unrecoverable = 1;
     ioc_err(mrioc, "controller is not present at the bringup\n");
     goto out_device_not_present;
    }
    msleep(100);
   } while (--timeout);
  }
  mpi3mr_print_fault_info(mrioc);
  ioc_info(mrioc, "issuing soft reset to bring to reset state\n");
  retval = mpi3mr_issue_reset(mrioc,
      MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET,
      MPI3MR_RESET_FROM_BRINGUP);
  if (retval) {
   ioc_err(mrioc,
       "soft reset failed with error %d\n", retval);
   goto out_failed;
  }
 }
 ioc_state = mpi3mr_get_iocstate(mrioc);
 if (ioc_state != MRIOC_STATE_RESET) {
  ioc_err(mrioc,
      "cannot bring controller to reset state, current state: %s\n",
      mpi3mr_iocstate_name(ioc_state));
  goto out_failed;
 }
 mpi3mr_clear_reset_history(mrioc);
 retval = mpi3mr_setup_admin_qpair(mrioc);
 if (retval) {
  ioc_err(mrioc, "failed to setup admin queues: error %d\n",
      retval);
  goto out_failed;
 }

 ioc_info(mrioc, "bringing controller to ready state\n");
 ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
 ioc_config |= MPI3_SYSIF_IOC_CONFIG_ENABLE_IOC;
 writel(ioc_config, &mrioc->sysif_regs->ioc_configuration);

 if (retry == 0)
  start_time = jiffies;

 timeout = mrioc->ready_timeout * 10;
 do {
  ioc_state = mpi3mr_get_iocstate(mrioc);
  if (ioc_state == MRIOC_STATE_READY) {
   ioc_info(mrioc,
       "successfully transitioned to %s state\n",
       mpi3mr_iocstate_name(ioc_state));
   return 0;
  }
  ioc_status = readl(&mrioc->sysif_regs->ioc_status);
  if ((ioc_status & MPI3_SYSIF_IOC_STATUS_RESET_HISTORY) ||
      (ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT)) {
   mpi3mr_print_fault_info(mrioc);
   goto out_failed;
  }
  if (!pci_device_is_present(mrioc->pdev)) {
   mrioc->unrecoverable = 1;
   ioc_err(mrioc,
       "controller is not present at the bringup\n");
   retval = -1;
   goto out_device_not_present;
  }
  msleep(100);
  elapsed_time_sec = jiffies_to_msecs(jiffies - start_time)/1000;
 } while (elapsed_time_sec < mrioc->ready_timeout);

out_failed:
 elapsed_time_sec = jiffies_to_msecs(jiffies - start_time)/1000;
 if ((retry < 2) && (elapsed_time_sec < (mrioc->ready_timeout - 60))) {
  retry++;

  ioc_warn(mrioc, "retrying to bring IOC ready, retry_count:%d\n"
    " elapsed time =%llu\n", retry, elapsed_time_sec);

  goto retry_bring_ioc_ready;
 }
 ioc_state = mpi3mr_get_iocstate(mrioc);
 ioc_err(mrioc,
     "failed to bring to ready state, current state: %s\n",
     mpi3mr_iocstate_name(ioc_state));
out_device_not_present:
 return retval;
}

/**
 * mpi3mr_soft_reset_success - Check softreset is success or not
 * @ioc_status: IOC status register value
 * @ioc_config: IOC config register value
 *
 * Check whether the soft reset is successful or not based on
 * IOC status and IOC config register values.
 *
 * Return: True when the soft reset is success, false otherwise.
 */

static inline bool
mpi3mr_soft_reset_success(u32 ioc_status, u32 ioc_config)
{
 if (!((ioc_status & MPI3_SYSIF_IOC_STATUS_READY) ||
     (ioc_config & MPI3_SYSIF_IOC_CONFIG_ENABLE_IOC)))
  return true;
 return false;
}

/**
 * mpi3mr_diagfault_success - Check diag fault is success or not
 * @mrioc: Adapter reference
 * @ioc_status: IOC status register value
 *
 * Check whether the controller hit diag reset fault code.
 *
 * Return: True when there is diag fault, false otherwise.
 */

static inline bool mpi3mr_diagfault_success(struct mpi3mr_ioc *mrioc,
 u32 ioc_status)
{
 u32 fault;

 if (!(ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT))
  return false;
 fault = readl(&mrioc->sysif_regs->fault) & MPI3_SYSIF_FAULT_CODE_MASK;
 if (fault == MPI3_SYSIF_FAULT_CODE_DIAG_FAULT_RESET) {
  mpi3mr_print_fault_info(mrioc);
  return true;
 }
 return false;
}

/**
 * mpi3mr_set_diagsave - Set diag save bit for snapdump
 * @mrioc: Adapter reference
 *
 * Set diag save bit in IOC configuration register to enable
 * snapdump.
 *
 * Return: Nothing.
 */

static inline void mpi3mr_set_diagsave(struct mpi3mr_ioc *mrioc)
{
 u32 ioc_config;

 ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
 ioc_config |= MPI3_SYSIF_IOC_CONFIG_DIAG_SAVE;
 writel(ioc_config, &mrioc->sysif_regs->ioc_configuration);
}

/**
 * mpi3mr_issue_reset - Issue reset to the controller
 * @mrioc: Adapter reference
 * @reset_type: Reset type
 * @reset_reason: Reset reason code
 *
 * Unlock the host diagnostic registers and write the specific
 * reset type to that, wait for reset acknowledgment from the
 * controller, if the reset is not successful retry for the
 * predefined number of times.
 *
 * Return: 0 on success, non-zero on failure.
 */

static int mpi3mr_issue_reset(struct mpi3mr_ioc *mrioc, u16 reset_type,
 u16 reset_reason)
{
 int retval = -1;
 u8 unlock_retry_count = 0;
 u32 host_diagnostic, ioc_status, ioc_config, scratch_pad0;
 u32 timeout = MPI3MR_RESET_ACK_TIMEOUT * 10;

 if ((reset_type != MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET) &&
     (reset_type != MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT))
  return retval;
 if (mrioc->unrecoverable)
  return retval;
 if (reset_reason == MPI3MR_RESET_FROM_FIRMWARE) {
  retval = 0;
  return retval;
 }

 ioc_info(mrioc, "%s reset due to %s(0x%x)\n",
     mpi3mr_reset_type_name(reset_type),
     mpi3mr_reset_rc_name(reset_reason), reset_reason);

 mpi3mr_clear_reset_history(mrioc);
 do {
  ioc_info(mrioc,
      "Write magic sequence to unlock host diag register (retry=%d)\n",
      ++unlock_retry_count);
  if (unlock_retry_count >= MPI3MR_HOSTDIAG_UNLOCK_RETRY_COUNT) {
   ioc_err(mrioc,
       "%s reset failed due to unlock failure, host_diagnostic(0x%08x)\n",
       mpi3mr_reset_type_name(reset_type),
       host_diagnostic);
   mrioc->unrecoverable = 1;
   return retval;
  }

  writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_FLUSH,
      &mrioc->sysif_regs->write_sequence);
  writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_1ST,
      &mrioc->sysif_regs->write_sequence);
  writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_2ND,
      &mrioc->sysif_regs->write_sequence);
  writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_3RD,
      &mrioc->sysif_regs->write_sequence);
  writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_4TH,
      &mrioc->sysif_regs->write_sequence);
  writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_5TH,
      &mrioc->sysif_regs->write_sequence);
  writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_6TH,
      &mrioc->sysif_regs->write_sequence);
  usleep_range(1000, 1100);
  host_diagnostic = readl(&mrioc->sysif_regs->host_diagnostic);
  ioc_info(mrioc,
      "wrote magic sequence: retry_count(%d), host_diagnostic(0x%08x)\n",
      unlock_retry_count, host_diagnostic);
 } while (!(host_diagnostic & MPI3_SYSIF_HOST_DIAG_DIAG_WRITE_ENABLE));

 scratch_pad0 = ((MPI3MR_RESET_REASON_OSTYPE_LINUX <<
     MPI3MR_RESET_REASON_OSTYPE_SHIFT) | (mrioc->facts.ioc_num <<
     MPI3MR_RESET_REASON_IOCNUM_SHIFT) | reset_reason);
 writel(reset_reason, &mrioc->sysif_regs->scratchpad[0]);
 writel(host_diagnostic | reset_type,
     &mrioc->sysif_regs->host_diagnostic);
 switch (reset_type) {
 case MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET:
  do {
   ioc_status = readl(&mrioc->sysif_regs->ioc_status);
   ioc_config =
       readl(&mrioc->sysif_regs->ioc_configuration);
   if ((ioc_status & MPI3_SYSIF_IOC_STATUS_RESET_HISTORY)
       && mpi3mr_soft_reset_success(ioc_status, ioc_config)
       ) {
    mpi3mr_clear_reset_history(mrioc);
    retval = 0;
    break;
   }
   msleep(100);
  } while (--timeout);
  mpi3mr_print_fault_info(mrioc);
  break;
 case MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT:
  do {
   ioc_status = readl(&mrioc->sysif_regs->ioc_status);
   if (mpi3mr_diagfault_success(mrioc, ioc_status)) {
    retval = 0;
    break;
   }
   msleep(100);
  } while (--timeout);
  break;
 default:
  break;
 }

 writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_2ND,
     &mrioc->sysif_regs->write_sequence);

 ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
 ioc_status = readl(&mrioc->sysif_regs->ioc_status);
 ioc_info(mrioc,
     "ioc_status/ioc_config after %s reset is (0x%08x)/(0x%08x)\n",
     (!retval)?"successful":"failed", ioc_status,
     ioc_config);
 if (retval)
  mrioc->unrecoverable = 1;
 return retval;
}

/**
 * mpi3mr_admin_request_post - Post request to admin queue
 * @mrioc: Adapter reference
 * @admin_req: MPI3 request
 * @admin_req_sz: Request size
 * @ignore_reset: Ignore reset in process
 *
 * Post the MPI3 request into admin request queue and
 * inform the controller, if the queue is full return
 * appropriate error.
 *
 * Return: 0 on success, non-zero on failure.
 */

int mpi3mr_admin_request_post(struct mpi3mr_ioc *mrioc, void *admin_req,
 u16 admin_req_sz, u8 ignore_reset)
{
 u16 areq_pi = 0, areq_ci = 0, max_entries = 0;
 int retval = 0;
 unsigned long flags;
 u8 *areq_entry;

 if (mrioc->unrecoverable) {
  ioc_err(mrioc, "%s : Unrecoverable controller\n", __func__);
  return -EFAULT;
 }

 spin_lock_irqsave(&mrioc->admin_req_lock, flags);
 areq_pi = mrioc->admin_req_pi;
 areq_ci = mrioc->admin_req_ci;
 max_entries = mrioc->num_admin_req;
 if ((areq_ci == (areq_pi + 1)) || ((!areq_ci) &&
     (areq_pi == (max_entries - 1)))) {
  ioc_err(mrioc, "AdminReqQ full condition detected\n");
  retval = -EAGAIN;
  goto out;
 }
 if (!ignore_reset && mrioc->reset_in_progress) {
  ioc_err(mrioc, "AdminReqQ submit reset in progress\n");
  retval = -EAGAIN;
  goto out;
 }
 if (mrioc->pci_err_recovery) {
  ioc_err(mrioc, "admin request queue submission failed due to pci error recovery in progress\n");
  retval = -EAGAIN;
  goto out;
 }

 areq_entry = (u8 *)mrioc->admin_req_base +
     (areq_pi * MPI3MR_ADMIN_REQ_FRAME_SZ);
 memset(areq_entry, 0, MPI3MR_ADMIN_REQ_FRAME_SZ);
 memcpy(areq_entry, (u8 *)admin_req, admin_req_sz);

 if (++areq_pi == max_entries)
  areq_pi = 0;
 mrioc->admin_req_pi = areq_pi;

 writel(mrioc->admin_req_pi, &mrioc->sysif_regs->admin_request_queue_pi);

out:
 spin_unlock_irqrestore(&mrioc->admin_req_lock, flags);

 return retval;
}

/**
 * mpi3mr_free_op_req_q_segments - free request memory segments
 * @mrioc: Adapter instance reference
 * @q_idx: operational request queue index
 *
 * Free memory segments allocated for operational request queue
 *
 * Return: Nothing.
 */

static void mpi3mr_free_op_req_q_segments(struct mpi3mr_ioc *mrioc, u16 q_idx)
{
 u16 j;
 int size;
 struct segments *segments;

 segments = mrioc->req_qinfo[q_idx].q_segments;
 if (!segments)
  return;

 if (mrioc->enable_segqueue) {
  size = MPI3MR_OP_REQ_Q_SEG_SIZE;
  if (mrioc->req_qinfo[q_idx].q_segment_list) {
   dma_free_coherent(&mrioc->pdev->dev,
       MPI3MR_MAX_SEG_LIST_SIZE,
       mrioc->req_qinfo[q_idx].q_segment_list,
       mrioc->req_qinfo[q_idx].q_segment_list_dma);
   mrioc->req_qinfo[q_idx].q_segment_list = NULL;
  }
 } else
  size = mrioc->req_qinfo[q_idx].segment_qd *
      mrioc->facts.op_req_sz;

 for (j = 0; j < mrioc->req_qinfo[q_idx].num_segments; j++) {
  if (!segments[j].segment)
   continue;
  dma_free_coherent(&mrioc->pdev->dev,
      size, segments[j].segment, segments[j].segment_dma);
  segments[j].segment = NULL;
 }
 kfree(mrioc->req_qinfo[q_idx].q_segments);
 mrioc->req_qinfo[q_idx].q_segments = NULL;
 mrioc->req_qinfo[q_idx].qid = 0;
}

/**
 * mpi3mr_free_op_reply_q_segments - free reply memory segments
 * @mrioc: Adapter instance reference
 * @q_idx: operational reply queue index
 *
 * Free memory segments allocated for operational reply queue
 *
 * Return: Nothing.
 */

static void mpi3mr_free_op_reply_q_segments(struct mpi3mr_ioc *mrioc, u16 q_idx)
{
 u16 j;
 int size;
 struct segments *segments;

 segments = mrioc->op_reply_qinfo[q_idx].q_segments;
 if (!segments)
  return;

 if (mrioc->enable_segqueue) {
  size = MPI3MR_OP_REP_Q_SEG_SIZE;
  if (mrioc->op_reply_qinfo[q_idx].q_segment_list) {
   dma_free_coherent(&mrioc->pdev->dev,
       MPI3MR_MAX_SEG_LIST_SIZE,
       mrioc->op_reply_qinfo[q_idx].q_segment_list,
       mrioc->op_reply_qinfo[q_idx].q_segment_list_dma);
   mrioc->op_reply_qinfo[q_idx].q_segment_list = NULL;
  }
 } else
  size = mrioc->op_reply_qinfo[q_idx].segment_qd *
      mrioc->op_reply_desc_sz;

 for (j = 0; j < mrioc->op_reply_qinfo[q_idx].num_segments; j++) {
  if (!segments[j].segment)
   continue;
  dma_free_coherent(&mrioc->pdev->dev,
      size, segments[j].segment, segments[j].segment_dma);
  segments[j].segment = NULL;
 }

 kfree(mrioc->op_reply_qinfo[q_idx].q_segments);
 mrioc->op_reply_qinfo[q_idx].q_segments = NULL;
 mrioc->op_reply_qinfo[q_idx].qid = 0;
}

/**
 * mpi3mr_delete_op_reply_q - delete operational reply queue
 * @mrioc: Adapter instance reference
 * @qidx: operational reply queue index
 *
 * Delete operatinal reply queue by issuing MPI request
 * through admin queue.
 *
 * Return:  0 on success, non-zero on failure.
 */

static int mpi3mr_delete_op_reply_q(struct mpi3mr_ioc *mrioc, u16 qidx)
{
 struct mpi3_delete_reply_queue_request delq_req;
 struct op_reply_qinfo *op_reply_q = mrioc->op_reply_qinfo + qidx;
 int retval = 0;
 u16 reply_qid = 0, midx;

 reply_qid = op_reply_q->qid;

 midx = REPLY_QUEUE_IDX_TO_MSIX_IDX(qidx, mrioc->op_reply_q_offset);

 if (!reply_qid) {
  retval = -1;
  ioc_err(mrioc, "Issue DelRepQ: called with invalid ReqQID\n");
  goto out;
 }

 (op_reply_q->qtype == MPI3MR_DEFAULT_QUEUE) ? mrioc->default_qcount-- :
     mrioc->active_poll_qcount--;

 memset(&delq_req, 0, sizeof(delq_req));
 mutex_lock(&mrioc->init_cmds.mutex);
 if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) {
  retval = -1;
  ioc_err(mrioc, "Issue DelRepQ: Init command is in use\n");
  mutex_unlock(&mrioc->init_cmds.mutex);
  goto out;
 }
 mrioc->init_cmds.state = MPI3MR_CMD_PENDING;
 mrioc->init_cmds.is_waiting = 1;
 mrioc->init_cmds.callback = NULL;
 delq_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS);
 delq_req.function = MPI3_FUNCTION_DELETE_REPLY_QUEUE;
 delq_req.queue_id = cpu_to_le16(reply_qid);

 init_completion(&mrioc->init_cmds.done);
 retval = mpi3mr_admin_request_post(mrioc, &delq_req, sizeof(delq_req),
     1);
 if (retval) {
  ioc_err(mrioc, "Issue DelRepQ: Admin Post failed\n");
  goto out_unlock;
 }
 wait_for_completion_timeout(&mrioc->init_cmds.done,
     (MPI3MR_INTADMCMD_TIMEOUT * HZ));
 if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
  ioc_err(mrioc, "delete reply queue timed out\n");
  mpi3mr_check_rh_fault_ioc(mrioc,
      MPI3MR_RESET_FROM_DELREPQ_TIMEOUT);
  retval = -1;
  goto out_unlock;
 }
 if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
     != MPI3_IOCSTATUS_SUCCESS) {
  ioc_err(mrioc,
      "Issue DelRepQ: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n",
      (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
      mrioc->init_cmds.ioc_loginfo);
  retval = -1;
  goto out_unlock;
 }
 mrioc->intr_info[midx].op_reply_q = NULL;

 mpi3mr_free_op_reply_q_segments(mrioc, qidx);
out_unlock:
 mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
 mutex_unlock(&mrioc->init_cmds.mutex);
out:

 return retval;
}

/**
 * mpi3mr_alloc_op_reply_q_segments -Alloc segmented reply pool
 * @mrioc: Adapter instance reference
 * @qidx: request queue index
 *
 * Allocate segmented memory pools for operational reply
 * queue.
 *
 * Return: 0 on success, non-zero on failure.
 */

static int mpi3mr_alloc_op_reply_q_segments(struct mpi3mr_ioc *mrioc, u16 qidx)
{
 struct op_reply_qinfo *op_reply_q = mrioc->op_reply_qinfo + qidx;
 int i, size;
 u64 *q_segment_list_entry = NULL;
 struct segments *segments;

 if (mrioc->enable_segqueue) {
  op_reply_q->segment_qd =
      MPI3MR_OP_REP_Q_SEG_SIZE / mrioc->op_reply_desc_sz;

  size = MPI3MR_OP_REP_Q_SEG_SIZE;

  op_reply_q->q_segment_list = dma_alloc_coherent(&mrioc->pdev->dev,
      MPI3MR_MAX_SEG_LIST_SIZE, &op_reply_q->q_segment_list_dma,
      GFP_KERNEL);
  if (!op_reply_q->q_segment_list)
   return -ENOMEM;
  q_segment_list_entry = (u64 *)op_reply_q->q_segment_list;
 } else {
  op_reply_q->segment_qd = op_reply_q->num_replies;
  size = op_reply_q->num_replies * mrioc->op_reply_desc_sz;
 }

 op_reply_q->num_segments = DIV_ROUND_UP(op_reply_q->num_replies,
     op_reply_q->segment_qd);

 op_reply_q->q_segments = kcalloc(op_reply_q->num_segments,
     sizeof(struct segments), GFP_KERNEL);
 if (!op_reply_q->q_segments)
  return -ENOMEM;

 segments = op_reply_q->q_segments;
 for (i = 0; i < op_reply_q->num_segments; i++) {
  segments[i].segment =
      dma_alloc_coherent(&mrioc->pdev->dev,
      size, &segments[i].segment_dma, GFP_KERNEL);
  if (!segments[i].segment)
   return -ENOMEM;
  if (mrioc->enable_segqueue)
   q_segment_list_entry[i] =
       (unsigned long)segments[i].segment_dma;
 }

 return 0;
}

/**
 * mpi3mr_alloc_op_req_q_segments - Alloc segmented req pool.
 * @mrioc: Adapter instance reference
 * @qidx: request queue index
 *
 * Allocate segmented memory pools for operational request
 * queue.
 *
 * Return: 0 on success, non-zero on failure.
 */

static int mpi3mr_alloc_op_req_q_segments(struct mpi3mr_ioc *mrioc, u16 qidx)
{
 struct op_req_qinfo *op_req_q = mrioc->req_qinfo + qidx;
 int i, size;
 u64 *q_segment_list_entry = NULL;
 struct segments *segments;

 if (mrioc->enable_segqueue) {
  op_req_q->segment_qd =
      MPI3MR_OP_REQ_Q_SEG_SIZE / mrioc->facts.op_req_sz;

  size = MPI3MR_OP_REQ_Q_SEG_SIZE;

  op_req_q->q_segment_list = dma_alloc_coherent(&mrioc->pdev->dev,
      MPI3MR_MAX_SEG_LIST_SIZE, &op_req_q->q_segment_list_dma,
      GFP_KERNEL);
  if (!op_req_q->q_segment_list)
   return -ENOMEM;
  q_segment_list_entry = (u64 *)op_req_q->q_segment_list;

 } else {
  op_req_q->segment_qd = op_req_q->num_requests;
  size = op_req_q->num_requests * mrioc->facts.op_req_sz;
 }

 op_req_q->num_segments = DIV_ROUND_UP(op_req_q->num_requests,
     op_req_q->segment_qd);

 op_req_q->q_segments = kcalloc(op_req_q->num_segments,
     sizeof(struct segments), GFP_KERNEL);
 if (!op_req_q->q_segments)
  return -ENOMEM;

 segments = op_req_q->q_segments;
 for (i = 0; i < op_req_q->num_segments; i++) {
  segments[i].segment =
      dma_alloc_coherent(&mrioc->pdev->dev,
      size, &segments[i].segment_dma, GFP_KERNEL);
  if (!segments[i].segment)
   return -ENOMEM;
  if (mrioc->enable_segqueue)
   q_segment_list_entry[i] =
       (unsigned long)segments[i].segment_dma;
 }

 return 0;
}

/**
 * mpi3mr_create_op_reply_q - create operational reply queue
 * @mrioc: Adapter instance reference
 * @qidx: operational reply queue index
 *
 * Create operatinal reply queue by issuing MPI request
 * through admin queue.
 *
 * Return:  0 on success, non-zero on failure.
 */

static int mpi3mr_create_op_reply_q(struct mpi3mr_ioc *mrioc, u16 qidx)
{
 struct mpi3_create_reply_queue_request create_req;
 struct op_reply_qinfo *op_reply_q = mrioc->op_reply_qinfo + qidx;
 int retval = 0;
 u16 reply_qid = 0, midx;

 reply_qid = op_reply_q->qid;

 midx = REPLY_QUEUE_IDX_TO_MSIX_IDX(qidx, mrioc->op_reply_q_offset);

 if (reply_qid) {
  retval = -1;
  ioc_err(mrioc, "CreateRepQ: called for duplicate qid %d\n",
      reply_qid);

  return retval;
 }

 reply_qid = qidx + 1;

 if (mrioc->pdev->device == MPI3_MFGPAGE_DEVID_SAS4116) {
  if (mrioc->pdev->revision)
   op_reply_q->num_replies = MPI3MR_OP_REP_Q_QD;
  else
   op_reply_q->num_replies = MPI3MR_OP_REP_Q_QD4K;
 } else
  op_reply_q->num_replies = MPI3MR_OP_REP_Q_QD2K;

 op_reply_q->ci = 0;
 op_reply_q->ephase = 1;
 atomic_set(&op_reply_q->pend_ios, 0);
 atomic_set(&op_reply_q->in_use, 0);
 op_reply_q->enable_irq_poll = false;
 op_reply_q->qfull_watermark =
  op_reply_q->num_replies - (MPI3MR_THRESHOLD_REPLY_COUNT * 2);

 if (!op_reply_q->q_segments) {
  retval = mpi3mr_alloc_op_reply_q_segments(mrioc, qidx);
  if (retval) {
   mpi3mr_free_op_reply_q_segments(mrioc, qidx);
   goto out;
  }
 }

 memset(&create_req, 0, sizeof(create_req));
 mutex_lock(&mrioc->init_cmds.mutex);
 if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) {
  retval = -1;
  ioc_err(mrioc, "CreateRepQ: Init command is in use\n");
  goto out_unlock;
 }
 mrioc->init_cmds.state = MPI3MR_CMD_PENDING;
 mrioc->init_cmds.is_waiting = 1;
 mrioc->init_cmds.callback = NULL;
 create_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS);
 create_req.function = MPI3_FUNCTION_CREATE_REPLY_QUEUE;
 create_req.queue_id = cpu_to_le16(reply_qid);

 if (midx < (mrioc->intr_info_count - mrioc->requested_poll_qcount))
  op_reply_q->qtype = MPI3MR_DEFAULT_QUEUE;
 else
  op_reply_q->qtype = MPI3MR_POLL_QUEUE;

 if (op_reply_q->qtype == MPI3MR_DEFAULT_QUEUE) {
  create_req.flags =
   MPI3_CREATE_REPLY_QUEUE_FLAGS_INT_ENABLE_ENABLE;
  create_req.msix_index =
   cpu_to_le16(mrioc->intr_info[midx].msix_index);
 } else {
  create_req.msix_index = cpu_to_le16(mrioc->intr_info_count - 1);
  ioc_info(mrioc, "create reply queue(polled): for qid(%d), midx(%d)\n",
   reply_qid, midx);
  if (!mrioc->active_poll_qcount)
   disable_irq_nosync(pci_irq_vector(mrioc->pdev,
       mrioc->intr_info_count - 1));
 }

 if (mrioc->enable_segqueue) {
  create_req.flags |=
      MPI3_CREATE_REQUEST_QUEUE_FLAGS_SEGMENTED_SEGMENTED;
  create_req.base_address = cpu_to_le64(
      op_reply_q->q_segment_list_dma);
 } else
  create_req.base_address = cpu_to_le64(
      op_reply_q->q_segments[0].segment_dma);

 create_req.size = cpu_to_le16(op_reply_q->num_replies);

 init_completion(&mrioc->init_cmds.done);
 retval = mpi3mr_admin_request_post(mrioc, &create_req,
     sizeof(create_req), 1);
 if (retval) {
  ioc_err(mrioc, "CreateRepQ: Admin Post failed\n");
  goto out_unlock;
 }
 wait_for_completion_timeout(&mrioc->init_cmds.done,
     (MPI3MR_INTADMCMD_TIMEOUT * HZ));
 if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
  ioc_err(mrioc, "create reply queue timed out\n");
  mpi3mr_check_rh_fault_ioc(mrioc,
      MPI3MR_RESET_FROM_CREATEREPQ_TIMEOUT);
  retval = -1;
  goto out_unlock;
 }
 if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
     != MPI3_IOCSTATUS_SUCCESS) {
  ioc_err(mrioc,
      "CreateRepQ: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n",
      (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
      mrioc->init_cmds.ioc_loginfo);
  retval = -1;
  goto out_unlock;
 }
 op_reply_q->qid = reply_qid;
 if (midx < mrioc->intr_info_count)
  mrioc->intr_info[midx].op_reply_q = op_reply_q;

 (op_reply_q->qtype == MPI3MR_DEFAULT_QUEUE) ? mrioc->default_qcount++ :
     mrioc->active_poll_qcount++;

out_unlock:
 mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
 mutex_unlock(&mrioc->init_cmds.mutex);
out:

 return retval;
}

/**
 * mpi3mr_create_op_req_q - create operational request queue
 * @mrioc: Adapter instance reference
 * @idx: operational request queue index
 * @reply_qid: Reply queue ID
 *
 * Create operatinal request queue by issuing MPI request
 * through admin queue.
 *
 * Return:  0 on success, non-zero on failure.
 */

static int mpi3mr_create_op_req_q(struct mpi3mr_ioc *mrioc, u16 idx,
 u16 reply_qid)
{
 struct mpi3_create_request_queue_request create_req;
 struct op_req_qinfo *op_req_q = mrioc->req_qinfo + idx;
 int retval = 0;
 u16 req_qid = 0;

 req_qid = op_req_q->qid;

 if (req_qid) {
  retval = -1;
  ioc_err(mrioc, "CreateReqQ: called for duplicate qid %d\n",
      req_qid);

  return retval;
 }
 req_qid = idx + 1;

 op_req_q->num_requests = MPI3MR_OP_REQ_Q_QD;
 op_req_q->ci = 0;
 op_req_q->pi = 0;
 op_req_q->reply_qid = reply_qid;
--> --------------------

--> maximum size reached

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

Messung V0.5
C=99 H=85 G=92

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