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

Quelle  mpi3mr_os.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/idr.h>

/* global driver scop variables */
LIST_HEAD(mrioc_list);
DEFINE_SPINLOCK(mrioc_list_lock);
static DEFINE_IDA(mrioc_ida);
static int warn_non_secure_ctlr;
atomic64_t event_counter;

MODULE_AUTHOR(MPI3MR_DRIVER_AUTHOR);
MODULE_DESCRIPTION(MPI3MR_DRIVER_DESC);
MODULE_LICENSE(MPI3MR_DRIVER_LICENSE);
MODULE_VERSION(MPI3MR_DRIVER_VERSION);

/* Module parameters*/
int prot_mask = -1;
module_param(prot_mask, int, 0);
MODULE_PARM_DESC(prot_mask, "Host protection capabilities mask, def=0x07");

static int prot_guard_mask = 3;
module_param(prot_guard_mask, int, 0);
MODULE_PARM_DESC(prot_guard_mask, " Host protection guard mask, def=3");
static int logging_level;
module_param(logging_level, int, 0);
MODULE_PARM_DESC(logging_level,
 " bits for enabling additional logging info (default=0)");
static int max_sgl_entries = MPI3MR_DEFAULT_SGL_ENTRIES;
module_param(max_sgl_entries, int, 0444);
MODULE_PARM_DESC(max_sgl_entries,
 "Preferred max number of SG entries to be used for a single I/O\n"
 "The actual value will be determined by the driver\n"
 "(Minimum=256, Maximum=2048, default=256)");

/* Forward declarations*/
static void mpi3mr_send_event_ack(struct mpi3mr_ioc *mrioc, u8 event,
 struct mpi3mr_drv_cmd *cmdparam, u32 event_ctx);

#define MPI3MR_DRIVER_EVENT_TG_QD_REDUCTION (0xFFFF)

#define MPI3_EVENT_WAIT_FOR_DEVICES_TO_REFRESH (0xFFFE)

/*
 * SAS Log info code for a NCQ collateral abort after an NCQ error:
 * IOC_LOGINFO_PREFIX_PL | PL_LOGINFO_CODE_SATA_NCQ_FAIL_ALL_CMDS_AFTR_ERR
 * See: drivers/message/fusion/lsi/mpi_log_sas.h
 */

#define IOC_LOGINFO_SATA_NCQ_FAIL_AFTER_ERR 0x31080000

/**
 * mpi3mr_host_tag_for_scmd - Get host tag for a scmd
 * @mrioc: Adapter instance reference
 * @scmd: SCSI command reference
 *
 * Calculate the host tag based on block tag for a given scmd.
 *
 * Return: Valid host tag or MPI3MR_HOSTTAG_INVALID.
 */

static u16 mpi3mr_host_tag_for_scmd(struct mpi3mr_ioc *mrioc,
 struct scsi_cmnd *scmd)
{
 struct scmd_priv *priv = NULL;
 u32 unique_tag;
 u16 host_tag, hw_queue;

 unique_tag = blk_mq_unique_tag(scsi_cmd_to_rq(scmd));

 hw_queue = blk_mq_unique_tag_to_hwq(unique_tag);
 if (hw_queue >= mrioc->num_op_reply_q)
  return MPI3MR_HOSTTAG_INVALID;
 host_tag = blk_mq_unique_tag_to_tag(unique_tag);

 if (WARN_ON(host_tag >= mrioc->max_host_ios))
  return MPI3MR_HOSTTAG_INVALID;

 priv = scsi_cmd_priv(scmd);
 /*host_tag 0 is invalid hence incrementing by 1*/
 priv->host_tag = host_tag + 1;
 priv->scmd = scmd;
 priv->in_lld_scope = 1;
 priv->req_q_idx = hw_queue;
 priv->meta_chain_idx = -1;
 priv->chain_idx = -1;
 priv->meta_sg_valid = 0;
 return priv->host_tag;
}

/**
 * mpi3mr_scmd_from_host_tag - Get SCSI command from host tag
 * @mrioc: Adapter instance reference
 * @host_tag: Host tag
 * @qidx: Operational queue index
 *
 * Identify the block tag from the host tag and queue index and
 * retrieve associated scsi command using scsi_host_find_tag().
 *
 * Return: SCSI command reference or NULL.
 */

static struct scsi_cmnd *mpi3mr_scmd_from_host_tag(
 struct mpi3mr_ioc *mrioc, u16 host_tag, u16 qidx)
{
 struct scsi_cmnd *scmd = NULL;
 struct scmd_priv *priv = NULL;
 u32 unique_tag = host_tag - 1;

 if (WARN_ON(host_tag > mrioc->max_host_ios))
  goto out;

 unique_tag |= (qidx << BLK_MQ_UNIQUE_TAG_BITS);

 scmd = scsi_host_find_tag(mrioc->shost, unique_tag);
 if (scmd) {
  priv = scsi_cmd_priv(scmd);
  if (!priv->in_lld_scope)
   scmd = NULL;
 }
out:
 return scmd;
}

/**
 * mpi3mr_clear_scmd_priv - Cleanup SCSI command private date
 * @mrioc: Adapter instance reference
 * @scmd: SCSI command reference
 *
 * Invalidate the SCSI command private data to mark the command
 * is not in LLD scope anymore.
 *
 * Return: Nothing.
 */

static void mpi3mr_clear_scmd_priv(struct mpi3mr_ioc *mrioc,
 struct scsi_cmnd *scmd)
{
 struct scmd_priv *priv = NULL;

 priv = scsi_cmd_priv(scmd);

 if (WARN_ON(priv->in_lld_scope == 0))
  return;
 priv->host_tag = MPI3MR_HOSTTAG_INVALID;
 priv->req_q_idx = 0xFFFF;
 priv->scmd = NULL;
 priv->in_lld_scope = 0;
 priv->meta_sg_valid = 0;
 if (priv->chain_idx >= 0) {
  clear_bit(priv->chain_idx, mrioc->chain_bitmap);
  priv->chain_idx = -1;
 }
 if (priv->meta_chain_idx >= 0) {
  clear_bit(priv->meta_chain_idx, mrioc->chain_bitmap);
  priv->meta_chain_idx = -1;
 }
}

static void mpi3mr_dev_rmhs_send_tm(struct mpi3mr_ioc *mrioc, u16 handle,
 struct mpi3mr_drv_cmd *cmdparam, u8 iou_rc);
static void mpi3mr_fwevt_worker(struct work_struct *work);

/**
 * mpi3mr_fwevt_free - firmware event memory dealloctor
 * @r: k reference pointer of the firmware event
 *
 * Free firmware event memory when no reference.
 */

static void mpi3mr_fwevt_free(struct kref *r)
{
 kfree(container_of(r, struct mpi3mr_fwevt, ref_count));
}

/**
 * mpi3mr_fwevt_get - k reference incrementor
 * @fwevt: Firmware event reference
 *
 * Increment firmware event reference count.
 */

static void mpi3mr_fwevt_get(struct mpi3mr_fwevt *fwevt)
{
 kref_get(&fwevt->ref_count);
}

/**
 * mpi3mr_fwevt_put - k reference decrementor
 * @fwevt: Firmware event reference
 *
 * decrement firmware event reference count.
 */

static void mpi3mr_fwevt_put(struct mpi3mr_fwevt *fwevt)
{
 kref_put(&fwevt->ref_count, mpi3mr_fwevt_free);
}

/**
 * mpi3mr_alloc_fwevt - Allocate firmware event
 * @len: length of firmware event data to allocate
 *
 * Allocate firmware event with required length and initialize
 * the reference counter.
 *
 * Return: firmware event reference.
 */

static struct mpi3mr_fwevt *mpi3mr_alloc_fwevt(int len)
{
 struct mpi3mr_fwevt *fwevt;

 fwevt = kzalloc(sizeof(*fwevt) + len, GFP_ATOMIC);
 if (!fwevt)
  return NULL;

 kref_init(&fwevt->ref_count);
 return fwevt;
}

/**
 * mpi3mr_fwevt_add_to_list - Add firmware event to the list
 * @mrioc: Adapter instance reference
 * @fwevt: Firmware event reference
 *
 * Add the given firmware event to the firmware event list.
 *
 * Return: Nothing.
 */

static void mpi3mr_fwevt_add_to_list(struct mpi3mr_ioc *mrioc,
 struct mpi3mr_fwevt *fwevt)
{
 unsigned long flags;

 if (!mrioc->fwevt_worker_thread)
  return;

 spin_lock_irqsave(&mrioc->fwevt_lock, flags);
 /* get fwevt reference count while adding it to fwevt_list */
 mpi3mr_fwevt_get(fwevt);
 INIT_LIST_HEAD(&fwevt->list);
 list_add_tail(&fwevt->list, &mrioc->fwevt_list);
 INIT_WORK(&fwevt->work, mpi3mr_fwevt_worker);
 /* get fwevt reference count while enqueueing it to worker queue */
 mpi3mr_fwevt_get(fwevt);
 queue_work(mrioc->fwevt_worker_thread, &fwevt->work);
 spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
}

/**
 * mpi3mr_hdb_trigger_data_event - Add hdb trigger data event to
 * the list
 * @mrioc: Adapter instance reference
 * @event_data: Event data
 *
 * Add the given hdb trigger data event to the firmware event
 * list.
 *
 * Return: Nothing.
 */

void mpi3mr_hdb_trigger_data_event(struct mpi3mr_ioc *mrioc,
 struct trigger_event_data *event_data)
{
 struct mpi3mr_fwevt *fwevt;
 u16 sz = sizeof(*event_data);

 fwevt = mpi3mr_alloc_fwevt(sz);
 if (!fwevt) {
  ioc_warn(mrioc, "failed to queue hdb trigger data event\n");
  return;
 }

 fwevt->mrioc = mrioc;
 fwevt->event_id = MPI3MR_DRIVER_EVENT_PROCESS_TRIGGER;
 fwevt->send_ack = 0;
 fwevt->process_evt = 1;
 fwevt->evt_ctx = 0;
 fwevt->event_data_size = sz;
 memcpy(fwevt->event_data, event_data, sz);

 mpi3mr_fwevt_add_to_list(mrioc, fwevt);
}

/**
 * mpi3mr_fwevt_del_from_list - Delete firmware event from list
 * @mrioc: Adapter instance reference
 * @fwevt: Firmware event reference
 *
 * Delete the given firmware event from the firmware event list.
 *
 * Return: Nothing.
 */

static void mpi3mr_fwevt_del_from_list(struct mpi3mr_ioc *mrioc,
 struct mpi3mr_fwevt *fwevt)
{
 unsigned long flags;

