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

Quelle  mpt3sas_scsih.c   Sprache: C

 
/*
 * Scsi Host Layer for MPT (Message Passing Technology) based controllers
 *
 * This code is based on drivers/scsi/mpt3sas/mpt3sas_scsih.c
 * Copyright (C) 2012-2014  LSI Corporation
 * Copyright (C) 2013-2014 Avago Technologies
 *  (mailto: MPT-FusionLinux.pdl@avagotech.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * NO WARRANTY
 * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
 * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
 * solely responsible for determining the appropriateness of using and
 * distributing the Program and assumes all risks associated with its
 * exercise of rights under this Agreement, including but not limited to
 * the risks and costs of program errors, damage to or loss of data,
 * programs or equipment, and unavailability or interruption of operations.

 * DISCLAIMER OF LIABILITY
 * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
 * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
 * USA.
 */


#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/blkdev.h>
#include <linux/sched.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/raid_class.h>
#include <linux/unaligned.h>

#include "mpt3sas_base.h"

#define RAID_CHANNEL 1

#define PCIE_CHANNEL 2

/* forward proto's */
static void _scsih_expander_node_remove(struct MPT3SAS_ADAPTER *ioc,
 struct _sas_node *sas_expander);
static void _firmware_event_work(struct work_struct *work);

static void _scsih_remove_device(struct MPT3SAS_ADAPTER *ioc,
 struct _sas_device *sas_device);
static int _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle,
 u8 retry_count, u8 is_pd);
static int _scsih_pcie_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle);
static void _scsih_pcie_device_remove_from_sml(struct MPT3SAS_ADAPTER *ioc,
 struct _pcie_device *pcie_device);
static void
_scsih_pcie_check_device(struct MPT3SAS_ADAPTER *ioc, u16 handle);
static u8 _scsih_check_for_pending_tm(struct MPT3SAS_ADAPTER *ioc, u16 smid);
static void _scsih_complete_devices_scanning(struct MPT3SAS_ADAPTER *ioc);

/* global parameters */
LIST_HEAD(mpt3sas_ioc_list);
/* global ioc lock for list operations */
DEFINE_SPINLOCK(gioc_lock);

MODULE_AUTHOR(MPT3SAS_AUTHOR);
MODULE_DESCRIPTION(MPT3SAS_DESCRIPTION);
MODULE_LICENSE("GPL");
MODULE_VERSION(MPT3SAS_DRIVER_VERSION);
MODULE_ALIAS("mpt2sas");

/* local parameters */
static u8 scsi_io_cb_idx = -1;
static u8 tm_cb_idx = -1;
static u8 ctl_cb_idx = -1;
static u8 base_cb_idx = -1;
static u8 port_enable_cb_idx = -1;
static u8 transport_cb_idx = -1;
static u8 scsih_cb_idx = -1;
static u8 config_cb_idx = -1;
static int mpt2_ids;
static int mpt3_ids;

static u8 tm_tr_cb_idx = -1 ;
static u8 tm_tr_volume_cb_idx = -1 ;
static u8 tm_sas_control_cb_idx = -1;

/* command line options */
static u32 logging_level;
MODULE_PARM_DESC(logging_level,
 " bits for enabling additional logging info (default=0)");


static ushort max_sectors = 0xFFFF;
module_param(max_sectors, ushort, 0444);
MODULE_PARM_DESC(max_sectors, "max sectors, range 64 to 32767 default=32767");


static int missing_delay[2] = {-1, -1};
module_param_array(missing_delay, int, NULL, 0444);
MODULE_PARM_DESC(missing_delay, " device missing delay , io missing delay");

/* scsi-mid layer global parmeter is max_report_luns, which is 511 */
#define MPT3SAS_MAX_LUN (16895)
static u64 max_lun = MPT3SAS_MAX_LUN;
module_param(max_lun, ullong, 0444);
MODULE_PARM_DESC(max_lun, " max lun, default=16895 ");

static ushort hbas_to_enumerate;
module_param(hbas_to_enumerate, ushort, 0444);
MODULE_PARM_DESC(hbas_to_enumerate,
  " 0 - enumerates both SAS 2.0 & SAS 3.0 generation HBAs\n \
    1 - enumerates only SAS 2.0 generation HBAs\n \
    2 - enumerates only SAS 3.0 generation HBAs (default=0)");

/* diag_buffer_enable is bitwise
 * bit 0 set = TRACE
 * bit 1 set = SNAPSHOT
 * bit 2 set = EXTENDED
 *
 * Either bit can be set, or both
 */

static int diag_buffer_enable = -1;
module_param(diag_buffer_enable, int, 0444);
MODULE_PARM_DESC(diag_buffer_enable,
 " post diag buffers (TRACE=1/SNAPSHOT=2/EXTENDED=4/default=0)");
static int disable_discovery = -1;
module_param(disable_discovery, int, 0444);
MODULE_PARM_DESC(disable_discovery, " disable discovery ");


/* permit overriding the host protection capabilities mask (EEDP/T10 PI) */
static int prot_mask = -1;
module_param(prot_mask, int, 0444);
MODULE_PARM_DESC(prot_mask, " host protection capabilities mask, def=7 ");

static bool enable_sdev_max_qd;
module_param(enable_sdev_max_qd, bool, 0444);
MODULE_PARM_DESC(enable_sdev_max_qd,
 "Enable sdev max qd as can_queue, def=disabled(0)");

static int multipath_on_hba = -1;
module_param(multipath_on_hba, int, 0);
MODULE_PARM_DESC(multipath_on_hba,
 "Multipath support to add same target device\n\t\t"
 "as many times as it is visible to HBA from various paths\n\t\t"
 "(by default:\n\t\t"
 "\t SAS 2.0 & SAS 3.0 HBA - This will be disabled,\n\t\t"
 "\t SAS 3.5 HBA - This will be enabled)");

static int host_tagset_enable = 1;
module_param(host_tagset_enable, int, 0444);
MODULE_PARM_DESC(host_tagset_enable,
 "Shared host tagset enable/disable Default: enable(1)");

/* raid transport support */
static struct raid_template *mpt3sas_raid_template;
static struct raid_template *mpt2sas_raid_template;


/**
 * struct sense_info - common structure for obtaining sense keys
 * @skey: sense key
 * @asc: additional sense code
 * @ascq: additional sense code qualifier
 */

struct sense_info {
 u8 skey;
 u8 asc;
 u8 ascq;
};

#define MPT3SAS_PROCESS_TRIGGER_DIAG (0xFFFB)
#define MPT3SAS_TURN_ON_PFA_LED (0xFFFC)
#define MPT3SAS_PORT_ENABLE_COMPLETE (0xFFFD)
#define MPT3SAS_ABRT_TASK_SET (0xFFFE)
#define MPT3SAS_REMOVE_UNRESPONDING_DEVICES (0xFFFF)

/*
 * 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

/**
 * struct fw_event_work - firmware event struct
 * @list: link list framework
 * @work: work object (ioc->fault_reset_work_q)
 * @ioc: per adapter object
 * @device_handle: device handle
 * @VF_ID: virtual function id
 * @VP_ID: virtual port id
 * @ignore: flag meaning this event has been marked to ignore
 * @event: firmware event MPI2_EVENT_XXX defined in mpi2_ioc.h
 * @refcount: kref for this event
 * @event_data: reply event data payload follows
 *
 * This object stored on ioc->fw_event_list.
 */

struct fw_event_work {
 struct list_head list;
 struct work_struct work;

 struct MPT3SAS_ADAPTER *ioc;
 u16   device_handle;
 u8   VF_ID;
 u8   VP_ID;
 u8   ignore;
 u16   event;
 struct kref  refcount;
 char   event_data[] __aligned(4);
};

static void fw_event_work_free(struct kref *r)
{
 kfree(container_of(r, struct fw_event_work, refcount));
}

static void fw_event_work_get(struct fw_event_work *fw_work)
{
 kref_get(&fw_work->refcount);
}

static void fw_event_work_put(struct fw_event_work *fw_work)
{
 kref_put(&fw_work->refcount, fw_event_work_free);
}

static struct fw_event_work *alloc_fw_event_work(int len)
{
 struct fw_event_work *fw_event;

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

 kref_init(&fw_event->refcount);
 return fw_event;
}

/**
 * struct _scsi_io_transfer - scsi io transfer
 * @handle: sas device handle (assigned by firmware)
 * @is_raid: flag set for hidden raid components
 * @dir: DMA_TO_DEVICE, DMA_FROM_DEVICE,
 * @data_length: data transfer length
 * @data_dma: dma pointer to data
 * @sense: sense data
 * @lun: lun number
 * @cdb_length: cdb length
 * @cdb: cdb contents
 * @timeout: timeout for this command
 * @VF_ID: virtual function id
 * @VP_ID: virtual port id
 * @valid_reply: flag set for reply message
 * @sense_length: sense length
 * @ioc_status: ioc status
 * @scsi_state: scsi state
 * @scsi_status: scsi staus
 * @log_info: log information
 * @transfer_length: data length transfer when there is a reply message
 *
 * Used for sending internal scsi commands to devices within this module.
 * Refer to _scsi_send_scsi_io().
 */