 spin_lock_irqsave(&mrioc->fwevt_lock, flags);
 if (!list_empty(&fwevt->list)) {
  list_del_init(&fwevt->list);
  /*
 * Put fwevt reference count after
 * removing it from fwevt_list
 */

  mpi3mr_fwevt_put(fwevt);
 }
 spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
}

/**
 * mpi3mr_dequeue_fwevt - Dequeue firmware event from the list
 * @mrioc: Adapter instance reference
 *
 * Dequeue a firmware event from the firmware event list.
 *
 * Return: firmware event.
 */

static struct mpi3mr_fwevt *mpi3mr_dequeue_fwevt(
 struct mpi3mr_ioc *mrioc)
{
 unsigned long flags;
 struct mpi3mr_fwevt *fwevt = NULL;

 spin_lock_irqsave(&mrioc->fwevt_lock, flags);
 if (!list_empty(&mrioc->fwevt_list)) {
  fwevt = list_first_entry(&mrioc->fwevt_list,
      struct mpi3mr_fwevt, list);
  list_del_init(&fwevt->list);
  /*
 * Put fwevt reference count after
 * removing it from fwevt_list
 */

  mpi3mr_fwevt_put(fwevt);
 }
 spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);

 return fwevt;
}

/**
 * mpi3mr_cancel_work - cancel firmware event
 * @fwevt: fwevt object which needs to be canceled
 *
 * Return: Nothing.
 */

static void mpi3mr_cancel_work(struct mpi3mr_fwevt *fwevt)
{
 /*
 * Wait on the fwevt to complete. If this returns 1, then
 * the event was never executed.
 *
 * If it did execute, we wait for it to finish, and the put will
 * happen from mpi3mr_process_fwevt()
 */

 if (cancel_work_sync(&fwevt->work)) {
  /*
 * Put fwevt reference count after
 * dequeuing it from worker queue
 */

  mpi3mr_fwevt_put(fwevt);
  /*
 * Put fwevt reference count to neutralize
 * kref_init increment
 */

  mpi3mr_fwevt_put(fwevt);
 }
}

/**
 * mpi3mr_cleanup_fwevt_list - Cleanup firmware event list
 * @mrioc: Adapter instance reference
 *
 * Flush all pending firmware events from the firmware event
 * list.
 *
 * Return: Nothing.
 */

void mpi3mr_cleanup_fwevt_list(struct mpi3mr_ioc *mrioc)
{
 struct mpi3mr_fwevt *fwevt = NULL;

 if ((list_empty(&mrioc->fwevt_list) && !mrioc->current_event) ||
     !mrioc->fwevt_worker_thread)
  return;

 while ((fwevt = mpi3mr_dequeue_fwevt(mrioc)))
  mpi3mr_cancel_work(fwevt);

 if (mrioc->current_event) {
  fwevt = mrioc->current_event;
  /*
 * Don't call cancel_work_sync() API for the
 * fwevt work if the controller reset is
 * get called as part of processing the
 * same fwevt work (or) when worker thread is
 * waiting for device add/remove APIs to complete.
 * Otherwise we will see deadlock.
 */

  if (current_work() == &fwevt->work || fwevt->pending_at_sml) {
   fwevt->discard = 1;
   return;
  }

  mpi3mr_cancel_work(fwevt);
 }
}

/**
 * mpi3mr_queue_qd_reduction_event - Queue TG QD reduction event
 * @mrioc: Adapter instance reference
 * @tg: Throttle group information pointer
 *
 * Accessor to queue on synthetically generated driver event to
 * the event worker thread, the driver event will be used to
 * reduce the QD of all VDs in the TG from the worker thread.
 *
 * Return: None.
 */

static void mpi3mr_queue_qd_reduction_event(struct mpi3mr_ioc *mrioc,
 struct mpi3mr_throttle_group_info *tg)
{
 struct mpi3mr_fwevt *fwevt;
 u16 sz = sizeof(struct mpi3mr_throttle_group_info *);

 /*
 * If the QD reduction event is already queued due to throttle and if
 * the QD is not restored through device info change event
 * then dont queue further reduction events
 */

 if (tg->fw_qd != tg->modified_qd)
  return;

 fwevt = mpi3mr_alloc_fwevt(sz);
 if (!fwevt) {
  ioc_warn(mrioc, "failed to queue TG QD reduction event\n");
  return;
 }
 *(struct mpi3mr_throttle_group_info **)fwevt->event_data = tg;
 fwevt->mrioc = mrioc;
 fwevt->event_id = MPI3MR_DRIVER_EVENT_TG_QD_REDUCTION;
 fwevt->send_ack = 0;
 fwevt->process_evt = 1;
 fwevt->evt_ctx = 0;
 fwevt->event_data_size = sz;
 tg->modified_qd = max_t(u16, (tg->fw_qd * tg->qd_reduction) / 10, 8);

 dprint_event_bh(mrioc, "qd reduction event queued for tg_id(%d)\n",
     tg->id);
 mpi3mr_fwevt_add_to_list(mrioc, fwevt);
}

/**
 * mpi3mr_invalidate_devhandles -Invalidate device handles
 * @mrioc: Adapter instance reference
 *
 * Invalidate the device handles in the target device structures
 * . Called post reset prior to reinitializing the controller.
 *
 * Return: Nothing.
 */

void mpi3mr_invalidate_devhandles(struct mpi3mr_ioc *mrioc)
{
 struct mpi3mr_tgt_dev *tgtdev;
 struct mpi3mr_stgt_priv_data *tgt_priv;

 list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list) {
  tgtdev->dev_handle = MPI3MR_INVALID_DEV_HANDLE;
  if (tgtdev->starget && tgtdev->starget->hostdata) {
   tgt_priv = tgtdev->starget->hostdata;
   tgt_priv->dev_handle = MPI3MR_INVALID_DEV_HANDLE;
   tgt_priv->io_throttle_enabled = 0;
   tgt_priv->io_divert = 0;
   tgt_priv->throttle_group = NULL;
   tgt_priv->wslen = 0;
   if (tgtdev->host_exposed)
    atomic_set(&tgt_priv->block_io, 1);
  }
 }
}

/**
 * mpi3mr_print_scmd - print individual SCSI command
 * @rq: Block request
 * @data: Adapter instance reference
 *
 * Print the SCSI command details if it is in LLD scope.
 *
 * Return: true always.
 */

static bool mpi3mr_print_scmd(struct request *rq, void *data)
{
 struct mpi3mr_ioc *mrioc = (struct mpi3mr_ioc *)data;
 struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(rq);
 struct scmd_priv *priv = NULL;

 if (scmd) {
  priv = scsi_cmd_priv(scmd);
  if (!priv->in_lld_scope)
   goto out;

  ioc_info(mrioc, "%s :Host Tag = %d, qid = %d\n",
      __func__, priv->host_tag, priv->req_q_idx + 1);
  scsi_print_command(scmd);
 }

out:
 return(true);
}

/**
 * mpi3mr_flush_scmd - Flush individual SCSI command
 * @rq: Block request
 * @data: Adapter instance reference
 *
 * Return the SCSI command to the upper layers if it is in LLD
 * scope.
 *
 * Return: true always.
 */


static bool mpi3mr_flush_scmd(struct request *rq, void *data)
{
 struct mpi3mr_ioc *mrioc = (struct mpi3mr_ioc *)data;
 struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(rq);
 struct scmd_priv *priv = NULL;

 if (scmd) {
  priv = scsi_cmd_priv(scmd);
  if (!priv->in_lld_scope)
   goto out;

  if (priv->meta_sg_valid)
   dma_unmap_sg(&mrioc->pdev->dev, scsi_prot_sglist(scmd),
       scsi_prot_sg_count(scmd), scmd->sc_data_direction);
  mpi3mr_clear_scmd_priv(mrioc, scmd);
  scsi_dma_unmap(scmd);
  scmd->result = DID_RESET << 16;
  scsi_print_command(scmd);
  scsi_done(scmd);
  mrioc->flush_io_count++;
 }

out:
 return(true);
}

/**
 * mpi3mr_count_dev_pending - Count commands pending for a lun
 * @rq: Block request
 * @data: SCSI device reference
 *
 * This is an iterator function called for each SCSI command in
 * a host and if the command is pending in the LLD for the
 * specific device(lun) then device specific pending I/O counter
 * is updated in the device structure.
 *
 * Return: true always.
 */


static bool mpi3mr_count_dev_pending(struct request *rq, void *data)
{
 struct scsi_device *sdev = (struct scsi_device *)data;
 struct mpi3mr_sdev_priv_data *sdev_priv_data = sdev->hostdata;
 struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(rq);
 struct scmd_priv *priv;

 if (scmd) {
  priv = scsi_cmd_priv(scmd);
  if (!priv->in_lld_scope)
   goto out;
  if (scmd->device == sdev)
   sdev_priv_data->pend_count++;
 }

out:
 return true;
}

/**
 * mpi3mr_count_tgt_pending - Count commands pending for target
 * @rq: Block request
 * @data: SCSI target reference
 *
 * This is an iterator function called for each SCSI command in
 * a host and if the command is pending in the LLD for the
 * specific target then target specific pending I/O counter is
 * updated in the target structure.
 *
 * Return: true always.
 */


static bool mpi3mr_count_tgt_pending(struct request *rq, void *data)
{
 struct scsi_target *starget = (struct scsi_target *)data;
 struct mpi3mr_stgt_priv_data *stgt_priv_data = starget->hostdata;
 struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(rq);
 struct scmd_priv *priv;

 if (scmd) {
  priv = scsi_cmd_priv(scmd);
  if (!priv->in_lld_scope)
   goto out;
  if (scmd->device && (scsi_target(scmd->device) == starget))
   stgt_priv_data->pend_count++;
 }

out:
 return true;
}

/**
 * mpi3mr_flush_host_io -  Flush host I/Os
 * @mrioc: Adapter instance reference
 *
 * Flush all of the pending I/Os by calling
 * blk_mq_tagset_busy_iter() for each possible tag. This is
 * executed post controller reset
 *
 * Return: Nothing.
 */

void mpi3mr_flush_host_io(struct mpi3mr_ioc *mrioc)
{
 struct Scsi_Host *shost = mrioc->shost;

 mrioc->flush_io_count = 0;
 ioc_info(mrioc, "%s :Flushing Host I/O cmds post reset\n", __func__);
 blk_mq_tagset_busy_iter(&shost->tag_set,
     mpi3mr_flush_scmd, (void *)mrioc);
 ioc_info(mrioc, "%s :Flushed %d Host I/O cmds\n", __func__,
     mrioc->flush_io_count);
}

/**
 * mpi3mr_flush_cmds_for_unrecovered_controller - Flush all pending cmds
 * @mrioc: Adapter instance reference
 *
 * This function waits for currently running IO poll threads to
 * exit and then flushes all host I/Os and any internal pending
 * cmds. This is executed after controller is marked as
 * unrecoverable.
 *
 * Return: Nothing.
 */

void mpi3mr_flush_cmds_for_unrecovered_controller(struct mpi3mr_ioc *mrioc)
{
 struct Scsi_Host *shost = mrioc->shost;
 int i;

 if (!mrioc->unrecoverable)
  return;

 if (mrioc->op_reply_qinfo) {
  for (i = 0; i < mrioc->num_queues; i++) {
   while (atomic_read(&mrioc->op_reply_qinfo[i].in_use))
    udelay(500);
   atomic_set(&mrioc->op_reply_qinfo[i].pend_ios, 0);
  }
 }
 mrioc->flush_io_count = 0;
 blk_mq_tagset_busy_iter(&shost->tag_set,
     mpi3mr_flush_scmd, (void *)mrioc);
 mpi3mr_flush_delayed_cmd_lists(mrioc);
 mpi3mr_flush_drv_cmds(mrioc);
}

/**
 * mpi3mr_alloc_tgtdev - target device allocator
 *
 * Allocate target device instance and initialize the reference
 * count
 *
 * Return: target device instance.
 */

static struct mpi3mr_tgt_dev *mpi3mr_alloc_tgtdev(void)
{
 struct mpi3mr_tgt_dev *tgtdev;

 tgtdev = kzalloc(sizeof(*tgtdev), GFP_ATOMIC);
 if (!tgtdev)
  return NULL;
 kref_init(&tgtdev->ref_count);
 return tgtdev;
}

/**
 * mpi3mr_tgtdev_add_to_list -Add tgtdevice to the list
 * @mrioc: Adapter instance reference
 * @tgtdev: Target device
 *
 * Add the target device to the target device list
 *
 * Return: Nothing.
 */

static void mpi3mr_tgtdev_add_to_list(struct mpi3mr_ioc *mrioc,
 struct mpi3mr_tgt_dev *tgtdev)
{
 unsigned long flags;

 spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
 mpi3mr_tgtdev_get(tgtdev);
 INIT_LIST_HEAD(&tgtdev->list);
 list_add_tail(&tgtdev->list, &mrioc->tgtdev_list);
 tgtdev->state = MPI3MR_DEV_CREATED;
 spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
}

/**
 * mpi3mr_tgtdev_del_from_list -Delete tgtdevice from the list
 * @mrioc: Adapter instance reference
 * @tgtdev: Target device
 * @must_delete: Must delete the target device from the list irrespective
 * of the device state.
 *
 * Remove the target device from the target device list
 *
 * Return: Nothing.
 */

static void mpi3mr_tgtdev_del_from_list(struct mpi3mr_ioc *mrioc,
 struct mpi3mr_tgt_dev *tgtdev, bool must_delete)
{
 unsigned long flags;

 spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
 if ((tgtdev->state == MPI3MR_DEV_REMOVE_HS_STARTED) || (must_delete == true)) {
  if (!list_empty(&tgtdev->list)) {
   list_del_init(&tgtdev->list);
   tgtdev->state = MPI3MR_DEV_DELETED;
   mpi3mr_tgtdev_put(tgtdev);
  }
 }
 spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
}

/**
 * __mpi3mr_get_tgtdev_by_handle -Get tgtdev from device handle
 * @mrioc: Adapter instance reference
 * @handle: Device handle
 *
 * Accessor to retrieve target device from the device handle.
 * Non Lock version
 *
 * Return: Target device reference.
 */

static struct mpi3mr_tgt_dev  *__mpi3mr_get_tgtdev_by_handle(
 struct mpi3mr_ioc *mrioc, u16 handle)
{
 struct mpi3mr_tgt_dev *tgtdev;

 assert_spin_locked(&mrioc->tgtdev_lock);
 list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list)
  if (tgtdev->dev_handle == handle)
   goto found_tgtdev;
 return NULL;

found_tgtdev:
 mpi3mr_tgtdev_get(tgtdev);
 return tgtdev;
}

/**
 * mpi3mr_get_tgtdev_by_handle -Get tgtdev from device handle
 * @mrioc: Adapter instance reference
 * @handle: Device handle
 *
 * Accessor to retrieve target device from the device handle.
 * Lock version
 *
 * Return: Target device reference.
 */

struct mpi3mr_tgt_dev *mpi3mr_get_tgtdev_by_handle(
 struct mpi3mr_ioc *mrioc, u16 handle)
{
 struct mpi3mr_tgt_dev *tgtdev;
 unsigned long flags;

 spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
 tgtdev = __mpi3mr_get_tgtdev_by_handle(mrioc, handle);
 spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
 return tgtdev;
}

/**
 * __mpi3mr_get_tgtdev_by_perst_id -Get tgtdev from persist ID
 * @mrioc: Adapter instance reference
 * @persist_id: Persistent ID
 *
 * Accessor to retrieve target device from the Persistent ID.
 * Non Lock version
 *
 * Return: Target device reference.
 */

static struct mpi3mr_tgt_dev  *__mpi3mr_get_tgtdev_by_perst_id(
 struct mpi3mr_ioc *mrioc, u16 persist_id)
{
 struct mpi3mr_tgt_dev *tgtdev;

 assert_spin_locked(&mrioc->tgtdev_lock);
 list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list)
  if (tgtdev->perst_id == persist_id)
   goto found_tgtdev;
 return NULL;

found_tgtdev:
 mpi3mr_tgtdev_get(tgtdev);
 return tgtdev;
}

/**
 * mpi3mr_get_tgtdev_by_perst_id -Get tgtdev from persistent ID
 * @mrioc: Adapter instance reference
 * @persist_id: Persistent ID
 *
 * Accessor to retrieve target device from the Persistent ID.
 * Lock version
 *
 * Return: Target device reference.
 */

static struct mpi3mr_tgt_dev *mpi3mr_get_tgtdev_by_perst_id(
 struct mpi3mr_ioc *mrioc, u16 persist_id)
{
 struct mpi3mr_tgt_dev *tgtdev;
 unsigned long flags;

 spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
 tgtdev = __mpi3mr_get_tgtdev_by_perst_id(mrioc, persist_id);
 spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
 return tgtdev;
}

/**
 * __mpi3mr_get_tgtdev_from_tgtpriv -Get tgtdev from tgt private
 * @mrioc: Adapter instance reference
 * @tgt_priv: Target private data
 *
 * Accessor to return target device from the target private
 * data. Non Lock version
 *
 * Return: Target device reference.
 */

static struct mpi3mr_tgt_dev  *__mpi3mr_get_tgtdev_from_tgtpriv(
 struct mpi3mr_ioc *mrioc, struct mpi3mr_stgt_priv_data *tgt_priv)
{
 struct mpi3mr_tgt_dev *tgtdev;

 assert_spin_locked(&mrioc->tgtdev_lock);
 tgtdev = tgt_priv->tgt_dev;
 if (tgtdev)
  mpi3mr_tgtdev_get(tgtdev);
 return tgtdev;
}

/**
 * mpi3mr_set_io_divert_for_all_vd_in_tg -set divert for TG VDs
 * @mrioc: Adapter instance reference
 * @tg: Throttle group information pointer
 * @divert_value: 1 or 0
 *
 * Accessor to set io_divert flag for each device associated
 * with the given throttle group with the given value.
 *
 * Return: None.
 */

static void mpi3mr_set_io_divert_for_all_vd_in_tg(struct mpi3mr_ioc *mrioc,
 struct mpi3mr_throttle_group_info *tg, u8 divert_value)
{
 unsigned long flags;
 struct mpi3mr_tgt_dev *tgtdev;
 struct mpi3mr_stgt_priv_data *tgt_priv;

 spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
 list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list) {
  if (tgtdev->starget && tgtdev->starget->hostdata) {
   tgt_priv = tgtdev->starget->hostdata;
   if (tgt_priv->throttle_group == tg)
    tgt_priv->io_divert = divert_value;
  }
 }
 spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
}

/**
 * mpi3mr_print_device_event_notice - print notice related to post processing of
 * device event after controller reset.
 *
 * @mrioc: Adapter instance reference
 * @device_add: true for device add event and false for device removal event
 *
 * Return: None.
 */

void mpi3mr_print_device_event_notice(struct mpi3mr_ioc *mrioc,
 bool device_add)
{
 ioc_notice(mrioc, "Device %s was in progress before the reset and\n",
     (device_add ? "addition" : "removal"));
 ioc_notice(mrioc, "completed after reset, verify whether the exposed devices\n");
 ioc_notice(mrioc, "are matched with attached devices for correctness\n");
}

/**
 * mpi3mr_remove_tgtdev_from_host - Remove dev from upper layers
 * @mrioc: Adapter instance reference
 * @tgtdev: Target device structure
 *
 * Checks whether the device is exposed to upper layers and if it
 * is then remove the device from upper layers by calling
 * scsi_remove_target().
 *
 * Return: 0 on success, non zero on failure.
 */

void mpi3mr_remove_tgtdev_from_host(struct mpi3mr_ioc *mrioc,
 struct mpi3mr_tgt_dev *tgtdev)
{
 struct mpi3mr_stgt_priv_data *tgt_priv;

 ioc_info(mrioc, "%s :Removing handle(0x%04x), wwid(0x%016llx)\n",
     __func__, tgtdev->dev_handle, (unsigned long long)tgtdev->wwid);
 if (tgtdev->starget && tgtdev->starget->hostdata) {
  tgt_priv = tgtdev->starget->hostdata;
  atomic_set(&tgt_priv->block_io, 0);
  tgt_priv->dev_handle = MPI3MR_INVALID_DEV_HANDLE;
 }

 if (!mrioc->sas_transport_enabled || (tgtdev->dev_type !=
     MPI3_DEVICE_DEVFORM_SAS_SATA) || tgtdev->non_stl) {
  if (tgtdev->starget) {
   if (mrioc->current_event)
    mrioc->current_event->pending_at_sml = 1;
   scsi_remove_target(&tgtdev->starget->dev);
   tgtdev->host_exposed = 0;
   if (mrioc->current_event) {
    mrioc->current_event->pending_at_sml = 0;
    if (mrioc->current_event->discard) {
     mpi3mr_print_device_event_notice(mrioc,
         false);
     return;
    }
   }
  }
 } else
  mpi3mr_remove_tgtdev_from_sas_transport(mrioc, tgtdev);
 mpi3mr_global_trigger(mrioc,
     MPI3_DRIVER2_GLOBALTRIGGER_DEVICE_REMOVAL_ENABLED);