struct _scsi_io_transfer {
 u16 handle;
 u8 is_raid;
 enum dma_data_direction dir;
 u32 data_length;
 dma_addr_t data_dma;
 u8 sense[SCSI_SENSE_BUFFERSIZE];
 u32 lun;
 u8 cdb_length;
 u8 cdb[32];
 u8 timeout;
 u8 VF_ID;
 u8 VP_ID;
 u8 valid_reply;
  /* the following bits are only valid when 'valid_reply = 1' */
 u32 sense_length;
 u16 ioc_status;
 u8 scsi_state;
 u8 scsi_status;
 u32 log_info;
 u32 transfer_length;
};

/**
 * _scsih_set_debug_level - global setting of ioc->logging_level.
 * @val: value of the parameter to be set
 * @kp: pointer to kernel_param structure
 *
 * Note: The logging levels are defined in mpt3sas_debug.h.
 */

static int
_scsih_set_debug_level(const char *val, const struct kernel_param *kp)
{
 int ret = param_set_int(val, kp);
 struct MPT3SAS_ADAPTER *ioc;

 if (ret)
  return ret;

 pr_info("setting logging_level(0x%08x)\n", logging_level);
 spin_lock(&gioc_lock);
 list_for_each_entry(ioc, &mpt3sas_ioc_list, list)
  ioc->logging_level = logging_level;
 spin_unlock(&gioc_lock);
 return 0;
}
module_param_call(logging_level, _scsih_set_debug_level, param_get_int,
 &logging_level, 0644);

/**
 * _scsih_srch_boot_sas_address - search based on sas_address
 * @sas_address: sas address
 * @boot_device: boot device object from bios page 2
 *
 * Return: 1 when there's a match, 0 means no match.
 */

static inline int
_scsih_srch_boot_sas_address(u64 sas_address,
 Mpi2BootDeviceSasWwid_t *boot_device)
{
 return (sas_address == le64_to_cpu(boot_device->SASAddress)) ?  1 : 0;
}

/**
 * _scsih_srch_boot_device_name - search based on device name
 * @device_name: device name specified in INDENTIFY fram
 * @boot_device: boot device object from bios page 2
 *
 * Return: 1 when there's a match, 0 means no match.
 */

static inline int
_scsih_srch_boot_device_name(u64 device_name,
 Mpi2BootDeviceDeviceName_t *boot_device)
{
 return (device_name == le64_to_cpu(boot_device->DeviceName)) ? 1 : 0;
}

/**
 * _scsih_srch_boot_encl_slot - search based on enclosure_logical_id/slot
 * @enclosure_logical_id: enclosure logical id
 * @slot_number: slot number
 * @boot_device: boot device object from bios page 2
 *
 * Return: 1 when there's a match, 0 means no match.
 */

static inline int
_scsih_srch_boot_encl_slot(u64 enclosure_logical_id, u16 slot_number,
 Mpi2BootDeviceEnclosureSlot_t *boot_device)
{
 return (enclosure_logical_id == le64_to_cpu(boot_device->
     EnclosureLogicalID) && slot_number == le16_to_cpu(boot_device->
     SlotNumber)) ? 1 : 0;
}

/**
 * mpt3sas_get_port_by_id - get hba port entry corresponding to provided
 *   port number from port list
 * @ioc: per adapter object
 * @port_id: port number
 * @bypass_dirty_port_flag: when set look the matching hba port entry even
 * if hba port entry is marked as dirty.
 *
 * Search for hba port entry corresponding to provided port number,
 * if available return port object otherwise return NULL.
 */

struct hba_port *
mpt3sas_get_port_by_id(struct MPT3SAS_ADAPTER *ioc,
 u8 port_id, u8 bypass_dirty_port_flag)
{
 struct hba_port *port, *port_next;

 /*
 * When multipath_on_hba is disabled then
 * search the hba_port entry using default
 * port id i.e. 255
 */

 if (!ioc->multipath_on_hba)
  port_id = MULTIPATH_DISABLED_PORT_ID;

 list_for_each_entry_safe(port, port_next,
     &ioc->port_table_list, list) {
  if (port->port_id != port_id)
   continue;
  if (bypass_dirty_port_flag)
   return port;
  if (port->flags & HBA_PORT_FLAG_DIRTY_PORT)
   continue;
  return port;
 }

 /*
 * Allocate hba_port object for default port id (i.e. 255)
 * when multipath_on_hba is disabled for the HBA.
 * And add this object to port_table_list.
 */

 if (!ioc->multipath_on_hba) {
  port = kzalloc(sizeof(struct hba_port), GFP_ATOMIC);
  if (!port)
   return NULL;

  port->port_id = port_id;
  ioc_info(ioc,
     "hba_port entry: %p, port: %d is added to hba_port list\n",
     port, port->port_id);
  list_add_tail(&port->list,
      &ioc->port_table_list);
  return port;
 }
 return NULL;
}

/**
 * mpt3sas_get_vphy_by_phy - get virtual_phy object corresponding to phy number
 * @ioc: per adapter object
 * @port: hba_port object
 * @phy: phy number
 *
 * Return virtual_phy object corresponding to phy number.
 */

struct virtual_phy *
mpt3sas_get_vphy_by_phy(struct MPT3SAS_ADAPTER *ioc,
 struct hba_port *port, u32 phy)
{
 struct virtual_phy *vphy, *vphy_next;

 if (!port->vphys_mask)
  return NULL;

 list_for_each_entry_safe(vphy, vphy_next, &port->vphys_list, list) {
  if (vphy->phy_mask & (1 << phy))
   return vphy;
 }
 return NULL;
}

/**
 * _scsih_is_boot_device - search for matching boot device.
 * @sas_address: sas address
 * @device_name: device name specified in INDENTIFY fram
 * @enclosure_logical_id: enclosure logical id
 * @slot: slot number
 * @form: specifies boot device form
 * @boot_device: boot device object from bios page 2
 *
 * Return: 1 when there's a match, 0 means no match.
 */

static int
_scsih_is_boot_device(u64 sas_address, u64 device_name,
 u64 enclosure_logical_id, u16 slot, u8 form,
 Mpi2BiosPage2BootDevice_t *boot_device)
{
 int rc = 0;

 switch (form) {
 case MPI2_BIOSPAGE2_FORM_SAS_WWID:
  if (!sas_address)
   break;
  rc = _scsih_srch_boot_sas_address(
      sas_address, &boot_device->SasWwid);
  break;
 case MPI2_BIOSPAGE2_FORM_ENCLOSURE_SLOT:
  if (!enclosure_logical_id)
   break;
  rc = _scsih_srch_boot_encl_slot(
      enclosure_logical_id,
      slot, &boot_device->EnclosureSlot);
  break;
 case MPI2_BIOSPAGE2_FORM_DEVICE_NAME:
  if (!device_name)
   break;
  rc = _scsih_srch_boot_device_name(
      device_name, &boot_device->DeviceName);
  break;
 case MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED:
  break;
 }

 return rc;
}

/**
 * _scsih_get_sas_address - set the sas_address for given device handle
 * @ioc: ?
 * @handle: device handle
 * @sas_address: sas address
 *
 * Return: 0 success, non-zero when failure
 */

static int
_scsih_get_sas_address(struct MPT3SAS_ADAPTER *ioc, u16 handle,
 u64 *sas_address)
{
 Mpi2SasDevicePage0_t sas_device_pg0;
 Mpi2ConfigReply_t mpi_reply;
 u32 ioc_status;

 *sas_address = 0;

 if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
     MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
  ioc_err(ioc, "failure at %s:%d/%s()!\n",
   __FILE__, __LINE__, __func__);
  return -ENXIO;
 }

 ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK;
 if (ioc_status == MPI2_IOCSTATUS_SUCCESS) {
  /* For HBA, vSES doesn't return HBA SAS address. Instead return
 * vSES's sas address.
 */

  if ((handle <= ioc->sas_hba.num_phys) &&
     (!(le32_to_cpu(sas_device_pg0.DeviceInfo) &
     MPI2_SAS_DEVICE_INFO_SEP)))
   *sas_address = ioc->sas_hba.sas_address;
  else
   *sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
  return 0;
 }

 /* we hit this because the given parent handle doesn't exist */
 if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
  return -ENXIO;

 /* else error case */
 ioc_err(ioc, "handle(0x%04x), ioc_status(0x%04x), failure at %s:%d/%s()!\n",
  handle, ioc_status, __FILE__, __LINE__, __func__);
 return -EIO;
}

/**
 * _scsih_determine_boot_device - determine boot device.
 * @ioc: per adapter object
 * @device: sas_device or pcie_device object
 * @channel: SAS or PCIe channel
 *
 * Determines whether this device should be first reported device to
 * to scsi-ml or sas transport, this purpose is for persistent boot device.
 * There are primary, alternate, and current entries in bios page 2. The order
 * priority is primary, alternate, then current.  This routine saves
 * the corresponding device object.
 * The saved data to be used later in _scsih_probe_boot_devices().
 */