 ioc_info(mrioc, "%s :Removed handle(0x%04x), wwid(0x%016llx)\n",
     __func__, tgtdev->dev_handle, (unsigned long long)tgtdev->wwid);
}

/**
 * mpi3mr_report_tgtdev_to_host - Expose device to upper layers
 * @mrioc: Adapter instance reference
 * @perst_id: Persistent ID of the device
 *
 * Checks whether the device can be exposed to upper layers and
 * if it is not then expose the device to upper layers by
 * calling scsi_scan_target().
 *
 * Return: 0 on success, non zero on failure.
 */

static int mpi3mr_report_tgtdev_to_host(struct mpi3mr_ioc *mrioc,
 u16 perst_id)
{
 int retval = 0;
 struct mpi3mr_tgt_dev *tgtdev;

 if (mrioc->reset_in_progress || mrioc->pci_err_recovery)
  return -1;

 tgtdev = mpi3mr_get_tgtdev_by_perst_id(mrioc, perst_id);
 if (!tgtdev) {
  retval = -1;
  goto out;
 }
 if (tgtdev->is_hidden || tgtdev->host_exposed) {
  retval = -1;
  goto out;
 }
 if (!mrioc->sas_transport_enabled || (tgtdev->dev_type !=
     MPI3_DEVICE_DEVFORM_SAS_SATA) || tgtdev->non_stl){
  tgtdev->host_exposed = 1;
  if (mrioc->current_event)
   mrioc->current_event->pending_at_sml = 1;
  scsi_scan_target(&mrioc->shost->shost_gendev,
      mrioc->scsi_device_channel, tgtdev->perst_id,
      SCAN_WILD_CARD, SCSI_SCAN_INITIAL);
  if (!tgtdev->starget)
   tgtdev->host_exposed = 0;
  if (mrioc->current_event) {
   mrioc->current_event->pending_at_sml = 0;
   if (mrioc->current_event->discard) {
    mpi3mr_print_device_event_notice(mrioc, true);
    goto out;
   }
  }
  dprint_event_bh(mrioc,
      "exposed target device with handle(0x%04x), perst_id(%d)\n",
      tgtdev->dev_handle, perst_id);
  goto out;
 } else
  mpi3mr_report_tgtdev_to_sas_transport(mrioc, tgtdev);
out:
 if (tgtdev)
  mpi3mr_tgtdev_put(tgtdev);

 return retval;
}

/**
 * mpi3mr_change_queue_depth- Change QD callback handler
 * @sdev: SCSI device reference
 * @q_depth: Queue depth
 *
 * Validate and limit QD and call scsi_change_queue_depth.
 *
 * Return: return value of scsi_change_queue_depth
 */

static int mpi3mr_change_queue_depth(struct scsi_device *sdev,
 int q_depth)
{
 struct scsi_target *starget = scsi_target(sdev);
 struct Scsi_Host *shost = dev_to_shost(&starget->dev);
 int retval = 0;

 if (!sdev->tagged_supported)
  q_depth = 1;
 if (q_depth > shost->can_queue)
  q_depth = shost->can_queue;
 else if (!q_depth)
  q_depth = MPI3MR_DEFAULT_SDEV_QD;
 retval = scsi_change_queue_depth(sdev, q_depth);
 sdev->max_queue_depth = sdev->queue_depth;

 return retval;
}

static void mpi3mr_configure_nvme_dev(struct mpi3mr_tgt_dev *tgt_dev,
  struct queue_limits *lim)
{
 u8 pgsz = tgt_dev->dev_spec.pcie_inf.pgsz ? : MPI3MR_DEFAULT_PGSZEXP;

 lim->max_hw_sectors = tgt_dev->dev_spec.pcie_inf.mdts / 512;
 lim->virt_boundary_mask = (1 << pgsz) - 1;
}

static void mpi3mr_configure_tgt_dev(struct mpi3mr_tgt_dev *tgt_dev,
  struct queue_limits *lim)
{
 if (tgt_dev->dev_type == MPI3_DEVICE_DEVFORM_PCIE &&
     (tgt_dev->dev_spec.pcie_inf.dev_info &
      MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_MASK) ==
   MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_NVME_DEVICE)
  mpi3mr_configure_nvme_dev(tgt_dev, lim);
}

/**
 * mpi3mr_update_sdev - Update SCSI device information
 * @sdev: SCSI device reference
 * @data: target device reference
 *
 * This is an iterator function called for each SCSI device in a
 * target to update the target specific information into each
 * SCSI device.
 *
 * Return: Nothing.
 */

static void
mpi3mr_update_sdev(struct scsi_device *sdev, void *data)
{
 struct mpi3mr_tgt_dev *tgtdev;
 struct queue_limits lim;

 tgtdev = (struct mpi3mr_tgt_dev *)data;
 if (!tgtdev)
  return;

 mpi3mr_change_queue_depth(sdev, tgtdev->q_depth);

 lim = queue_limits_start_update(sdev->request_queue);
 mpi3mr_configure_tgt_dev(tgtdev, &lim);
 WARN_ON_ONCE(queue_limits_commit_update(sdev->request_queue, &lim));
}

/**
 * mpi3mr_refresh_tgtdevs - Refresh target device exposure
 * @mrioc: Adapter instance reference
 *
 * This is executed post controller reset to identify any
 * missing devices during reset and remove from the upper layers
 * or expose any newly detected device to the upper layers.
 *
 * Return: Nothing.
 */

static void mpi3mr_refresh_tgtdevs(struct mpi3mr_ioc *mrioc)
{
 struct mpi3mr_tgt_dev *tgtdev, *tgtdev_next;
 struct mpi3mr_stgt_priv_data *tgt_priv;

 dprint_reset(mrioc, "refresh target devices: check for removals\n");
 list_for_each_entry_safe(tgtdev, tgtdev_next, &mrioc->tgtdev_list,
     list) {
  if (((tgtdev->dev_handle == MPI3MR_INVALID_DEV_HANDLE) ||
       tgtdev->is_hidden) &&
       tgtdev->host_exposed && tgtdev->starget &&
       tgtdev->starget->hostdata) {
   tgt_priv = tgtdev->starget->hostdata;
   tgt_priv->dev_removed = 1;
   atomic_set(&tgt_priv->block_io, 0);
  }
 }

 list_for_each_entry_safe(tgtdev, tgtdev_next, &mrioc->tgtdev_list,
     list) {
  if (tgtdev->dev_handle == MPI3MR_INVALID_DEV_HANDLE) {
   dprint_reset(mrioc, "removing target device with perst_id(%d)\n",
       tgtdev->perst_id);
   if (tgtdev->host_exposed)
    mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev);
   mpi3mr_tgtdev_del_from_list(mrioc, tgtdev, true);
   mpi3mr_tgtdev_put(tgtdev);
  } else if (tgtdev->is_hidden & tgtdev->host_exposed) {
   dprint_reset(mrioc, "hiding target device with perst_id(%d)\n",
         tgtdev->perst_id);
   mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev);
  }
 }

 tgtdev = NULL;
 list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list) {
  if ((tgtdev->dev_handle != MPI3MR_INVALID_DEV_HANDLE) &&
      !tgtdev->is_hidden) {
   if (!tgtdev->host_exposed)
    mpi3mr_report_tgtdev_to_host(mrioc,
            tgtdev->perst_id);
   else if (tgtdev->starget)
    starget_for_each_device(tgtdev->starget,
       (void *)tgtdev, mpi3mr_update_sdev);
 }
 }
}

/**
 * mpi3mr_update_tgtdev - DevStatusChange evt bottomhalf
 * @mrioc: Adapter instance reference
 * @tgtdev: Target device internal structure
 * @dev_pg0: New device page0
 * @is_added: Flag to indicate the device is just added
 *
 * Update the information from the device page0 into the driver
 * cached target device structure.
 *
 * Return: Nothing.
 */

static void mpi3mr_update_tgtdev(struct mpi3mr_ioc *mrioc,
 struct mpi3mr_tgt_dev *tgtdev, struct mpi3_device_page0 *dev_pg0,
 bool is_added)
{
 u16 flags = 0;
 struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data = NULL;
 struct mpi3mr_enclosure_node *enclosure_dev = NULL;
 u8 prot_mask = 0;

 tgtdev->perst_id = le16_to_cpu(dev_pg0->persistent_id);
 tgtdev->dev_handle = le16_to_cpu(dev_pg0->dev_handle);
 tgtdev->dev_type = dev_pg0->device_form;
 tgtdev->io_unit_port = dev_pg0->io_unit_port;
 tgtdev->encl_handle = le16_to_cpu(dev_pg0->enclosure_handle);
 tgtdev->parent_handle = le16_to_cpu(dev_pg0->parent_dev_handle);
 tgtdev->slot = le16_to_cpu(dev_pg0->slot);
 tgtdev->q_depth = le16_to_cpu(dev_pg0->queue_depth);
 tgtdev->wwid = le64_to_cpu(dev_pg0->wwid);
 tgtdev->devpg0_flag = le16_to_cpu(dev_pg0->flags);

 if (tgtdev->encl_handle)
  enclosure_dev = mpi3mr_enclosure_find_by_handle(mrioc,
      tgtdev->encl_handle);
 if (enclosure_dev)
  tgtdev->enclosure_logical_id = le64_to_cpu(
      enclosure_dev->pg0.enclosure_logical_id);

 flags = tgtdev->devpg0_flag;

 tgtdev->is_hidden = (flags & MPI3_DEVICE0_FLAGS_HIDDEN);

 if (is_added == true)
  tgtdev->io_throttle_enabled =
      (flags & MPI3_DEVICE0_FLAGS_IO_THROTTLING_REQUIRED) ? 1 : 0;

 switch (flags & MPI3_DEVICE0_FLAGS_MAX_WRITE_SAME_MASK) {
 case MPI3_DEVICE0_FLAGS_MAX_WRITE_SAME_256_LB:
  tgtdev->wslen = MPI3MR_WRITE_SAME_MAX_LEN_256_BLKS;
  break;
 case MPI3_DEVICE0_FLAGS_MAX_WRITE_SAME_2048_LB:
  tgtdev->wslen = MPI3MR_WRITE_SAME_MAX_LEN_2048_BLKS;
  break;
 case MPI3_DEVICE0_FLAGS_MAX_WRITE_SAME_NO_LIMIT:
 default:
  tgtdev->wslen = 0;
  break;
 }

 if (tgtdev->starget && tgtdev->starget->hostdata) {
  scsi_tgt_priv_data = (struct mpi3mr_stgt_priv_data *)
      tgtdev->starget->hostdata;
  scsi_tgt_priv_data->perst_id = tgtdev->perst_id;
  scsi_tgt_priv_data->dev_handle = tgtdev->dev_handle;
  scsi_tgt_priv_data->dev_type = tgtdev->dev_type;
  scsi_tgt_priv_data->io_throttle_enabled =
      tgtdev->io_throttle_enabled;
  if (is_added == true)
   atomic_set(&scsi_tgt_priv_data->block_io, 0);
  scsi_tgt_priv_data->wslen = tgtdev->wslen;
 }

 switch (dev_pg0->access_status) {
 case MPI3_DEVICE0_ASTATUS_NO_ERRORS:
 case MPI3_DEVICE0_ASTATUS_PREPARE:
 case MPI3_DEVICE0_ASTATUS_NEEDS_INITIALIZATION:
 case MPI3_DEVICE0_ASTATUS_DEVICE_MISSING_DELAY:
  break;
 default:
  tgtdev->is_hidden = 1;
  break;
 }

 switch (tgtdev->dev_type) {
 case MPI3_DEVICE_DEVFORM_SAS_SATA:
 {
  struct mpi3_device0_sas_sata_format *sasinf =
      &dev_pg0->device_specific.sas_sata_format;
  u16 dev_info = le16_to_cpu(sasinf->device_info);

  tgtdev->dev_spec.sas_sata_inf.dev_info = dev_info;
  tgtdev->dev_spec.sas_sata_inf.sas_address =
      le64_to_cpu(sasinf->sas_address);
  tgtdev->dev_spec.sas_sata_inf.phy_id = sasinf->phy_num;
  tgtdev->dev_spec.sas_sata_inf.attached_phy_id =
      sasinf->attached_phy_identifier;
  if ((dev_info & MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_MASK) !=
      MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_END_DEVICE)
   tgtdev->is_hidden = 1;
  else if (!(dev_info & (MPI3_SAS_DEVICE_INFO_STP_SATA_TARGET |
      MPI3_SAS_DEVICE_INFO_SSP_TARGET)))
   tgtdev->is_hidden = 1;

  if (((tgtdev->devpg0_flag &
      MPI3_DEVICE0_FLAGS_ATT_METHOD_DIR_ATTACHED)
      && (tgtdev->devpg0_flag &
      MPI3_DEVICE0_FLAGS_ATT_METHOD_VIRTUAL)) ||
      (tgtdev->parent_handle == 0xFFFF))
   tgtdev->non_stl = 1;
  if (tgtdev->dev_spec.sas_sata_inf.hba_port)
   tgtdev->dev_spec.sas_sata_inf.hba_port->port_id =
       dev_pg0->io_unit_port;
  break;
 }
 case MPI3_DEVICE_DEVFORM_PCIE:
 {
  struct mpi3_device0_pcie_format *pcieinf =
      &dev_pg0->device_specific.pcie_format;
  u16 dev_info = le16_to_cpu(pcieinf->device_info);

  tgtdev->dev_spec.pcie_inf.dev_info = dev_info;
  tgtdev->dev_spec.pcie_inf.capb =
      le32_to_cpu(pcieinf->capabilities);
  tgtdev->dev_spec.pcie_inf.mdts = MPI3MR_DEFAULT_MDTS;
  /* 2^12 = 4096 */
  tgtdev->dev_spec.pcie_inf.pgsz = 12;
  if (dev_pg0->access_status == MPI3_DEVICE0_ASTATUS_NO_ERRORS) {
   tgtdev->dev_spec.pcie_inf.mdts =
       le32_to_cpu(pcieinf->maximum_data_transfer_size);
   tgtdev->dev_spec.pcie_inf.pgsz = pcieinf->page_size;
   tgtdev->dev_spec.pcie_inf.reset_to =
       max_t(u8, pcieinf->controller_reset_to,
        MPI3MR_INTADMCMD_TIMEOUT);
   tgtdev->dev_spec.pcie_inf.abort_to =
       max_t(u8, pcieinf->nvme_abort_to,
       MPI3MR_INTADMCMD_TIMEOUT);
  }
  if (tgtdev->dev_spec.pcie_inf.mdts > (1024 * 1024))
   tgtdev->dev_spec.pcie_inf.mdts = (1024 * 1024);
  if (((dev_info & MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_MASK) !=
      MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_NVME_DEVICE) &&
      ((dev_info & MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_MASK) !=
      MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_SCSI_DEVICE))
   tgtdev->is_hidden = 1;
  tgtdev->non_stl = 1;
  if (!mrioc->shost)
   break;
  prot_mask = scsi_host_get_prot(mrioc->shost);
  if (prot_mask & SHOST_DIX_TYPE0_PROTECTION) {
   scsi_host_set_prot(mrioc->shost, prot_mask & 0x77);
   ioc_info(mrioc,
       "%s : Disabling DIX0 prot capability\n", __func__);
   ioc_info(mrioc,
       "because HBA does not support DIX0 operation on NVME drives\n");
  }
  break;
 }
 case MPI3_DEVICE_DEVFORM_VD:
 {
  struct mpi3_device0_vd_format *vdinf =
      &dev_pg0->device_specific.vd_format;
  struct mpi3mr_throttle_group_info *tg = NULL;
  u16 vdinf_io_throttle_group =
      le16_to_cpu(vdinf->io_throttle_group);

  tgtdev->dev_spec.vd_inf.state = vdinf->vd_state;
  if (vdinf->vd_state == MPI3_DEVICE0_VD_STATE_OFFLINE)
   tgtdev->is_hidden = 1;
  tgtdev->non_stl = 1;
  tgtdev->dev_spec.vd_inf.tg_id = vdinf_io_throttle_group;
  tgtdev->dev_spec.vd_inf.tg_high =
      le16_to_cpu(vdinf->io_throttle_group_high) * 2048;
  tgtdev->dev_spec.vd_inf.tg_low =
      le16_to_cpu(vdinf->io_throttle_group_low) * 2048;
  if (vdinf_io_throttle_group < mrioc->num_io_throttle_group) {
   tg = mrioc->throttle_groups + vdinf_io_throttle_group;
   tg->id = vdinf_io_throttle_group;
   tg->high = tgtdev->dev_spec.vd_inf.tg_high;
   tg->low = tgtdev->dev_spec.vd_inf.tg_low;
   tg->qd_reduction =
       tgtdev->dev_spec.vd_inf.tg_qd_reduction;
   if (is_added == true)
    tg->fw_qd = tgtdev->q_depth;
   tg->modified_qd = tgtdev->q_depth;
  }
  tgtdev->dev_spec.vd_inf.tg = tg;
  if (scsi_tgt_priv_data)
   scsi_tgt_priv_data->throttle_group = tg;
  break;
 }
 default:
  break;
 }
}

/**
 * mpi3mr_devstatuschg_evt_bh - DevStatusChange evt bottomhalf
 * @mrioc: Adapter instance reference
 * @fwevt: Firmware event information.
 *
 * Process Device status Change event and based on device's new
 * information, either expose the device to the upper layers, or
 * remove the device from upper layers.
 *
 * Return: Nothing.
 */

static void mpi3mr_devstatuschg_evt_bh(struct mpi3mr_ioc *mrioc,
 struct mpi3mr_fwevt *fwevt)
{
 u16 dev_handle = 0;
 u8 uhide = 0, delete = 0, cleanup = 0;
 struct mpi3mr_tgt_dev *tgtdev = NULL;
 struct mpi3_event_data_device_status_change *evtdata =
     (struct mpi3_event_data_device_status_change *)fwevt->event_data;

 dev_handle = le16_to_cpu(evtdata->dev_handle);
 dprint_event_bh(mrioc,
     "processing device status change event bottom half for handle(0x%04x), rc(0x%02x)\n",
     dev_handle, evtdata->reason_code);
 switch (evtdata->reason_code) {
 case MPI3_EVENT_DEV_STAT_RC_HIDDEN:
  delete = 1;
  break;
 case MPI3_EVENT_DEV_STAT_RC_NOT_HIDDEN:
  uhide = 1;
  break;
 case MPI3_EVENT_DEV_STAT_RC_VD_NOT_RESPONDING:
  delete = 1;
  cleanup = 1;
  break;
 default:
  ioc_info(mrioc, "%s :Unhandled reason code(0x%x)\n", __func__,
      evtdata->reason_code);
  break;
 }

 tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, dev_handle);
 if (!tgtdev) {
  dprint_event_bh(mrioc,
      "processing device status change event bottom half,\n"
      "cannot identify target device for handle(0x%04x), rc(0x%02x)\n",
      dev_handle, evtdata->reason_code);
  goto out;
 }
 if (uhide) {
  tgtdev->is_hidden = 0;
  if (!tgtdev->host_exposed)
   mpi3mr_report_tgtdev_to_host(mrioc, tgtdev->perst_id);
 }

 if (delete)
  mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev);

 if (cleanup) {
  mpi3mr_tgtdev_del_from_list(mrioc, tgtdev, false);
  mpi3mr_tgtdev_put(tgtdev);
 }

out:
 if (tgtdev)
  mpi3mr_tgtdev_put(tgtdev);
}

/**
 * mpi3mr_devinfochg_evt_bh - DeviceInfoChange evt bottomhalf
 * @mrioc: Adapter instance reference
 * @dev_pg0: New device page0
 *
 * Process Device Info Change event and based on device's new
 * information, either expose the device to the upper layers, or
 * remove the device from upper layers or update the details of
 * the device.
 *
 * Return: Nothing.
 */

static void mpi3mr_devinfochg_evt_bh(struct mpi3mr_ioc *mrioc,
 struct mpi3_device_page0 *dev_pg0)
{
 struct mpi3mr_tgt_dev *tgtdev = NULL;
 u16 dev_handle = 0, perst_id = 0;

 perst_id = le16_to_cpu(dev_pg0->persistent_id);
 dev_handle = le16_to_cpu(dev_pg0->dev_handle);
 dprint_event_bh(mrioc,
     "processing device info change event bottom half for handle(0x%04x), perst_id(%d)\n",
     dev_handle, perst_id);
 tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, dev_handle);
 if (!tgtdev) {
  dprint_event_bh(mrioc,
      "cannot identify target device for device info\n"
      "change event handle(0x%04x), perst_id(%d)\n",
      dev_handle, perst_id);
  goto out;
 }
 mpi3mr_update_tgtdev(mrioc, tgtdev, dev_pg0, false);
 if (!tgtdev->is_hidden && !tgtdev->host_exposed)
  mpi3mr_report_tgtdev_to_host(mrioc, perst_id);
 if (tgtdev->is_hidden && tgtdev->host_exposed)
  mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev);
 if (!tgtdev->is_hidden && tgtdev->host_exposed && tgtdev->starget)
  starget_for_each_device(tgtdev->starget, (void *)tgtdev,
      mpi3mr_update_sdev);