static void
_scsih_determine_boot_device(struct MPT3SAS_ADAPTER *ioc, void *device,
 u32 channel)
{
 struct _sas_device *sas_device;
 struct _pcie_device *pcie_device;
 struct _raid_device *raid_device;
 u64 sas_address;
 u64 device_name;
 u64 enclosure_logical_id;
 u16 slot;

  /* only process this function when driver loads */
 if (!ioc->is_driver_loading)
  return;

  /* no Bios, return immediately */
 if (!ioc->bios_pg3.BiosVersion)
  return;

 if (channel == RAID_CHANNEL) {
  raid_device = device;
  sas_address = raid_device->wwid;
  device_name = 0;
  enclosure_logical_id = 0;
  slot = 0;
 } else if (channel == PCIE_CHANNEL) {
  pcie_device = device;
  sas_address = pcie_device->wwid;
  device_name = 0;
  enclosure_logical_id = 0;
  slot = 0;
 } else {
  sas_device = device;
  sas_address = sas_device->sas_address;
  device_name = sas_device->device_name;
  enclosure_logical_id = sas_device->enclosure_logical_id;
  slot = sas_device->slot;
 }

 if (!ioc->req_boot_device.device) {
  if (_scsih_is_boot_device(sas_address, device_name,
      enclosure_logical_id, slot,
      (ioc->bios_pg2.ReqBootDeviceForm &
      MPI2_BIOSPAGE2_FORM_MASK),
      &ioc->bios_pg2.RequestedBootDevice)) {
   dinitprintk(ioc,
        ioc_info(ioc, "%s: req_boot_device(0x%016llx)\n",
          __func__, (u64)sas_address));
   ioc->req_boot_device.device = device;
   ioc->req_boot_device.channel = channel;
  }
 }

 if (!ioc->req_alt_boot_device.device) {
  if (_scsih_is_boot_device(sas_address, device_name,
      enclosure_logical_id, slot,
      (ioc->bios_pg2.ReqAltBootDeviceForm &
      MPI2_BIOSPAGE2_FORM_MASK),
      &ioc->bios_pg2.RequestedAltBootDevice)) {
   dinitprintk(ioc,
        ioc_info(ioc, "%s: req_alt_boot_device(0x%016llx)\n",
          __func__, (u64)sas_address));
   ioc->req_alt_boot_device.device = device;
   ioc->req_alt_boot_device.channel = channel;
  }
 }

 if (!ioc->current_boot_device.device) {
  if (_scsih_is_boot_device(sas_address, device_name,
      enclosure_logical_id, slot,
      (ioc->bios_pg2.CurrentBootDeviceForm &
      MPI2_BIOSPAGE2_FORM_MASK),
      &ioc->bios_pg2.CurrentBootDevice)) {
   dinitprintk(ioc,
        ioc_info(ioc, "%s: current_boot_device(0x%016llx)\n",
          __func__, (u64)sas_address));
   ioc->current_boot_device.device = device;
   ioc->current_boot_device.channel = channel;
  }
 }
}

static struct _sas_device *
__mpt3sas_get_sdev_from_target(struct MPT3SAS_ADAPTER *ioc,
  struct MPT3SAS_TARGET *tgt_priv)
{
 struct _sas_device *ret;

 assert_spin_locked(&ioc->sas_device_lock);

 ret = tgt_priv->sas_dev;
 if (ret)
  sas_device_get(ret);

 return ret;
}

static struct _sas_device *
mpt3sas_get_sdev_from_target(struct MPT3SAS_ADAPTER *ioc,
  struct MPT3SAS_TARGET *tgt_priv)
{
 struct _sas_device *ret;
 unsigned long flags;

 spin_lock_irqsave(&ioc->sas_device_lock, flags);
 ret = __mpt3sas_get_sdev_from_target(ioc, tgt_priv);
 spin_unlock_irqrestore(&ioc->sas_device_lock, flags);

 return ret;
}

static struct _pcie_device *
__mpt3sas_get_pdev_from_target(struct MPT3SAS_ADAPTER *ioc,
 struct MPT3SAS_TARGET *tgt_priv)
{
 struct _pcie_device *ret;

 assert_spin_locked(&ioc->pcie_device_lock);

 ret = tgt_priv->pcie_dev;
 if (ret)
  pcie_device_get(ret);

 return ret;
}

/**
 * mpt3sas_get_pdev_from_target - pcie device search
 * @ioc: per adapter object
 * @tgt_priv: starget private object
 *
 * Context: This function will acquire ioc->pcie_device_lock and will release
 * before returning the pcie_device object.
 *
 * This searches for pcie_device from target, then return pcie_device object.
 */

static struct _pcie_device *
mpt3sas_get_pdev_from_target(struct MPT3SAS_ADAPTER *ioc,
 struct MPT3SAS_TARGET *tgt_priv)
{
 struct _pcie_device *ret;
 unsigned long flags;

 spin_lock_irqsave(&ioc->pcie_device_lock, flags);
 ret = __mpt3sas_get_pdev_from_target(ioc, tgt_priv);
 spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);

 return ret;
}


/**
 * __mpt3sas_get_sdev_by_rphy - sas device search
 * @ioc: per adapter object
 * @rphy: sas_rphy pointer
 *
 * Context: This function will acquire ioc->sas_device_lock and will release
 * before returning the sas_device object.
 *
 * This searches for sas_device from rphy object
 * then return sas_device object.
 */

struct _sas_device *
__mpt3sas_get_sdev_by_rphy(struct MPT3SAS_ADAPTER *ioc,
 struct sas_rphy *rphy)
{
 struct _sas_device *sas_device;

 assert_spin_locked(&ioc->sas_device_lock);

 list_for_each_entry(sas_device, &ioc->sas_device_list, list) {
  if (sas_device->rphy != rphy)
   continue;
  sas_device_get(sas_device);
  return sas_device;
 }

 sas_device = NULL;
 list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) {
  if (sas_device->rphy != rphy)
   continue;
  sas_device_get(sas_device);
  return sas_device;
 }

 return NULL;
}

/**
 * __mpt3sas_get_sdev_by_addr - get _sas_device object corresponding to provided
 * sas address from sas_device_list list
 * @ioc: per adapter object
 * @sas_address: device sas address
 * @port: port number
 *
 * Search for _sas_device object corresponding to provided sas address,
 * if available return _sas_device object address otherwise return NULL.
 */

struct _sas_device *
__mpt3sas_get_sdev_by_addr(struct MPT3SAS_ADAPTER *ioc,
 u64 sas_address, struct hba_port *port)
{
 struct _sas_device *sas_device;

 if (!port)
  return NULL;

 assert_spin_locked(&ioc->sas_device_lock);

 list_for_each_entry(sas_device, &ioc->sas_device_list, list) {
  if (sas_device->sas_address != sas_address)
   continue;
  if (sas_device->port != port)
   continue;
  sas_device_get(sas_device);
  return sas_device;
 }

 list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) {
  if (sas_device->sas_address != sas_address)
   continue;
  if (sas_device->port != port)
   continue;
  sas_device_get(sas_device);
  return sas_device;
 }

 return NULL;
}

/**
 * mpt3sas_get_sdev_by_addr - sas device search
 * @ioc: per adapter object
 * @sas_address: sas address
 * @port: hba port entry
 * Context: Calling function should acquire ioc->sas_device_lock
 *
 * This searches for sas_device based on sas_address & port number,
 * then return sas_device object.
 */

struct _sas_device *
mpt3sas_get_sdev_by_addr(struct MPT3SAS_ADAPTER *ioc,
 u64 sas_address, struct hba_port *port)
{
 struct _sas_device *sas_device;
 unsigned long flags;

 spin_lock_irqsave(&ioc->sas_device_lock, flags);
 sas_device = __mpt3sas_get_sdev_by_addr(ioc,
     sas_address, port);
 spin_unlock_irqrestore(&ioc->sas_device_lock, flags);

 return sas_device;
}

static struct _sas_device *
__mpt3sas_get_sdev_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
{
 struct _sas_device *sas_device;

 assert_spin_locked(&ioc->sas_device_lock);

 list_for_each_entry(sas_device, &ioc->sas_device_list, list)
  if (sas_device->handle == handle)
   goto found_device;

 list_for_each_entry(sas_device, &ioc->sas_device_init_list, list)
  if (sas_device->handle == handle)
   goto found_device;

 return NULL;

found_device:
 sas_device_get(sas_device);
 return sas_device;
}

/**
 * mpt3sas_get_sdev_by_handle - sas device search
 * @ioc: per adapter object
 * @handle: sas device handle (assigned by firmware)
 * Context: Calling function should acquire ioc->sas_device_lock
 *
 * This searches for sas_device based on sas_address, then return sas_device
 * object.
 */

struct _sas_device *
mpt3sas_get_sdev_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
{
 struct _sas_device *sas_device;
 unsigned long flags;

 spin_lock_irqsave(&ioc->sas_device_lock, flags);
 sas_device = __mpt3sas_get_sdev_by_handle(ioc, handle);
 spin_unlock_irqrestore(&ioc->sas_device_lock, flags);

 return sas_device;
}

/**
 * _scsih_display_enclosure_chassis_info - display device location info
 * @ioc: per adapter object
 * @sas_device: per sas device object
 * @sdev: scsi device struct
 * @starget: scsi target struct
 */

static void
_scsih_display_enclosure_chassis_info(struct MPT3SAS_ADAPTER *ioc,
 struct _sas_device *sas_device, struct scsi_device *sdev,
 struct scsi_target *starget)
{
 if (sdev) {
  if (sas_device->enclosure_handle != 0)
   sdev_printk(KERN_INFO, sdev,
       "enclosure logical id (0x%016llx), slot(%d) \n",
       (unsigned long long)
       sas_device->enclosure_logical_id,
       sas_device->slot);
  if (sas_device->connector_name[0] != '\0')
   sdev_printk(KERN_INFO, sdev,
       "enclosure level(0x%04x), connector name( %s)\n",
       sas_device->enclosure_level,
       sas_device->connector_name);
  if (sas_device->is_chassis_slot_valid)
   sdev_printk(KERN_INFO, sdev, "chassis slot(0x%04x)\n",
       sas_device->chassis_slot);
 } else if (starget) {
  if (sas_device->enclosure_handle != 0)
   starget_printk(KERN_INFO, starget,
       "enclosure logical id(0x%016llx), slot(%d) \n",
       (unsigned long long)
       sas_device->enclosure_logical_id,
       sas_device->slot);
  if (sas_device->connector_name[0] != '\0')
   starget_printk(KERN_INFO, starget,
       "enclosure level(0x%04x), connector name( %s)\n",
       sas_device->enclosure_level,
       sas_device->connector_name);
  if (sas_device->is_chassis_slot_valid)
   starget_printk(KERN_INFO, starget,
       "chassis slot(0x%04x)\n",
       sas_device->chassis_slot);
 } else {
  if (sas_device->enclosure_handle != 0)
   ioc_info(ioc, "enclosure logical id(0x%016llx), slot(%d)\n",
     (u64)sas_device->enclosure_logical_id,
     sas_device->slot);
  if (sas_device->connector_name[0] != '\0')
   ioc_info(ioc, "enclosure level(0x%04x), connector name( %s)\n",
     sas_device->enclosure_level,
     sas_device->connector_name);
  if (sas_device->is_chassis_slot_valid)
   ioc_info(ioc, "chassis slot(0x%04x)\n",
     sas_device->chassis_slot);
 }
}

/**
 * _scsih_sas_device_remove - remove sas_device from list.
 * @ioc: per adapter object
 * @sas_device: the sas_device object
 * Context: This function will acquire ioc->sas_device_lock.
 *
 * If sas_device is on the list, remove it and decrement its reference count.
 */

static void
_scsih_sas_device_remove(struct MPT3SAS_ADAPTER *ioc,
 struct _sas_device *sas_device)
{
 unsigned long flags;

 if (!sas_device)
  return;
 ioc_info(ioc, "removing handle(0x%04x), sas_addr(0x%016llx)\n",
   sas_device->handle, (u64)sas_device->sas_address);

 _scsih_display_enclosure_chassis_info(ioc, sas_device, NULL, NULL);

 /*
 * The lock serializes access to the list, but we still need to verify
 * that nobody removed the entry while we were waiting on the lock.
 */

 spin_lock_irqsave(&ioc->sas_device_lock, flags);
 if (!list_empty(&sas_device->list)) {
  list_del_init(&sas_device->list);
  sas_device_put(sas_device);
 }
 spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
}

/**
 * _scsih_device_remove_by_handle - removing device object by handle
 * @ioc: per adapter object
 * @handle: device handle
 */

static void
_scsih_device_remove_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
{
 struct _sas_device *sas_device;
 unsigned long flags;

 if (ioc->shost_recovery)
  return;

 spin_lock_irqsave(&ioc->sas_device_lock, flags);
 sas_device = __mpt3sas_get_sdev_by_handle(ioc, handle);
 if (sas_device) {
  list_del_init(&sas_device->list);
  sas_device_put(sas_device);
 }
 spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 if (sas_device) {
  _scsih_remove_device(ioc, sas_device);
  sas_device_put(sas_device);
 }
}

/**
 * mpt3sas_device_remove_by_sas_address - removing device object by
 * sas address & port number
 * @ioc: per adapter object
 * @sas_address: device sas_address
 * @port: hba port entry
 *
 * Return nothing.
 */

void
mpt3sas_device_remove_by_sas_address(struct MPT3SAS_ADAPTER *ioc,
 u64 sas_address, struct hba_port *port)
{
 struct _sas_device *sas_device;
 unsigned long flags;

 if (ioc->shost_recovery)
  return;

 spin_lock_irqsave(&ioc->sas_device_lock, flags);
 sas_device = __mpt3sas_get_sdev_by_addr(ioc, sas_address, port);
 if (sas_device) {
  list_del_init(&sas_device->list);
  sas_device_put(sas_device);
 }
 spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 if (sas_device) {
  _scsih_remove_device(ioc, sas_device);
  sas_device_put(sas_device);
 }
}

/**
 * _scsih_sas_device_add - insert sas_device to the list.
 * @ioc: per adapter object
 * @sas_device: the sas_device object
 * Context: This function will acquire ioc->sas_device_lock.
 *
 * Adding new object to the ioc->sas_device_list.
 */

static void
_scsih_sas_device_add(struct MPT3SAS_ADAPTER *ioc,
 struct _sas_device *sas_device)
{
 unsigned long flags;

 dewtprintk(ioc,
     ioc_info(ioc, "%s: handle(0x%04x), sas_addr(0x%016llx)\n",
       __func__, sas_device->handle,
       (u64)sas_device->sas_address));

 dewtprintk(ioc, _scsih_display_enclosure_chassis_info(ioc, sas_device,
     NULL, NULL));

 spin_lock_irqsave(&ioc->sas_device_lock, flags);
 sas_device_get(sas_device);
 list_add_tail(&sas_device->list, &ioc->sas_device_list);
 spin_unlock_irqrestore(&ioc->sas_device_lock, flags);

 if (ioc->hide_drives) {
  clear_bit(sas_device->handle, ioc->pend_os_device_add);
  return;
 }

 if (!mpt3sas_transport_port_add(ioc, sas_device->handle,
      sas_device->sas_address_parent, sas_device->port)) {
  _scsih_sas_device_remove(ioc, sas_device);
 } else if (!sas_device->starget) {
  /*
 * When asyn scanning is enabled, its not possible to remove
 * devices while scanning is turned on due to an oops in
 * scsi_sysfs_add_sdev()->add_device()->sysfs_addrm_start()
 */

  if (!ioc->is_driver_loading) {
   mpt3sas_transport_port_remove(ioc,
       sas_device->sas_address,
       sas_device->sas_address_parent,
       sas_device->port);
   _scsih_sas_device_remove(ioc, sas_device);
  }
 } else
  clear_bit(sas_device->handle, ioc->pend_os_device_add);
}

/**
 * _scsih_sas_device_init_add - insert sas_device to the list.
 * @ioc: per adapter object
 * @sas_device: the sas_device object
 * Context: This function will acquire ioc->sas_device_lock.
 *
 * Adding new object at driver load time to the ioc->sas_device_init_list.
 */

static void
_scsih_sas_device_init_add(struct MPT3SAS_ADAPTER *ioc,
 struct _sas_device *sas_device)
{
 unsigned long flags;

 dewtprintk(ioc,
     ioc_info(ioc, "%s: handle(0x%04x), sas_addr(0x%016llx)\n",
       __func__, sas_device->handle,
       (u64)sas_device->sas_address));

 dewtprintk(ioc, _scsih_display_enclosure_chassis_info(ioc, sas_device,
     NULL, NULL));

 spin_lock_irqsave(&ioc->sas_device_lock, flags);
 sas_device_get(sas_device);
 list_add_tail(&sas_device->list, &ioc->sas_device_init_list);
 _scsih_determine_boot_device(ioc, sas_device, 0);
 spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
}


static struct _pcie_device *
__mpt3sas_get_pdev_by_wwid(struct MPT3SAS_ADAPTER *ioc, u64 wwid)
{
 struct _pcie_device *pcie_device;

 assert_spin_locked(&ioc->pcie_device_lock);

 list_for_each_entry(pcie_device, &ioc->pcie_device_list, list)
  if (pcie_device->wwid == wwid)
   goto found_device;

 list_for_each_entry(pcie_device, &ioc->pcie_device_init_list, list)
  if (pcie_device->wwid == wwid)
   goto found_device;

 return NULL;

found_device:
 pcie_device_get(pcie_device);
 return pcie_device;
}


/**
 * mpt3sas_get_pdev_by_wwid - pcie device search
 * @ioc: per adapter object
 * @wwid: wwid
 *
 * Context: This function will acquire ioc->pcie_device_lock and will release
 * before returning the pcie_device object.
 *
 * This searches for pcie_device based on wwid, then return pcie_device object.
 */

static struct _pcie_device *
mpt3sas_get_pdev_by_wwid(struct MPT3SAS_ADAPTER *ioc, u64 wwid)
{
 struct _pcie_device *pcie_device;
 unsigned long flags;

 spin_lock_irqsave(&ioc->pcie_device_lock, flags);
 pcie_device = __mpt3sas_get_pdev_by_wwid(ioc, wwid);
 spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);

 return pcie_device;
}


static struct _pcie_device *
__mpt3sas_get_pdev_by_idchannel(struct MPT3SAS_ADAPTER *ioc, int id,
 int channel)
{
 struct _pcie_device *pcie_device;

 assert_spin_locked(&ioc->pcie_device_lock);

 list_for_each_entry(pcie_device, &ioc->pcie_device_list, list)
  if (pcie_device->id == id && pcie_device->channel == channel)
   goto found_device;

 list_for_each_entry(pcie_device, &ioc->pcie_device_init_list, list)
  if (pcie_device->id == id && pcie_device->channel == channel)
   goto found_device;

 return NULL;

found_device:
 pcie_device_get(pcie_device);
 return pcie_device;
}