out:
 if (tgtdev)
  mpi3mr_tgtdev_put(tgtdev);
}

/**
 * mpi3mr_free_enclosure_list - release enclosures
 * @mrioc: Adapter instance reference
 *
 * Free memory allocated during encloure add.
 *
 * Return nothing.
 */

void mpi3mr_free_enclosure_list(struct mpi3mr_ioc *mrioc)
{
 struct mpi3mr_enclosure_node *enclosure_dev, *enclosure_dev_next;

 list_for_each_entry_safe(enclosure_dev,
     enclosure_dev_next, &mrioc->enclosure_list, list) {
  list_del(&enclosure_dev->list);
  kfree(enclosure_dev);
 }
}

/**
 * mpi3mr_enclosure_find_by_handle - enclosure search by handle
 * @mrioc: Adapter instance reference
 * @handle: Firmware device handle of the enclosure
 *
 * This searches for enclosure device based on handle, then returns the
 * enclosure object.
 *
 * Return: Enclosure object reference or NULL
 */

struct mpi3mr_enclosure_node *mpi3mr_enclosure_find_by_handle(
 struct mpi3mr_ioc *mrioc, u16 handle)
{
 struct mpi3mr_enclosure_node *enclosure_dev, *r = NULL;

 list_for_each_entry(enclosure_dev, &mrioc->enclosure_list, list) {
  if (le16_to_cpu(enclosure_dev->pg0.enclosure_handle) != handle)
   continue;
  r = enclosure_dev;
  goto out;
 }
out:
 return r;
}

/**
 * mpi3mr_process_trigger_data_event_bh - Process trigger event
 * data
 * @mrioc: Adapter instance reference
 * @event_data: Event data
 *
 * This function releases diage buffers or issues diag fault
 * based on trigger conditions
 *
 * Return: Nothing
 */

static void mpi3mr_process_trigger_data_event_bh(struct mpi3mr_ioc *mrioc,
 struct trigger_event_data *event_data)
{
 struct diag_buffer_desc *trace_hdb = event_data->trace_hdb;
 struct diag_buffer_desc *fw_hdb = event_data->fw_hdb;
 unsigned long flags;
 int retval = 0;
 u8 trigger_type = event_data->trigger_type;
 union mpi3mr_trigger_data *trigger_data =
  &event_data->trigger_specific_data;

 if (event_data->snapdump)  {
  if (trace_hdb)
   mpi3mr_set_trigger_data_in_hdb(trace_hdb, trigger_type,
       trigger_data, 1);
  if (fw_hdb)
   mpi3mr_set_trigger_data_in_hdb(fw_hdb, trigger_type,
       trigger_data, 1);
  mpi3mr_soft_reset_handler(mrioc,
       MPI3MR_RESET_FROM_TRIGGER, 1);
  return;
 }

 if (trace_hdb) {
  retval = mpi3mr_issue_diag_buf_release(mrioc, trace_hdb);
  if (!retval) {
   mpi3mr_set_trigger_data_in_hdb(trace_hdb, trigger_type,
       trigger_data, 1);
  }
  spin_lock_irqsave(&mrioc->trigger_lock, flags);
  mrioc->trace_release_trigger_active = false;
  spin_unlock_irqrestore(&mrioc->trigger_lock, flags);
 }
 if (fw_hdb) {
  retval = mpi3mr_issue_diag_buf_release(mrioc, fw_hdb);
  if (!retval) {
   mpi3mr_set_trigger_data_in_hdb(fw_hdb, trigger_type,
      trigger_data, 1);
  }
  spin_lock_irqsave(&mrioc->trigger_lock, flags);
  mrioc->fw_release_trigger_active = false;
  spin_unlock_irqrestore(&mrioc->trigger_lock, flags);
 }
}

/**
 * mpi3mr_encldev_add_chg_evt_debug - debug for enclosure event
 * @mrioc: Adapter instance reference
 * @encl_pg0: Enclosure page 0.
 * @is_added: Added event or not
 *
 * Return nothing.
 */

static void mpi3mr_encldev_add_chg_evt_debug(struct mpi3mr_ioc *mrioc,
 struct mpi3_enclosure_page0 *encl_pg0, u8 is_added)
{
 char *reason_str = NULL;

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

 if (is_added)
  reason_str = "enclosure added";
 else
  reason_str = "enclosure dev status changed";

 ioc_info(mrioc,
     "%s: handle(0x%04x), enclosure logical id(0x%016llx)\n",
     reason_str, le16_to_cpu(encl_pg0->enclosure_handle),
     (unsigned long long)le64_to_cpu(encl_pg0->enclosure_logical_id));
 ioc_info(mrioc,
     "number of slots(%d), port(%d), flags(0x%04x), present(%d)\n",
     le16_to_cpu(encl_pg0->num_slots), encl_pg0->io_unit_port,
     le16_to_cpu(encl_pg0->flags),
     ((le16_to_cpu(encl_pg0->flags) &
       MPI3_ENCLS0_FLAGS_ENCL_DEV_PRESENT_MASK) >> 4));
}

/**
 * mpi3mr_encldev_add_chg_evt_bh - Enclosure evt bottomhalf
 * @mrioc: Adapter instance reference
 * @fwevt: Firmware event reference
 *
 * Prints information about the Enclosure device status or
 * Enclosure add events if logging is enabled and add or remove
 * the enclosure from the controller's internal list of
 * enclosures.
 *
 * Return: Nothing.
 */

static void mpi3mr_encldev_add_chg_evt_bh(struct mpi3mr_ioc *mrioc,
 struct mpi3mr_fwevt *fwevt)
{
 struct mpi3mr_enclosure_node *enclosure_dev = NULL;
 struct mpi3_enclosure_page0 *encl_pg0;
 u16 encl_handle;
 u8 added, present;

 encl_pg0 = (struct mpi3_enclosure_page0 *) fwevt->event_data;
 added = (fwevt->event_id == MPI3_EVENT_ENCL_DEVICE_ADDED) ? 1 : 0;
 mpi3mr_encldev_add_chg_evt_debug(mrioc, encl_pg0, added);


 encl_handle = le16_to_cpu(encl_pg0->enclosure_handle);
 present = ((le16_to_cpu(encl_pg0->flags) &
       MPI3_ENCLS0_FLAGS_ENCL_DEV_PRESENT_MASK) >> 4);

 if (encl_handle)
  enclosure_dev = mpi3mr_enclosure_find_by_handle(mrioc,
      encl_handle);
 if (!enclosure_dev && present) {
  enclosure_dev =
   kzalloc(sizeof(struct mpi3mr_enclosure_node),
       GFP_KERNEL);
  if (!enclosure_dev)
   return;
  list_add_tail(&enclosure_dev->list,
      &mrioc->enclosure_list);
 }
 if (enclosure_dev) {
  if (!present) {
   list_del(&enclosure_dev->list);
   kfree(enclosure_dev);
  } else
   memcpy(&enclosure_dev->pg0, encl_pg0,
       sizeof(enclosure_dev->pg0));

 }
}

/**
 * mpi3mr_sastopochg_evt_debug - SASTopoChange details
 * @mrioc: Adapter instance reference
 * @event_data: SAS topology change list event data
 *
 * Prints information about the SAS topology change event.
 *
 * Return: Nothing.
 */

static void
mpi3mr_sastopochg_evt_debug(struct mpi3mr_ioc *mrioc,
 struct mpi3_event_data_sas_topology_change_list *event_data)
{
 int i;
 u16 handle;
 u8 reason_code, phy_number;
 char *status_str = NULL;
 u8 link_rate, prev_link_rate;

 switch (event_data->exp_status) {
 case MPI3_EVENT_SAS_TOPO_ES_NOT_RESPONDING:
  status_str = "remove";
  break;
 case MPI3_EVENT_SAS_TOPO_ES_RESPONDING:
  status_str =  "responding";
  break;
 case MPI3_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING:
  status_str = "remove delay";
  break;
 case MPI3_EVENT_SAS_TOPO_ES_NO_EXPANDER:
  status_str = "direct attached";
  break;
 default:
  status_str = "unknown status";
  break;
 }
 ioc_info(mrioc, "%s :sas topology change: (%s)\n",
     __func__, status_str);
 ioc_info(mrioc,
     "%s :\texpander_handle(0x%04x), port(%d), enclosure_handle(0x%04x) start_phy(%02d), num_entries(%d)\n",
     __func__, le16_to_cpu(event_data->expander_dev_handle),
     event_data->io_unit_port,
     le16_to_cpu(event_data->enclosure_handle),
     event_data->start_phy_num, event_data->num_entries);
 for (i = 0; i < event_data->num_entries; i++) {
  handle = le16_to_cpu(event_data->phy_entry[i].attached_dev_handle);
  if (!handle)
   continue;
  phy_number = event_data->start_phy_num + i;
  reason_code = event_data->phy_entry[i].status &
      MPI3_EVENT_SAS_TOPO_PHY_RC_MASK;
  switch (reason_code) {
  case MPI3_EVENT_SAS_TOPO_PHY_RC_TARG_NOT_RESPONDING:
   status_str = "target remove";
   break;
  case MPI3_EVENT_SAS_TOPO_PHY_RC_DELAY_NOT_RESPONDING:
   status_str = "delay target remove";
   break;
  case MPI3_EVENT_SAS_TOPO_PHY_RC_PHY_CHANGED:
   status_str = "link status change";
   break;
  case MPI3_EVENT_SAS_TOPO_PHY_RC_NO_CHANGE:
   status_str = "link status no change";
   break;
  case MPI3_EVENT_SAS_TOPO_PHY_RC_RESPONDING:
   status_str = "target responding";
   break;
  default:
   status_str = "unknown";
   break;
  }
  link_rate = event_data->phy_entry[i].link_rate >> 4;
  prev_link_rate = event_data->phy_entry[i].link_rate & 0xF;
  ioc_info(mrioc,
      "%s :\tphy(%02d), attached_handle(0x%04x): %s: link rate: new(0x%02x), old(0x%02x)\n",
      __func__, phy_number, handle, status_str, link_rate,
      prev_link_rate);
 }
}

/**
 * mpi3mr_sastopochg_evt_bh - SASTopologyChange evt bottomhalf
 * @mrioc: Adapter instance reference
 * @fwevt: Firmware event reference
 *
 * Prints information about the SAS topology change event and
 * for "not responding" event code, removes the device from the
 * upper layers.
 *
 * Return: Nothing.
 */