static struct _pcie_device *
__mpt3sas_get_pdev_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
{
 struct _pcie_device *pcie_device;

 assert_spin_locked(&ioc->pcie_device_lock);

 list_for_each_entry(pcie_device, &ioc->pcie_device_list, list)
  if (pcie_device->handle == handle)
   goto found_device;

 list_for_each_entry(pcie_device, &ioc->pcie_device_init_list, list)
  if (pcie_device->handle == handle)
   goto found_device;

 return NULL;

found_device:
 pcie_device_get(pcie_device);
 return pcie_device;
}


/**
 * mpt3sas_get_pdev_by_handle - pcie device search
 * @ioc: per adapter object
 * @handle: Firmware device handle
 *
 * Context: This function will acquire ioc->pcie_device_lock and will release
 * before returning the pcie_device object.
 *
 * This searches for pcie_device based on handle, then return pcie_device
 * object.
 */

struct _pcie_device *
mpt3sas_get_pdev_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
{
 struct _pcie_device *pcie_device;
 unsigned long flags;

 spin_lock_irqsave(&ioc->pcie_device_lock, flags);
 pcie_device = __mpt3sas_get_pdev_by_handle(ioc, handle);
 spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);

 return pcie_device;
}

/**
 * _scsih_set_nvme_max_shutdown_latency - Update max_shutdown_latency.
 * @ioc: per adapter object
 * Context: This function will acquire ioc->pcie_device_lock
 *
 * Update ioc->max_shutdown_latency to that NVMe drives RTD3 Entry Latency
 * which has reported maximum among all available NVMe drives.
 * Minimum max_shutdown_latency will be six seconds.
 */

static void
_scsih_set_nvme_max_shutdown_latency(struct MPT3SAS_ADAPTER *ioc)
{
 struct _pcie_device *pcie_device;
 unsigned long flags;
 u16 shutdown_latency = IO_UNIT_CONTROL_SHUTDOWN_TIMEOUT;

 spin_lock_irqsave(&ioc->pcie_device_lock, flags);
 list_for_each_entry(pcie_device, &ioc->pcie_device_list, list) {
  if (pcie_device->shutdown_latency) {
   if (shutdown_latency < pcie_device->shutdown_latency)
    shutdown_latency =
     pcie_device->shutdown_latency;
  }
 }
 ioc->max_shutdown_latency = shutdown_latency;
 spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
}

/**
 * _scsih_pcie_device_remove - remove pcie_device from list.
 * @ioc: per adapter object
 * @pcie_device: the pcie_device object
 * Context: This function will acquire ioc->pcie_device_lock.
 *
 * If pcie_device is on the list, remove it and decrement its reference count.
 */

static void
_scsih_pcie_device_remove(struct MPT3SAS_ADAPTER *ioc,
 struct _pcie_device *pcie_device)
{
 unsigned long flags;
 int was_on_pcie_device_list = 0;
 u8 update_latency = 0;

 if (!pcie_device)
  return;
 ioc_info(ioc, "removing handle(0x%04x), wwid(0x%016llx)\n",
   pcie_device->handle, (u64)pcie_device->wwid);
 if (pcie_device->enclosure_handle != 0)
  ioc_info(ioc, "removing enclosure logical id(0x%016llx), slot(%d)\n",
    (u64)pcie_device->enclosure_logical_id,
    pcie_device->slot);
 if (pcie_device->connector_name[0] != '\0')
  ioc_info(ioc, "removing enclosure level(0x%04x), connector name( %s)\n",
    pcie_device->enclosure_level,
    pcie_device->connector_name);

 spin_lock_irqsave(&ioc->pcie_device_lock, flags);
 if (!list_empty(&pcie_device->list)) {
  list_del_init(&pcie_device->list);
  was_on_pcie_device_list = 1;
 }
 if (pcie_device->shutdown_latency == ioc->max_shutdown_latency)
  update_latency = 1;
 spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
 if (was_on_pcie_device_list) {
  kfree(pcie_device->serial_number);
  pcie_device_put(pcie_device);
 }

 /*
 * This device's RTD3 Entry Latency matches IOC's
 * max_shutdown_latency. Recalculate IOC's max_shutdown_latency
 * from the available drives as current drive is getting removed.
 */

 if (update_latency)
  _scsih_set_nvme_max_shutdown_latency(ioc);
}


/**
 * _scsih_pcie_device_remove_by_handle - removing pcie device object by handle
 * @ioc: per adapter object
 * @handle: device handle
 */

static void
_scsih_pcie_device_remove_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
{
 struct _pcie_device *pcie_device;
 unsigned long flags;
 int was_on_pcie_device_list = 0;
 u8 update_latency = 0;

 if (ioc->shost_recovery)
  return;

 spin_lock_irqsave(&ioc->pcie_device_lock, flags);
 pcie_device = __mpt3sas_get_pdev_by_handle(ioc, handle);
 if (pcie_device) {
  if (!list_empty(&pcie_device->list)) {
   list_del_init(&pcie_device->list);
   was_on_pcie_device_list = 1;
   pcie_device_put(pcie_device);
  }
  if (pcie_device->shutdown_latency == ioc->max_shutdown_latency)
   update_latency = 1;
 }
 spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
 if (was_on_pcie_device_list) {
  _scsih_pcie_device_remove_from_sml(ioc, pcie_device);
  pcie_device_put(pcie_device);
 }

 /*
 * This device's RTD3 Entry Latency matches IOC's
 * max_shutdown_latency. Recalculate IOC's max_shutdown_latency
 * from the available drives as current drive is getting removed.
 */

 if (update_latency)
  _scsih_set_nvme_max_shutdown_latency(ioc);
}

/**
 * _scsih_pcie_device_add - add pcie_device object
 * @ioc: per adapter object
 * @pcie_device: pcie_device object
 *
 * This is added to the pcie_device_list link list.
 */

static void
_scsih_pcie_device_add(struct MPT3SAS_ADAPTER *ioc,
 struct _pcie_device *pcie_device)
{
 unsigned long flags;

 dewtprintk(ioc,
     ioc_info(ioc, "%s: handle (0x%04x), wwid(0x%016llx)\n",
       __func__,
       pcie_device->handle, (u64)pcie_device->wwid));
 if (pcie_device->enclosure_handle != 0)
  dewtprintk(ioc,
      ioc_info(ioc, "%s: enclosure logical id(0x%016llx), slot( %d)\n",
        __func__,
        (u64)pcie_device->enclosure_logical_id,
        pcie_device->slot));
 if (pcie_device->connector_name[0] != '\0')
  dewtprintk(ioc,
      ioc_info(ioc, "%s: enclosure level(0x%04x), connector name( %s)\n",
        __func__, pcie_device->enclosure_level,
        pcie_device->connector_name));

 spin_lock_irqsave(&ioc->pcie_device_lock, flags);
 pcie_device_get(pcie_device);
 list_add_tail(&pcie_device->list, &ioc->pcie_device_list);
 spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);

 if (pcie_device->access_status ==
     MPI26_PCIEDEV0_ASTATUS_DEVICE_BLOCKED) {
  clear_bit(pcie_device->handle, ioc->pend_os_device_add);
  return;
 }
 if (scsi_add_device(ioc->shost, PCIE_CHANNEL, pcie_device->id, 0)) {
  _scsih_pcie_device_remove(ioc, pcie_device);
 } else if (!pcie_device->starget) {
  if (!ioc->is_driver_loading) {
/*TODO-- Need to find out whether this condition will occur or not*/
   clear_bit(pcie_device->handle, ioc->pend_os_device_add);
  }
 } else
  clear_bit(pcie_device->handle, ioc->pend_os_device_add);
}

/*
 * _scsih_pcie_device_init_add - insert pcie_device to the init list.
 * @ioc: per adapter object
 * @pcie_device: the pcie_device object
 * Context: This function will acquire ioc->pcie_device_lock.
 *
 * Adding new object at driver load time to the ioc->pcie_device_init_list.
 */

static void
_scsih_pcie_device_init_add(struct MPT3SAS_ADAPTER *ioc,
    struct _pcie_device *pcie_device)
{
 unsigned long flags;

 dewtprintk(ioc,
     ioc_info(ioc, "%s: handle (0x%04x), wwid(0x%016llx)\n",
       __func__,
       pcie_device->handle, (u64)pcie_device->wwid));
 if (pcie_device->enclosure_handle != 0)
  dewtprintk(ioc,
      ioc_info(ioc, "%s: enclosure logical id(0x%016llx), slot( %d)\n",
        __func__,
        (u64)pcie_device->enclosure_logical_id,
        pcie_device->slot));
 if (pcie_device->connector_name[0] != '\0')
  dewtprintk(ioc,
      ioc_info(ioc, "%s: enclosure level(0x%04x), connector name( %s)\n",
        __func__, pcie_device->enclosure_level,
        pcie_device->connector_name));