static void mpi3mr_sastopochg_evt_bh(struct mpi3mr_ioc *mrioc,
 struct mpi3mr_fwevt *fwevt)
{
 struct mpi3_event_data_sas_topology_change_list *event_data =
     (struct mpi3_event_data_sas_topology_change_list *)fwevt->event_data;
 int i;
 u16 handle;
 u8 reason_code;
 u64 exp_sas_address = 0, parent_sas_address = 0;
 struct mpi3mr_hba_port *hba_port = NULL;
 struct mpi3mr_tgt_dev *tgtdev = NULL;
 struct mpi3mr_sas_node *sas_expander = NULL;
 unsigned long flags;
 u8 link_rate, prev_link_rate, parent_phy_number;

 mpi3mr_sastopochg_evt_debug(mrioc, event_data);
 if (mrioc->sas_transport_enabled) {
  hba_port = mpi3mr_get_hba_port_by_id(mrioc,
      event_data->io_unit_port);
  if (le16_to_cpu(event_data->expander_dev_handle)) {
   spin_lock_irqsave(&mrioc->sas_node_lock, flags);
   sas_expander = __mpi3mr_expander_find_by_handle(mrioc,
       le16_to_cpu(event_data->expander_dev_handle));
   if (sas_expander) {
    exp_sas_address = sas_expander->sas_address;
    hba_port = sas_expander->hba_port;
   }
   spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
   parent_sas_address = exp_sas_address;
  } else
   parent_sas_address = mrioc->sas_hba.sas_address;
 }

 for (i = 0; i < event_data->num_entries; i++) {
  if (fwevt->discard)
   return;
  handle = le16_to_cpu(event_data->phy_entry[i].attached_dev_handle);
  if (!handle)
   continue;
  tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, handle);
  if (!tgtdev)
   continue;

  reason_code = event_data->phy_entry[i].status &
      MPI3_EVENT_SAS_TOPO_PHY_RC_MASK;

  switch (reason_code) {
  case MPI3_EVENT_SAS_TOPO_PHY_RC_TARG_NOT_RESPONDING:
   if (tgtdev->host_exposed)
    mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev);
   mpi3mr_tgtdev_del_from_list(mrioc, tgtdev, false);
   mpi3mr_tgtdev_put(tgtdev);
   break;
  case MPI3_EVENT_SAS_TOPO_PHY_RC_RESPONDING:
  case MPI3_EVENT_SAS_TOPO_PHY_RC_PHY_CHANGED:
  case MPI3_EVENT_SAS_TOPO_PHY_RC_NO_CHANGE:
  {
   if (!mrioc->sas_transport_enabled || tgtdev->non_stl
       || tgtdev->is_hidden)
    break;
   link_rate = event_data->phy_entry[i].link_rate >> 4;
   prev_link_rate = event_data->phy_entry[i].link_rate & 0xF;
   if (link_rate == prev_link_rate)
    break;
   if (!parent_sas_address)
    break;
   parent_phy_number = event_data->start_phy_num + i;
   mpi3mr_update_links(mrioc, parent_sas_address, handle,
       parent_phy_number, link_rate, hba_port);
   break;
  }
  default:
   break;
  }
  if (tgtdev)
   mpi3mr_tgtdev_put(tgtdev);
 }

 if (mrioc->sas_transport_enabled && (event_data->exp_status ==
     MPI3_EVENT_SAS_TOPO_ES_NOT_RESPONDING)) {
  if (sas_expander)
   mpi3mr_expander_remove(mrioc, exp_sas_address,
       hba_port);
 }
}

/**
 * mpi3mr_pcietopochg_evt_debug - PCIeTopoChange details
 * @mrioc: Adapter instance reference
 * @event_data: PCIe topology change list event data
 *
 * Prints information about the PCIe topology change event.
 *
 * Return: Nothing.
 */

static void
mpi3mr_pcietopochg_evt_debug(struct mpi3mr_ioc *mrioc,
 struct mpi3_event_data_pcie_topology_change_list *event_data)
{
 int i;
 u16 handle;
 u16 reason_code;
 u8 port_number;
 char *status_str = NULL;
 u8 link_rate, prev_link_rate;

 switch (event_data->switch_status) {
 case MPI3_EVENT_PCIE_TOPO_SS_NOT_RESPONDING:
  status_str = "remove";
  break;
 case MPI3_EVENT_PCIE_TOPO_SS_RESPONDING:
  status_str =  "responding";
  break;
 case MPI3_EVENT_PCIE_TOPO_SS_DELAY_NOT_RESPONDING:
  status_str = "remove delay";
  break;
 case MPI3_EVENT_PCIE_TOPO_SS_NO_PCIE_SWITCH:
  status_str = "direct attached";
  break;
 default:
  status_str = "unknown status";
  break;
 }
 ioc_info(mrioc, "%s :pcie topology change: (%s)\n",
     __func__, status_str);
 ioc_info(mrioc,
     "%s :\tswitch_handle(0x%04x), enclosure_handle(0x%04x) start_port(%02d), num_entries(%d)\n",
     __func__, le16_to_cpu(event_data->switch_dev_handle),
     le16_to_cpu(event_data->enclosure_handle),
     event_data->start_port_num, event_data->num_entries);
 for (i = 0; i < event_data->num_entries; i++) {
  handle =
      le16_to_cpu(event_data->port_entry[i].attached_dev_handle);
  if (!handle)
   continue;
  port_number = event_data->start_port_num + i;
  reason_code = event_data->port_entry[i].port_status;
  switch (reason_code) {
  case MPI3_EVENT_PCIE_TOPO_PS_NOT_RESPONDING:
   status_str = "target remove";
   break;
  case MPI3_EVENT_PCIE_TOPO_PS_DELAY_NOT_RESPONDING:
   status_str = "delay target remove";
   break;
  case MPI3_EVENT_PCIE_TOPO_PS_PORT_CHANGED:
   status_str = "link status change";
   break;
  case MPI3_EVENT_PCIE_TOPO_PS_NO_CHANGE:
   status_str = "link status no change";
   break;
  case MPI3_EVENT_PCIE_TOPO_PS_RESPONDING:
   status_str = "target responding";
   break;
  default:
   status_str = "unknown";
   break;
  }
  link_rate = event_data->port_entry[i].current_port_info &
      MPI3_EVENT_PCIE_TOPO_PI_RATE_MASK;
  prev_link_rate = event_data->port_entry[i].previous_port_info &
      MPI3_EVENT_PCIE_TOPO_PI_RATE_MASK;
  ioc_info(mrioc,
      "%s :\tport(%02d), attached_handle(0x%04x): %s: link rate: new(0x%02x), old(0x%02x)\n",
      __func__, port_number, handle, status_str, link_rate,
      prev_link_rate);
 }
}

/**
 * mpi3mr_pcietopochg_evt_bh - PCIeTopologyChange evt bottomhalf
 * @mrioc: Adapter instance reference
 * @fwevt: Firmware event reference
 *
 * Prints information about the PCIe topology change event and
 * for "not responding" event code, removes the device from the
 * upper layers.
 *
 * Return: Nothing.
 */

static void mpi3mr_pcietopochg_evt_bh(struct mpi3mr_ioc *mrioc,
 struct mpi3mr_fwevt *fwevt)
{
 struct mpi3_event_data_pcie_topology_change_list *event_data =
     (struct mpi3_event_data_pcie_topology_change_list *)fwevt->event_data;
 int i;
 u16 handle;
 u8 reason_code;
 struct mpi3mr_tgt_dev *tgtdev = NULL;

 mpi3mr_pcietopochg_evt_debug(mrioc, event_data);

 for (i = 0; i < event_data->num_entries; i++) {
  if (fwevt->discard)
   return;
  handle =
      le16_to_cpu(event_data->port_entry[i].attached_dev_handle);
  if (!handle)
   continue;
  tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, handle);
  if (!tgtdev)
   continue;

  reason_code = event_data->port_entry[i].port_status;

  switch (reason_code) {
  case MPI3_EVENT_PCIE_TOPO_PS_NOT_RESPONDING:
   if (tgtdev->host_exposed)
    mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev);
   mpi3mr_tgtdev_del_from_list(mrioc, tgtdev, false);
   mpi3mr_tgtdev_put(tgtdev);
   break;
  default:
   break;
  }
  if (tgtdev)
   mpi3mr_tgtdev_put(tgtdev);
 }
}

/**
 * mpi3mr_logdata_evt_bh -  Log data event bottomhalf
 * @mrioc: Adapter instance reference
 * @fwevt: Firmware event reference
 *
 * Extracts the event data and calls application interfacing
 * function to process the event further.
 *
 * Return: Nothing.
 */

static void mpi3mr_logdata_evt_bh(struct mpi3mr_ioc *mrioc,
 struct mpi3mr_fwevt *fwevt)
{
 mpi3mr_app_save_logdata(mrioc, fwevt->event_data,
     fwevt->event_data_size);
}

/**
 * mpi3mr_update_sdev_qd - Update SCSI device queue depath
 * @sdev: SCSI device reference
 * @data: Queue depth reference
 *
 * This is an iterator function called for each SCSI device in a
 * target to update the QD of each SCSI device.
 *
 * Return: Nothing.
 */

static void mpi3mr_update_sdev_qd(struct scsi_device *sdev, void *data)
{
 u16 *q_depth = (u16 *)data;

 scsi_change_queue_depth(sdev, (int)*q_depth);
 sdev->max_queue_depth = sdev->queue_depth;
}

/**
 * mpi3mr_set_qd_for_all_vd_in_tg -set QD for TG VDs
 * @mrioc: Adapter instance reference
 * @tg: Throttle group information pointer
 *
 * Accessor to reduce QD for each device associated with the
 * given throttle group.
 *
 * Return: None.
 */

static void mpi3mr_set_qd_for_all_vd_in_tg(struct mpi3mr_ioc *mrioc,
 struct mpi3mr_throttle_group_info *tg)
{
 unsigned long flags;
 struct mpi3mr_tgt_dev *tgtdev;
 struct mpi3mr_stgt_priv_data *tgt_priv;


 spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
 list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list) {
  if (tgtdev->starget && tgtdev->starget->hostdata) {
   tgt_priv = tgtdev->starget->hostdata;
   if (tgt_priv->throttle_group == tg) {
    dprint_event_bh(mrioc,
        "updating qd due to throttling for persist_id(%d) original_qd(%d), reduced_qd (%d)\n",
        tgt_priv->perst_id, tgtdev->q_depth,
        tg->modified_qd);
    starget_for_each_device(tgtdev->starget,
        (void *)&tg->modified_qd,
        mpi3mr_update_sdev_qd);
   }
  }
 }
 spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
}

/**
 * mpi3mr_fwevt_bh - Firmware event bottomhalf handler
 * @mrioc: Adapter instance reference
 * @fwevt: Firmware event reference
 *
 * Identifies the firmware event and calls corresponding bottomg
 * half handler and sends event acknowledgment if required.
 *
 * Return: Nothing.
 */