 spin_lock_irqsave(&ioc->pcie_device_lock, flags);
 pcie_device_get(pcie_device);
 list_add_tail(&pcie_device->list, &ioc->pcie_device_init_list);
 if (pcie_device->access_status !=
     MPI26_PCIEDEV0_ASTATUS_DEVICE_BLOCKED)
  _scsih_determine_boot_device(ioc, pcie_device, PCIE_CHANNEL);
 spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
}
/**
 * _scsih_raid_device_find_by_id - raid device search
 * @ioc: per adapter object
 * @id: sas device target id
 * @channel: sas device channel
 * Context: Calling function should acquire ioc->raid_device_lock
 *
 * This searches for raid_device based on target id, then return raid_device
 * object.
 */

static struct _raid_device *
_scsih_raid_device_find_by_id(struct MPT3SAS_ADAPTER *ioc, int id, int channel)
{
 struct _raid_device *raid_device, *r;

 r = NULL;
 list_for_each_entry(raid_device, &ioc->raid_device_list, list) {
  if (raid_device->id == id && raid_device->channel == channel) {
   r = raid_device;
   goto out;
  }
 }

 out:
 return r;
}

/**
 * mpt3sas_raid_device_find_by_handle - raid device search
 * @ioc: per adapter object
 * @handle: sas device handle (assigned by firmware)
 * Context: Calling function should acquire ioc->raid_device_lock
 *
 * This searches for raid_device based on handle, then return raid_device
 * object.
 */

struct _raid_device *
mpt3sas_raid_device_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
{
 struct _raid_device *raid_device, *r;

 r = NULL;
 list_for_each_entry(raid_device, &ioc->raid_device_list, list) {
  if (raid_device->handle != handle)
   continue;
  r = raid_device;
  goto out;
 }

 out:
 return r;
}

/**
 * _scsih_raid_device_find_by_wwid - raid device search
 * @ioc: per adapter object
 * @wwid: ?
 * Context: Calling function should acquire ioc->raid_device_lock
 *
 * This searches for raid_device based on wwid, then return raid_device
 * object.
 */

static struct _raid_device *
_scsih_raid_device_find_by_wwid(struct MPT3SAS_ADAPTER *ioc, u64 wwid)
{
 struct _raid_device *raid_device, *r;

 r = NULL;
 list_for_each_entry(raid_device, &ioc->raid_device_list, list) {
  if (raid_device->wwid != wwid)
   continue;
  r = raid_device;
  goto out;
 }

 out:
 return r;
}

/**
 * _scsih_raid_device_add - add raid_device object
 * @ioc: per adapter object
 * @raid_device: raid_device object
 *
 * This is added to the raid_device_list link list.
 */

static void
_scsih_raid_device_add(struct MPT3SAS_ADAPTER *ioc,
 struct _raid_device *raid_device)
{
 unsigned long flags;

 dewtprintk(ioc,
     ioc_info(ioc, "%s: handle(0x%04x), wwid(0x%016llx)\n",
       __func__,
       raid_device->handle, (u64)raid_device->wwid));

 spin_lock_irqsave(&ioc->raid_device_lock, flags);
 list_add_tail(&raid_device->list, &ioc->raid_device_list);
 spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
}

/**
 * _scsih_raid_device_remove - delete raid_device object
 * @ioc: per adapter object
 * @raid_device: raid_device object
 *
 */

static void
_scsih_raid_device_remove(struct MPT3SAS_ADAPTER *ioc,
 struct _raid_device *raid_device)
{
 unsigned long flags;

 spin_lock_irqsave(&ioc->raid_device_lock, flags);
 list_del(&raid_device->list);
 kfree(raid_device);
 spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
}

/**
 * mpt3sas_scsih_expander_find_by_handle - expander device search
 * @ioc: per adapter object
 * @handle: expander handle (assigned by firmware)
 * Context: Calling function should acquire ioc->sas_device_lock
 *
 * This searches for expander device based on handle, then returns the
 * sas_node object.
 */

struct _sas_node *
mpt3sas_scsih_expander_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
{
 struct _sas_node *sas_expander, *r;

 r = NULL;
 list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) {
  if (sas_expander->handle != handle)
   continue;
  r = sas_expander;
  goto out;
 }
 out:
 return r;
}

/**
 * mpt3sas_scsih_enclosure_find_by_handle - exclosure device search
 * @ioc: per adapter object
 * @handle: enclosure handle (assigned by firmware)
 * Context: Calling function should acquire ioc->sas_device_lock
 *
 * This searches for enclosure device based on handle, then returns the
 * enclosure object.
 */

static struct _enclosure_node *
mpt3sas_scsih_enclosure_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
{
 struct _enclosure_node *enclosure_dev, *r;

 r = NULL;
 list_for_each_entry(enclosure_dev, &ioc->enclosure_list, list) {
  if (le16_to_cpu(enclosure_dev->pg0.EnclosureHandle) != handle)
   continue;
  r = enclosure_dev;
  goto out;
 }
out:
 return r;
}
/**
 * mpt3sas_scsih_expander_find_by_sas_address - expander device search
 * @ioc: per adapter object
 * @sas_address: sas address
 * @port: hba port entry
 * Context: Calling function should acquire ioc->sas_node_lock.
 *
 * This searches for expander device based on sas_address & port number,
 * then returns the sas_node object.
 */

struct _sas_node *
mpt3sas_scsih_expander_find_by_sas_address(struct MPT3SAS_ADAPTER *ioc,
 u64 sas_address, struct hba_port *port)
{
 struct _sas_node *sas_expander, *r = NULL;

 if (!port)
  return r;

 list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) {
  if (sas_expander->sas_address != sas_address)
   continue;
  if (sas_expander->port != port)
   continue;
  r = sas_expander;
  goto out;
 }
 out:
 return r;
}

/**
 * _scsih_expander_node_add - insert expander device to the list.
 * @ioc: per adapter object
 * @sas_expander: the sas_device object
 * Context: This function will acquire ioc->sas_node_lock.
 *
 * Adding new object to the ioc->sas_expander_list.
 */

static void
_scsih_expander_node_add(struct MPT3SAS_ADAPTER *ioc,
 struct _sas_node *sas_expander)
{
 unsigned long flags;

 spin_lock_irqsave(&ioc->sas_node_lock, flags);
 list_add_tail(&sas_expander->list, &ioc->sas_expander_list);
 spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
}

/**
 * _scsih_is_end_device - determines if device is an end device
 * @device_info: bitfield providing information about the device.
 * Context: none
 *
 * Return: 1 if end device.
 */

static int
_scsih_is_end_device(u32 device_info)
{
 if (device_info & MPI2_SAS_DEVICE_INFO_END_DEVICE &&
  ((device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) |
  (device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET) |
  (device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE)))
  return 1;
 else
  return 0;
}

/**
 * _scsih_is_nvme_pciescsi_device - determines if
 * device is an pcie nvme/scsi device
 * @device_info: bitfield providing information about the device.
 * Context: none
 *
 * Returns 1 if device is pcie device type nvme/scsi.
 */

static int
_scsih_is_nvme_pciescsi_device(u32 device_info)
{
 if (((device_info & MPI26_PCIE_DEVINFO_MASK_DEVICE_TYPE)
     == MPI26_PCIE_DEVINFO_NVME) ||
     ((device_info & MPI26_PCIE_DEVINFO_MASK_DEVICE_TYPE)
     == MPI26_PCIE_DEVINFO_SCSI))
  return 1;
 else
  return 0;
}

/**
 * _scsih_scsi_lookup_find_by_target - search for matching channel:id
 * @ioc: per adapter object
 * @id: target id
 * @channel: channel
 * Context: This function will acquire ioc->scsi_lookup_lock.
 *
 * This will search for a matching channel:id in the scsi_lookup array,
 * returning 1 if found.
 */

static u8
_scsih_scsi_lookup_find_by_target(struct MPT3SAS_ADAPTER *ioc, int id,
 int channel)
{
 int smid;
 struct scsi_cmnd *scmd;

 for (smid = 1;
      smid <= ioc->shost->can_queue; smid++) {
  scmd = mpt3sas_scsih_scsi_lookup_get(ioc, smid);
  if (!scmd)
   continue;
  if (scmd->device->id == id &&
      scmd->device->channel == channel)
   return 1;
 }
 return 0;
}

/**
 * _scsih_scsi_lookup_find_by_lun - search for matching channel:id:lun
 * @ioc: per adapter object
 * @id: target id
 * @lun: lun number
 * @channel: channel
 * Context: This function will acquire ioc->scsi_lookup_lock.
 *
 * This will search for a matching channel:id:lun in the scsi_lookup array,
 * returning 1 if found.
 */

static u8
_scsih_scsi_lookup_find_by_lun(struct MPT3SAS_ADAPTER *ioc, int id,
 unsigned int lun, int channel)
{
 int smid;
 struct scsi_cmnd *scmd;

 for (smid = 1; smid <= ioc->shost->can_queue; smid++) {

  scmd = mpt3sas_scsih_scsi_lookup_get(ioc, smid);
  if (!scmd)
   continue;
  if (scmd->device->id == id &&
      scmd->device->channel == channel &&
      scmd->device->lun == lun)
   return 1;
 }
 return 0;
}

/**
 * mpt3sas_scsih_scsi_lookup_get - returns scmd entry
 * @ioc: per adapter object
 * @smid: system request message index
 *
 * Return: the smid stored scmd pointer.
 * Then will dereference the stored scmd pointer.
 */

struct scsi_cmnd *
mpt3sas_scsih_scsi_lookup_get(struct MPT3SAS_ADAPTER *ioc, u16 smid)
{
 struct scsi_cmnd *scmd = NULL;
 struct scsiio_tracker *st;
 Mpi25SCSIIORequest_t *mpi_request;
 u16 tag = smid - 1;

 if (smid > 0  &&
     smid <= ioc->scsiio_depth - INTERNAL_SCSIIO_CMDS_COUNT) {
  u32 unique_tag =
      ioc->io_queue_num[tag] << BLK_MQ_UNIQUE_TAG_BITS | tag;

  mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);

  /*
 * If SCSI IO request is outstanding at driver level then
 * DevHandle filed must be non-zero. If DevHandle is zero
 * then it means that this smid is free at driver level,
 * so return NULL.
 */

  if (!mpi_request->DevHandle)
   return scmd;

  scmd = scsi_host_find_tag(ioc->shost, unique_tag);
  if (scmd) {
   st = scsi_cmd_priv(scmd);
   if (st->cb_idx == 0xFF || st->smid == 0)
    scmd = NULL;
  }
 }
 return scmd;
}

/**
 * scsih_change_queue_depth - setting device queue depth
 * @sdev: scsi device struct
 * @qdepth: requested queue depth
 *
 * Return: queue depth.
 */

static int
scsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
{
 struct Scsi_Host *shost = sdev->host;
 int max_depth;
 struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
 struct MPT3SAS_DEVICE *sas_device_priv_data;
 struct MPT3SAS_TARGET *sas_target_priv_data;
 struct _sas_device *sas_device;
 unsigned long flags;

 max_depth = shost->can_queue;

 /*
 * limit max device queue for SATA to 32 if enable_sdev_max_qd
 * is disabled.
 */

 if (ioc->enable_sdev_max_qd || ioc->is_gen35_ioc)
  goto not_sata;

 sas_device_priv_data = sdev->hostdata;
 if (!sas_device_priv_data)
  goto not_sata;
 sas_target_priv_data = sas_device_priv_data->sas_target;
 if (!sas_target_priv_data)
  goto not_sata;
 if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME))
  goto not_sata;

 spin_lock_irqsave(&ioc->sas_device_lock, flags);
 sas_device = __mpt3sas_get_sdev_from_target(ioc, sas_target_priv_data);
 if (sas_device) {
  if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE)
   max_depth = MPT3SAS_SATA_QUEUE_DEPTH;

  sas_device_put(sas_device);
 }
 spin_unlock_irqrestore(&ioc->sas_device_lock, flags);

 not_sata:

 if (!sdev->tagged_supported)
  max_depth = 1;
 if (qdepth > max_depth)
  qdepth = max_depth;
 scsi_change_queue_depth(sdev, qdepth);
 sdev_printk(KERN_INFO, sdev,
     "qdepth(%d), tagged(%d), scsi_level(%d), cmd_que(%d)\n",
     sdev->queue_depth, sdev->tagged_supported,
     sdev->scsi_level, ((sdev->inquiry[7] & 2) >> 1));
 return sdev->queue_depth;
}

/**
 * mpt3sas_scsih_change_queue_depth - setting device queue depth
 * @sdev: scsi device struct
 * @qdepth: requested queue depth
 *
 * Returns nothing.
 */

void
mpt3sas_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
{
 struct Scsi_Host *shost = sdev->host;
 struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);

 if (ioc->enable_sdev_max_qd)
  qdepth = shost->can_queue;

 scsih_change_queue_depth(sdev, qdepth);
}

/**
 * scsih_target_alloc - target add routine
 * @starget: scsi target struct
 *
 * Return: 0 if ok. Any other return is assumed to be an error and
 * the device is ignored.
 */

static int
scsih_target_alloc(struct scsi_target *starget)
{
 struct Scsi_Host *shost = dev_to_shost(&starget->dev);
 struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
 struct MPT3SAS_TARGET *sas_target_priv_data;
 struct _sas_device *sas_device;
 struct _raid_device *raid_device;
 struct _pcie_device *pcie_device;
 unsigned long flags;
 struct sas_rphy *rphy;

 sas_target_priv_data = kzalloc(sizeof(*sas_target_priv_data),
           GFP_KERNEL);
 if (!sas_target_priv_data)
  return -ENOMEM;

 starget->hostdata = sas_target_priv_data;
 sas_target_priv_data->starget = starget;
 sas_target_priv_data->handle = MPT3SAS_INVALID_DEVICE_HANDLE;

 /* RAID volumes */
 if (starget->channel == RAID_CHANNEL) {
  spin_lock_irqsave(&ioc->raid_device_lock, flags);
  raid_device = _scsih_raid_device_find_by_id(ioc, starget->id,
      starget->channel);
  if (raid_device) {
   sas_target_priv_data->handle = raid_device->handle;
   sas_target_priv_data->sas_address = raid_device->wwid;
   sas_target_priv_data->flags |= MPT_TARGET_FLAGS_VOLUME;
   if (ioc->is_warpdrive)
    sas_target_priv_data->raid_device = raid_device;
   raid_device->starget = starget;
  }
  spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
  return 0;
 }

 /* PCIe devices */
 if (starget->channel == PCIE_CHANNEL) {
  spin_lock_irqsave(&ioc->pcie_device_lock, flags);
  pcie_device = __mpt3sas_get_pdev_by_idchannel(ioc, starget->id,
   starget->channel);
  if (pcie_device) {
   sas_target_priv_data->handle = pcie_device->handle;
   sas_target_priv_data->sas_address = pcie_device->wwid;
   sas_target_priv_data->port = NULL;
   sas_target_priv_data->pcie_dev = pcie_device;
   pcie_device->starget = starget;
   pcie_device->id = starget->id;
   pcie_device->channel = starget->channel;
   sas_target_priv_data->flags |=
    MPT_TARGET_FLAGS_PCIE_DEVICE;
   if (pcie_device->fast_path)
    sas_target_priv_data->flags |=
     MPT_TARGET_FASTPATH_IO;
  }
  spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
  return 0;
 }

 /* sas/sata devices */
 spin_lock_irqsave(&ioc->sas_device_lock, flags);
 rphy = dev_to_rphy(starget->dev.parent);
 sas_device = __mpt3sas_get_sdev_by_rphy(ioc, rphy);

 if (sas_device) {
  sas_target_priv_data->handle = sas_device->handle;
  sas_target_priv_data->sas_address = sas_device->sas_address;
  sas_target_priv_data->port = sas_device->port;
  sas_target_priv_data->sas_dev = sas_device;
  sas_device->starget = starget;
  sas_device->id = starget->id;
  sas_device->channel = starget->channel;
  if (test_bit(sas_device->handle, ioc->pd_handles))
   sas_target_priv_data->flags |=
       MPT_TARGET_FLAGS_RAID_COMPONENT;
  if (sas_device->fast_path)
   sas_target_priv_data->flags |=
     MPT_TARGET_FASTPATH_IO;
 }
 spin_unlock_irqrestore(&ioc->sas_device_lock, flags);

 return 0;
}

/**
 * scsih_target_destroy - target destroy routine
 * @starget: scsi target struct
 */

static void
scsih_target_destroy(struct scsi_target *starget)
{
 struct Scsi_Host *shost = dev_to_shost(&starget->dev);
 struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
 struct MPT3SAS_TARGET *sas_target_priv_data;
 struct _sas_device *sas_device;
 struct _raid_device *raid_device;
 struct _pcie_device *pcie_device;
 unsigned long flags;

 sas_target_priv_data = starget->hostdata;
 if (!sas_target_priv_data)
  return;

 if (starget->channel == RAID_CHANNEL) {
  spin_lock_irqsave(&ioc->raid_device_lock, flags);
  raid_device = _scsih_raid_device_find_by_id(ioc, starget->id,
      starget->channel);
  if (raid_device) {
   raid_device->starget = NULL;
   raid_device->sdev = NULL;
  }
  spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
  goto out;
 }

 if (starget->channel == PCIE_CHANNEL) {
  spin_lock_irqsave(&ioc->pcie_device_lock, flags);
  pcie_device = __mpt3sas_get_pdev_from_target(ioc,
       sas_target_priv_data);
  if (pcie_device && (pcie_device->starget == starget) &&
   (pcie_device->id == starget->id) &&
   (pcie_device->channel == starget->channel))
   pcie_device->starget = NULL;

  if (pcie_device) {
   /*
 * Corresponding get() is in _scsih_target_alloc()
 */

   sas_target_priv_data->pcie_dev = NULL;
   pcie_device_put(pcie_device);
   pcie_device_put(pcie_device);
  }
  spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);
  goto out;
 }

 spin_lock_irqsave(&ioc->sas_device_lock, flags);
 sas_device = __mpt3sas_get_sdev_from_target(ioc, sas_target_priv_data);
 if (sas_device && (sas_device->starget == starget) &&
     (sas_device->id == starget->id) &&
     (sas_device->channel == starget->channel))
  sas_device->starget = NULL;

 if (sas_device) {
  /*
 * Corresponding get() is in _scsih_target_alloc()
 */

  sas_target_priv_data->sas_dev = NULL;
  sas_device_put(sas_device);

  sas_device_put(sas_device);
 }
 spin_unlock_irqrestore(&ioc->sas_device_lock, flags);

 out:
 kfree(sas_target_priv_data);
 starget->hostdata = NULL;
}