static void mpi3mr_fwevt_bh(struct mpi3mr_ioc *mrioc,
 struct mpi3mr_fwevt *fwevt)
{
 struct mpi3_device_page0 *dev_pg0 = NULL;
 u16 perst_id, handle, dev_info;
 struct mpi3_device0_sas_sata_format *sasinf = NULL;
 unsigned int timeout;

 mpi3mr_fwevt_del_from_list(mrioc, fwevt);
 mrioc->current_event = fwevt;

 if (mrioc->stop_drv_processing) {
  dprint_event_bh(mrioc, "ignoring event(0x%02x) in the bottom half handler\n"
    "due to stop_drv_processing\n", fwevt->event_id);
  goto out;
 }

 if (mrioc->unrecoverable) {
  dprint_event_bh(mrioc,
      "ignoring event(0x%02x) in bottom half handler due to unrecoverable controller\n",
      fwevt->event_id);
  goto out;
 }

 if (!fwevt->process_evt)
  goto evt_ack;

 dprint_event_bh(mrioc, "processing event(0x%02x) -(0x%08x) in the bottom half handler\n",
   fwevt->event_id, fwevt->evt_ctx);

 switch (fwevt->event_id) {
 case MPI3_EVENT_DEVICE_ADDED:
 {
  dev_pg0 = (struct mpi3_device_page0 *)fwevt->event_data;
  perst_id = le16_to_cpu(dev_pg0->persistent_id);
  handle = le16_to_cpu(dev_pg0->dev_handle);
  if (perst_id != MPI3_DEVICE0_PERSISTENTID_INVALID)
   mpi3mr_report_tgtdev_to_host(mrioc, perst_id);
  else if (mrioc->sas_transport_enabled &&
      (dev_pg0->device_form == MPI3_DEVICE_DEVFORM_SAS_SATA)) {
   sasinf = &dev_pg0->device_specific.sas_sata_format;
   dev_info = le16_to_cpu(sasinf->device_info);
   if (!mrioc->sas_hba.num_phys)
    mpi3mr_sas_host_add(mrioc);
   else
    mpi3mr_sas_host_refresh(mrioc);

   if (mpi3mr_is_expander_device(dev_info))
    mpi3mr_expander_add(mrioc, handle);
  }
  break;
 }
 case MPI3_EVENT_DEVICE_INFO_CHANGED:
 {
  dev_pg0 = (struct mpi3_device_page0 *)fwevt->event_data;
  perst_id = le16_to_cpu(dev_pg0->persistent_id);
  if (perst_id != MPI3_DEVICE0_PERSISTENTID_INVALID)
   mpi3mr_devinfochg_evt_bh(mrioc, dev_pg0);
  break;
 }
 case MPI3_EVENT_DEVICE_STATUS_CHANGE:
 {
  mpi3mr_devstatuschg_evt_bh(mrioc, fwevt);
  break;
 }
 case MPI3_EVENT_ENCL_DEVICE_ADDED:
 case MPI3_EVENT_ENCL_DEVICE_STATUS_CHANGE:
 {
  mpi3mr_encldev_add_chg_evt_bh(mrioc, fwevt);
  break;
 }

 case MPI3_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
 {
  mpi3mr_sastopochg_evt_bh(mrioc, fwevt);
  break;
 }
 case MPI3_EVENT_PCIE_TOPOLOGY_CHANGE_LIST:
 {
  mpi3mr_pcietopochg_evt_bh(mrioc, fwevt);
  break;
 }
 case MPI3_EVENT_LOG_DATA:
 {
  mpi3mr_logdata_evt_bh(mrioc, fwevt);
  break;
 }
 case MPI3MR_DRIVER_EVENT_TG_QD_REDUCTION:
 {
  struct mpi3mr_throttle_group_info *tg;

  tg = *(struct mpi3mr_throttle_group_info **)fwevt->event_data;
  dprint_event_bh(mrioc,
      "qd reduction event processed for tg_id(%d) reduction_needed(%d)\n",
      tg->id, tg->need_qd_reduction);
  if (tg->need_qd_reduction) {
   mpi3mr_set_qd_for_all_vd_in_tg(mrioc, tg);
   tg->need_qd_reduction = 0;
  }
  break;
 }
 case MPI3_EVENT_WAIT_FOR_DEVICES_TO_REFRESH:
 {
  timeout = MPI3MR_RESET_TIMEOUT * 2;
  while ((mrioc->device_refresh_on || mrioc->block_on_pci_err) &&
      !mrioc->unrecoverable && !mrioc->pci_err_recovery) {
   msleep(500);
   if (!timeout--) {
    mrioc->unrecoverable = 1;
    break;
   }
  }

  if (mrioc->unrecoverable || mrioc->pci_err_recovery)
   break;

  dprint_event_bh(mrioc,
      "scan for non responding and newly added devices after soft reset started\n");
  if (mrioc->sas_transport_enabled) {
   mpi3mr_refresh_sas_ports(mrioc);
   mpi3mr_refresh_expanders(mrioc);
  }
  mpi3mr_refresh_tgtdevs(mrioc);
  ioc_info(mrioc,
      "scan for non responding and newly added devices after soft reset completed\n");
  break;
 }
 case MPI3MR_DRIVER_EVENT_PROCESS_TRIGGER:
 {
  mpi3mr_process_trigger_data_event_bh(mrioc,
      (struct trigger_event_data *)fwevt->event_data);
  break;
 }
 default:
  break;
 }

evt_ack:
 if (fwevt->send_ack)
  mpi3mr_process_event_ack(mrioc, fwevt->event_id,
      fwevt->evt_ctx);
out:
 /* Put fwevt reference count to neutralize kref_init increment */
 mpi3mr_fwevt_put(fwevt);
 mrioc->current_event = NULL;
}

/**
 * mpi3mr_fwevt_worker - Firmware event worker
 * @work: Work struct containing firmware event
 *
 * Extracts the firmware event and calls mpi3mr_fwevt_bh.
 *
 * Return: Nothing.
 */

static void mpi3mr_fwevt_worker(struct work_struct *work)
{
 struct mpi3mr_fwevt *fwevt = container_of(work, struct mpi3mr_fwevt,
     work);
 mpi3mr_fwevt_bh(fwevt->mrioc, fwevt);
 /*
 * Put fwevt reference count after
 * dequeuing it from worker queue
 */

 mpi3mr_fwevt_put(fwevt);
}

/**
 * mpi3mr_create_tgtdev - Create and add a target device
 * @mrioc: Adapter instance reference
 * @dev_pg0: Device Page 0 data
 *
 * If the device specified by the device page 0 data is not
 * present in the driver's internal list, allocate the memory
 * for the device, populate the data and add to the list, else
 * update the device data.  The key is persistent ID.
 *
 * Return: 0 on success, -ENOMEM on memory allocation failure
 */

static int mpi3mr_create_tgtdev(struct mpi3mr_ioc *mrioc,
 struct mpi3_device_page0 *dev_pg0)
{
 int retval = 0;
 struct mpi3mr_tgt_dev *tgtdev = NULL;
 u16 perst_id = 0;
 unsigned long flags;

 perst_id = le16_to_cpu(dev_pg0->persistent_id);
 if (perst_id == MPI3_DEVICE0_PERSISTENTID_INVALID)
  return retval;

 spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
 tgtdev = __mpi3mr_get_tgtdev_by_perst_id(mrioc, perst_id);
 if (tgtdev)
  tgtdev->state = MPI3MR_DEV_CREATED;
 spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);

 if (tgtdev) {
  mpi3mr_update_tgtdev(mrioc, tgtdev, dev_pg0, true);
  mpi3mr_tgtdev_put(tgtdev);
 } else {
  tgtdev = mpi3mr_alloc_tgtdev();
  if (!tgtdev)
   return -ENOMEM;
  mpi3mr_update_tgtdev(mrioc, tgtdev, dev_pg0, true);
  mpi3mr_tgtdev_add_to_list(mrioc, tgtdev);
 }

 return retval;
}

/**
 * mpi3mr_flush_delayed_cmd_lists - Flush pending commands
 * @mrioc: Adapter instance reference
 *
 * Flush pending commands in the delayed lists due to a
 * controller reset or driver removal as a cleanup.
 *
 * Return: Nothing
 */

void mpi3mr_flush_delayed_cmd_lists(struct mpi3mr_ioc *mrioc)
{
 struct delayed_dev_rmhs_node *_rmhs_node;
 struct delayed_evt_ack_node *_evtack_node;

 dprint_reset(mrioc, "flushing delayed dev_remove_hs commands\n");
 while (!list_empty(&mrioc->delayed_rmhs_list)) {
  _rmhs_node = list_entry(mrioc->delayed_rmhs_list.next,
      struct delayed_dev_rmhs_node, list);
  list_del(&_rmhs_node->list);
  kfree(_rmhs_node);
 }
 dprint_reset(mrioc, "flushing delayed event ack commands\n");
 while (!list_empty(&mrioc->delayed_evtack_cmds_list)) {
  _evtack_node = list_entry(mrioc->delayed_evtack_cmds_list.next,
      struct delayed_evt_ack_node, list);
  list_del(&_evtack_node->list);
  kfree(_evtack_node);
 }
}

/**
 * mpi3mr_dev_rmhs_complete_iou - Device removal IOUC completion
 * @mrioc: Adapter instance reference
 * @drv_cmd: Internal command tracker
 *
 * Issues a target reset TM to the firmware from the device
 * removal TM pend list or retry the removal handshake sequence
 * based on the IOU control request IOC status.
 *
 * Return: Nothing
 */

static void mpi3mr_dev_rmhs_complete_iou(struct mpi3mr_ioc *mrioc,
 struct mpi3mr_drv_cmd *drv_cmd)
{
 u16 cmd_idx = drv_cmd->host_tag - MPI3MR_HOSTTAG_DEVRMCMD_MIN;
 struct delayed_dev_rmhs_node *delayed_dev_rmhs = NULL;

 if (drv_cmd->state & MPI3MR_CMD_RESET)
  goto clear_drv_cmd;

 ioc_info(mrioc,
     "%s :dev_rmhs_iouctrl_complete:handle(0x%04x), ioc_status(0x%04x), loginfo(0x%08x)\n",
     __func__, drv_cmd->dev_handle, drv_cmd->ioc_status,
     drv_cmd->ioc_loginfo);
 if (drv_cmd->ioc_status != MPI3_IOCSTATUS_SUCCESS) {
  if (drv_cmd->retry_count < MPI3MR_DEV_RMHS_RETRY_COUNT) {
   drv_cmd->retry_count++;
   ioc_info(mrioc,
--> --------------------

--> maximum size reached

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

Messung V0.5
C=96 H=91 G=93

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