/**
 * scsih_sdev_init - device add routine
 * @sdev: scsi device struct
 *
 * Return: 0 if ok. Any other return is assumed to be an error and
 * the device is ignored.
 */

static int
scsih_sdev_init(struct scsi_device *sdev)
{
 struct Scsi_Host *shost;
 struct MPT3SAS_ADAPTER *ioc;
 struct MPT3SAS_TARGET *sas_target_priv_data;
 struct MPT3SAS_DEVICE *sas_device_priv_data;
 struct scsi_target *starget;
 struct _raid_device *raid_device;
 struct _sas_device *sas_device;
 struct _pcie_device *pcie_device;
 unsigned long flags;

 sas_device_priv_data = kzalloc(sizeof(*sas_device_priv_data),
           GFP_KERNEL);
 if (!sas_device_priv_data)
  return -ENOMEM;

 sas_device_priv_data->lun = sdev->lun;
 sas_device_priv_data->flags = MPT_DEVICE_FLAGS_INIT;

 starget = scsi_target(sdev);
 sas_target_priv_data = starget->hostdata;
 sas_target_priv_data->num_luns++;
 sas_device_priv_data->sas_target = sas_target_priv_data;
 sdev->hostdata = sas_device_priv_data;
 if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT))
  sdev->no_uld_attach = 1;

 shost = dev_to_shost(&starget->dev);
 ioc = shost_priv(shost);
 if (starget->channel == RAID_CHANNEL) {
  spin_lock_irqsave(&ioc->raid_device_lock, flags);
  raid_device = _scsih_raid_device_find_by_id(ioc,
      starget->id, starget->channel);
  if (raid_device)
   raid_device->sdev = sdev; /* raid is single lun */
  spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
 }
 if (starget->channel == PCIE_CHANNEL) {
  spin_lock_irqsave(&ioc->pcie_device_lock, flags);
  pcie_device = __mpt3sas_get_pdev_by_wwid(ioc,
    sas_target_priv_data->sas_address);
  if (pcie_device && (pcie_device->starget == NULL)) {
   sdev_printk(KERN_INFO, sdev,
       "%s : pcie_device->starget set to starget @ %d\n",
       __func__, __LINE__);
   pcie_device->starget = starget;
  }

  if (pcie_device)
   pcie_device_put(pcie_device);
  spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);

 } else  if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) {
  spin_lock_irqsave(&ioc->sas_device_lock, flags);
  sas_device = __mpt3sas_get_sdev_by_addr(ioc,
      sas_target_priv_data->sas_address,
      sas_target_priv_data->port);
  if (sas_device && (sas_device->starget == NULL)) {
   sdev_printk(KERN_INFO, sdev,
   "%s : sas_device->starget set to starget @ %d\n",
        __func__, __LINE__);
   sas_device->starget = starget;
  }

  if (sas_device)
   sas_device_put(sas_device);

  spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 }

 return 0;
}

/**
 * scsih_sdev_destroy - device destroy routine
 * @sdev: scsi device struct
 */

static void
scsih_sdev_destroy(struct scsi_device *sdev)
{
 struct MPT3SAS_TARGET *sas_target_priv_data;
 struct scsi_target *starget;
 struct Scsi_Host *shost;
 struct MPT3SAS_ADAPTER *ioc;
 struct _sas_device *sas_device;
 struct _pcie_device *pcie_device;
 unsigned long flags;

 if (!sdev->hostdata)
  return;

 starget = scsi_target(sdev);
 sas_target_priv_data = starget->hostdata;
 sas_target_priv_data->num_luns--;

 shost = dev_to_shost(&starget->dev);
 ioc = shost_priv(shost);

 if (sas_target_priv_data->flags & MPT_TARGET_FLAGS_PCIE_DEVICE) {
  spin_lock_irqsave(&ioc->pcie_device_lock, flags);
  pcie_device = __mpt3sas_get_pdev_from_target(ioc,
    sas_target_priv_data);
  if (pcie_device && !sas_target_priv_data->num_luns)
   pcie_device->starget = NULL;

  if (pcie_device)
   pcie_device_put(pcie_device);

  spin_unlock_irqrestore(&ioc->pcie_device_lock, flags);

 } else if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) {
  spin_lock_irqsave(&ioc->sas_device_lock, flags);
  sas_device = __mpt3sas_get_sdev_from_target(ioc,
    sas_target_priv_data);
  if (sas_device && !sas_target_priv_data->num_luns)
   sas_device->starget = NULL;

  if (sas_device)
   sas_device_put(sas_device);
  spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 }

 kfree(sdev->hostdata);
 sdev->hostdata = NULL;
}

/**
 * _scsih_display_sata_capabilities - sata capabilities
 * @ioc: per adapter object
 * @handle: device handle
 * @sdev: scsi device struct
 */

static void
_scsih_display_sata_capabilities(struct MPT3SAS_ADAPTER *ioc,
 u16 handle, struct scsi_device *sdev)
{
 Mpi2ConfigReply_t mpi_reply;
 Mpi2SasDevicePage0_t sas_device_pg0;
 u32 ioc_status;
 u16 flags;
 u32 device_info;

 if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
     MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
  ioc_err(ioc, "failure at %s:%d/%s()!\n",
   __FILE__, __LINE__, __func__);
  return;
 }

 ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
     MPI2_IOCSTATUS_MASK;
 if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
  ioc_err(ioc, "failure at %s:%d/%s()!\n",
   __FILE__, __LINE__, __func__);
  return;
 }

 flags = le16_to_cpu(sas_device_pg0.Flags);
 device_info = le32_to_cpu(sas_device_pg0.DeviceInfo);

 sdev_printk(KERN_INFO, sdev,
     "atapi(%s), ncq(%s), asyn_notify(%s), smart(%s), fua(%s), "
     "sw_preserve(%s)\n",
     (device_info & MPI2_SAS_DEVICE_INFO_ATAPI_DEVICE) ? "y" : "n",
     (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_NCQ_SUPPORTED) ? "y" : "n",
     (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_ASYNCHRONOUS_NOTIFY) ? "y" :
     "n",
     (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_SMART_SUPPORTED) ? "y" : "n",
     (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_FUA_SUPPORTED) ? "y" : "n",
     (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_SW_PRESERVE) ? "y" : "n");
}

/*
 * raid transport support -
 * Enabled for SLES11 and newer, in older kernels the driver will panic when
 * unloading the driver followed by a load - I believe that the subroutine
 * raid_class_release() is not cleaning up properly.
 */


/**
 * scsih_is_raid - return boolean indicating device is raid volume
 * @dev: the device struct object
 */

static int
scsih_is_raid(struct device *dev)
{
 struct scsi_device *sdev = to_scsi_device(dev);
 struct MPT3SAS_ADAPTER *ioc = shost_priv(sdev->host);

 if (ioc->is_warpdrive)
  return 0;
 return (sdev->channel == RAID_CHANNEL) ? 1 : 0;
}

static int
scsih_is_nvme(struct device *dev)
{
 struct scsi_device *sdev = to_scsi_device(dev);

 return (sdev->channel == PCIE_CHANNEL) ? 1 : 0;
}

/**
 * scsih_get_resync - get raid volume resync percent complete
 * @dev: the device struct object
 */

static void
scsih_get_resync(struct device *dev)
{
 struct scsi_device *sdev = to_scsi_device(dev);
 struct MPT3SAS_ADAPTER *ioc = shost_priv(sdev->host);
 static struct _raid_device *raid_device;
 unsigned long flags;
 Mpi2RaidVolPage0_t vol_pg0;
 Mpi2ConfigReply_t mpi_reply;
 u32 volume_status_flags;
 u8 percent_complete;
 u16 handle;

 percent_complete = 0;
 handle = 0;
 if (ioc->is_warpdrive)
  goto out;

 spin_lock_irqsave(&ioc->raid_device_lock, flags);
 raid_device = _scsih_raid_device_find_by_id(ioc, sdev->id,
     sdev->channel);
 if (raid_device) {
  handle = raid_device->handle;
  percent_complete = raid_device->percent_complete;
 }
 spin_unlock_irqrestore(&ioc->raid_device_lock, flags);

 if (!handle)
  goto out;

 if (mpt3sas_config_get_raid_volume_pg0(ioc, &mpi_reply, &vol_pg0,
      MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle,
      sizeof(Mpi2RaidVolPage0_t))) {
  ioc_err(ioc, "failure at %s:%d/%s()!\n",
   __FILE__, __LINE__, __func__);
  percent_complete = 0;
  goto out;
 }

 volume_status_flags = le32_to_cpu(vol_pg0.VolumeStatusFlags);
 if (!(volume_status_flags &
     MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS))
  percent_complete = 0;

 out:

 switch (ioc->hba_mpi_version_belonged) {
 case MPI2_VERSION:
  raid_set_resync(mpt2sas_raid_template, dev, percent_complete);
  break;
 case MPI25_VERSION:
 case MPI26_VERSION:
  raid_set_resync(mpt3sas_raid_template, dev, percent_complete);
  break;
 }
}

/**
 * scsih_get_state - get raid volume level
 * @dev: the device struct object
 */

static void
--> --------------------

--> maximum size reached

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

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

¤ Dauer der Verarbeitung: 0.42 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.