[SCSI] libfc: call ddp setup for only FCP reads to avoid accessing junk fsp pointer
[safe/jmp/linux-2.6] / drivers / scsi / mpt2sas / mpt2sas_scsih.c
index b1326bf..efabea1 100644 (file)
@@ -2,7 +2,7 @@
  * Scsi Host Layer for MPT (Message Passing Technology) based controllers
  *
  * This code is based on drivers/scsi/mpt2sas/mpt2_scsih.c
- * Copyright (C) 2007-2008  LSI Corporation
+ * Copyright (C) 2007-2009  LSI Corporation
  *  (mailto:DL-MPTFusionLinux@lsi.com)
  *
  * This program is free software; you can redistribute it and/or
@@ -76,9 +76,13 @@ static u8 tm_cb_idx = -1;
 static u8 ctl_cb_idx = -1;
 static u8 base_cb_idx = -1;
 static u8 transport_cb_idx = -1;
+static u8 scsih_cb_idx = -1;
 static u8 config_cb_idx = -1;
 static int mpt_ids;
 
+static u8 tm_tr_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 "
@@ -103,13 +107,13 @@ struct sense_info {
 };
 
 
-#define MPT2SAS_RESCAN_AFTER_HOST_RESET (0xFFFF)
 /**
  * struct fw_event_work - firmware event struct
  * @list: link list framework
  * @work: work object (ioc->fault_reset_work_q)
  * @ioc: per adapter object
  * @VF_ID: virtual function id
+ * @VP_ID: virtual port id
  * @host_reset_handling: handling events during host reset
  * @ignore: flag meaning this event has been marked to ignore
  * @event: firmware event MPI2_EVENT_XXX defined in mpt2_ioc.h
@@ -122,6 +126,7 @@ struct fw_event_work {
        struct work_struct      work;
        struct MPT2SAS_ADAPTER *ioc;
        u8                      VF_ID;
+       u8                      VP_ID;
        u8                      host_reset_handling;
        u8                      ignore;
        u16                     event;
@@ -139,8 +144,10 @@ struct fw_event_work {
  * @lun: lun number
  * @cdb_length: cdb length
  * @cdb: cdb contents
- * @valid_reply: flag set for reply message
  * @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
@@ -162,6 +169,8 @@ struct _scsi_io_transfer {
        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;
@@ -188,10 +197,28 @@ static struct pci_device_id scsih_pci_table[] = {
                PCI_ANY_ID, PCI_ANY_ID },
        { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_3,
                PCI_ANY_ID, PCI_ANY_ID },
+       /* Meteor ~ 2116 */
        { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_1,
                PCI_ANY_ID, PCI_ANY_ID },
        { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_2,
                PCI_ANY_ID, PCI_ANY_ID },
+       /* Thunderbolt ~ 2208 */
+       { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_1,
+               PCI_ANY_ID, PCI_ANY_ID },
+       { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_2,
+               PCI_ANY_ID, PCI_ANY_ID },
+       { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_3,
+               PCI_ANY_ID, PCI_ANY_ID },
+       { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_4,
+               PCI_ANY_ID, PCI_ANY_ID },
+       { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_5,
+               PCI_ANY_ID, PCI_ANY_ID },
+       { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_6,
+               PCI_ANY_ID, PCI_ANY_ID },
+       { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_7,
+               PCI_ANY_ID, PCI_ANY_ID },
+       { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_8,
+               PCI_ANY_ID, PCI_ANY_ID },
        {0}     /* Terminating entry */
 };
 MODULE_DEVICE_TABLE(pci, scsih_pci_table);
@@ -309,6 +336,47 @@ _scsih_is_boot_device(u64 sas_address, u64 device_name,
 }
 
 /**
+ * _scsih_get_sas_address - set the sas_address for given device handle
+ * @handle: device handle
+ * @sas_address: sas address
+ *
+ * Returns 0 success, non-zero when failure
+ */
+static int
+_scsih_get_sas_address(struct MPT2SAS_ADAPTER *ioc, u16 handle,
+    u64 *sas_address)
+{
+       Mpi2SasDevicePage0_t sas_device_pg0;
+       Mpi2ConfigReply_t mpi_reply;
+       u32 ioc_status;
+
+       if (handle <= ioc->sas_hba.num_phys) {
+               *sas_address = ioc->sas_hba.sas_address;
+               return 0;
+       } else
+               *sas_address = 0;
+
+       if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
+           MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
+               printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+                   ioc->name, __FILE__, __LINE__, __func__);
+               return -ENXIO;
+       }
+
+       ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+           MPI2_IOCSTATUS_MASK;
+       if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+               printk(MPT2SAS_ERR_FMT "handle(0x%04x), ioc_status(0x%04x)"
+                   "\nfailure at %s:%d/%s()!\n", ioc->name, handle, ioc_status,
+                    __FILE__, __LINE__, __func__);
+               return -EIO;
+       }
+
+       *sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
+       return 0;
+}
+
+/**
  * _scsih_determine_boot_device - determine boot device.
  * @ioc: per adapter object
  * @device: either sas_device or raid_device object
@@ -502,8 +570,6 @@ _scsih_sas_device_add(struct MPT2SAS_ADAPTER *ioc,
     struct _sas_device *sas_device)
 {
        unsigned long flags;
-       u16 handle, parent_handle;
-       u64 sas_address;
 
        dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: handle"
            "(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__,
@@ -513,10 +579,8 @@ _scsih_sas_device_add(struct MPT2SAS_ADAPTER *ioc,
        list_add_tail(&sas_device->list, &ioc->sas_device_list);
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
-       handle = sas_device->handle;
-       parent_handle = sas_device->parent_handle;
-       sas_address = sas_device->sas_address;
-       if (!mpt2sas_transport_port_add(ioc, handle, parent_handle))
+       if (!mpt2sas_transport_port_add(ioc, sas_device->handle,
+            sas_device->sas_address_parent))
                _scsih_sas_device_remove(ioc, sas_device);
 }
 
@@ -545,31 +609,6 @@ _scsih_sas_device_init_add(struct MPT2SAS_ADAPTER *ioc,
 }
 
 /**
- * mpt2sas_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 *
-mpt2sas_scsih_expander_find_by_handle(struct MPT2SAS_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;
-}
-
-/**
  * _scsih_raid_device_find_by_id - raid device search
  * @ioc: per adapter object
  * @id: sas device target id
@@ -691,6 +730,31 @@ _scsih_raid_device_remove(struct MPT2SAS_ADAPTER *ioc,
 }
 
 /**
+ * mpt2sas_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 *
+mpt2sas_scsih_expander_find_by_handle(struct MPT2SAS_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;
+}
+
+/**
  * mpt2sas_scsih_expander_find_by_sas_address - expander device search
  * @ioc: per adapter object
  * @sas_address: sas address
@@ -757,66 +821,16 @@ _scsih_is_end_device(u32 device_info)
 }
 
 /**
- * _scsih_scsi_lookup_get - returns scmd entry
+ * mptscsih_get_scsi_lookup - returns scmd entry
  * @ioc: per adapter object
  * @smid: system request message index
- * Context: This function will acquire ioc->scsi_lookup_lock.
  *
  * Returns the smid stored scmd pointer.
  */
 static struct scsi_cmnd *
 _scsih_scsi_lookup_get(struct MPT2SAS_ADAPTER *ioc, u16 smid)
 {
-       unsigned long   flags;
-       struct scsi_cmnd *scmd;
-
-       spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
-       scmd = ioc->scsi_lookup[smid - 1].scmd;
-       spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
-       return scmd;
-}
-
-/**
- * mptscsih_getclear_scsi_lookup - returns scmd entry
- * @ioc: per adapter object
- * @smid: system request message index
- * Context: This function will acquire ioc->scsi_lookup_lock.
- *
- * Returns the smid stored scmd pointer, as well as clearing the scmd pointer.
- */
-static struct scsi_cmnd *
-_scsih_scsi_lookup_getclear(struct MPT2SAS_ADAPTER *ioc, u16 smid)
-{
-       unsigned long   flags;
-       struct scsi_cmnd *scmd;
-
-       spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
-       scmd = ioc->scsi_lookup[smid - 1].scmd;
-       ioc->scsi_lookup[smid - 1].scmd = NULL;
-       spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
-       return scmd;
-}
-
-/**
- * _scsih_scsi_lookup_set - updates scmd entry in lookup
- * @ioc: per adapter object
- * @smid: system request message index
- * @scmd: pointer to scsi command object
- * Context: This function will acquire ioc->scsi_lookup_lock.
- *
- * This will save scmd pointer in the scsi_lookup array.
- *
- * Return nothing.
- */
-static void
-_scsih_scsi_lookup_set(struct MPT2SAS_ADAPTER *ioc, u16 smid,
-    struct scsi_cmnd *scmd)
-{
-       unsigned long   flags;
-
-       spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
-       ioc->scsi_lookup[smid - 1].scmd = scmd;
-       spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+       return ioc->scsi_lookup[smid - 1].scmd;
 }
 
 /**
@@ -839,9 +853,9 @@ _scsih_scsi_lookup_find_by_scmd(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd
 
        spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
        smid = 0;
-       for (i = 0; i < ioc->request_depth; i++) {
+       for (i = 0; i < ioc->scsiio_depth; i++) {
                if (ioc->scsi_lookup[i].scmd == scmd) {
-                       smid = i + 1;
+                       smid = ioc->scsi_lookup[i].smid;
                        goto out;
                }
        }
@@ -870,7 +884,7 @@ _scsih_scsi_lookup_find_by_target(struct MPT2SAS_ADAPTER *ioc, int id,
 
        spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
        found = 0;
-       for (i = 0 ; i < ioc->request_depth; i++) {
+       for (i = 0 ; i < ioc->scsiio_depth; i++) {
                if (ioc->scsi_lookup[i].scmd &&
                    (ioc->scsi_lookup[i].scmd->device->id == id &&
                    ioc->scsi_lookup[i].scmd->device->channel == channel)) {
@@ -904,7 +918,7 @@ _scsih_scsi_lookup_find_by_lun(struct MPT2SAS_ADAPTER *ioc, int id,
 
        spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
        found = 0;
-       for (i = 0 ; i < ioc->request_depth; i++) {
+       for (i = 0 ; i < ioc->scsiio_depth; i++) {
                if (ioc->scsi_lookup[i].scmd &&
                    (ioc->scsi_lookup[i].scmd->device->id == id &&
                    ioc->scsi_lookup[i].scmd->device->channel == channel &&
@@ -1085,17 +1099,46 @@ _scsih_build_scatter_gather(struct MPT2SAS_ADAPTER *ioc,
  * _scsih_change_queue_depth - setting device queue depth
  * @sdev: scsi device struct
  * @qdepth: requested queue depth
+ * @reason: calling context
  *
  * Returns queue depth.
  */
 static int
-_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
+_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
 {
        struct Scsi_Host *shost = sdev->host;
        int max_depth;
        int tag_type;
+       struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+       struct MPT2SAS_DEVICE *sas_device_priv_data;
+       struct MPT2SAS_TARGET *sas_target_priv_data;
+       struct _sas_device *sas_device;
+       unsigned long flags;
+
+       if (reason != SCSI_QDEPTH_DEFAULT)
+               return -EOPNOTSUPP;
 
        max_depth = shost->can_queue;
+
+       /* limit max device queue for SATA to 32 */
+       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 = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
+          sas_device_priv_data->sas_target->sas_address);
+       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+       if (sas_device && sas_device->device_info &
+           MPI2_SAS_DEVICE_INFO_SATA_DEVICE)
+               max_depth = MPT2SAS_SATA_QUEUE_DEPTH;
+
+ not_sata:
+
        if (!sdev->tagged_supported)
                max_depth = 1;
        if (qdepth > max_depth)
@@ -1114,7 +1157,7 @@ _scsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
 }
 
 /**
- * _scsih_change_queue_depth - changing device queue tag type
+ * _scsih_change_queue_type - changing device queue tag type
  * @sdev: scsi device struct
  * @tag_type: requested tag type
  *
@@ -1502,7 +1545,13 @@ _scsih_slave_configure(struct scsi_device *sdev)
                        break;
                case MPI2_RAID_VOL_TYPE_RAID1E:
                        qdepth = MPT2SAS_RAID_QUEUE_DEPTH;
-                       r_level = "RAID1E";
+                       if (ioc->manu_pg10.OEMIdentifier &&
+                           (ioc->manu_pg10.GenericFlags0 &
+                           MFG10_GF0_R10_DISPLAY) &&
+                           !(raid_device->num_pds % 2))
+                               r_level = "RAID10";
+                       else
+                               r_level = "RAID1E";
                        break;
                case MPI2_RAID_VOL_TYPE_RAID1:
                        qdepth = MPT2SAS_RAID_QUEUE_DEPTH;
@@ -1524,7 +1573,7 @@ _scsih_slave_configure(struct scsi_device *sdev)
                    r_level, raid_device->handle,
                    (unsigned long long)raid_device->wwid,
                    raid_device->num_pds, ds);
-               _scsih_change_queue_depth(sdev, qdepth);
+               _scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT);
                return 0;
        }
 
@@ -1570,7 +1619,7 @@ _scsih_slave_configure(struct scsi_device *sdev)
                        _scsih_display_sata_capabilities(ioc, sas_device, sdev);
        }
 
-       _scsih_change_queue_depth(sdev, qdepth);
+       _scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT);
 
        if (ssp_target)
                sas_read_port_mode_page(sdev);
@@ -1674,23 +1723,24 @@ _scsih_response_code(struct MPT2SAS_ADAPTER *ioc, u8 response_code)
  * _scsih_tm_done - tm completion routine
  * @ioc: per adapter object
  * @smid: system request message index
- * @VF_ID: virtual function id
+ * @msix_index: MSIX table index supplied by the OS
  * @reply: reply message frame(lower 32bit addr)
  * Context: none.
  *
  * The callback handler when using scsih_issue_tm.
  *
- * Return nothing.
+ * Return 1 meaning mf should be freed from _base_interrupt
+ *        0 means the mf is freed from this function.
  */
-static void
-_scsih_tm_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
+static u8
+_scsih_tm_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
 {
        MPI2DefaultReply_t *mpi_reply;
 
        if (ioc->tm_cmds.status == MPT2_CMD_NOT_USED)
-               return;
+               return 1;
        if (ioc->tm_cmds.smid != smid)
-               return;
+               return 1;
        ioc->tm_cmds.status |= MPT2_CMD_COMPLETE;
        mpi_reply =  mpt2sas_base_get_reply_virt_addr(ioc, reply);
        if (mpi_reply) {
@@ -1699,6 +1749,7 @@ _scsih_tm_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
        }
        ioc->tm_cmds.status &= ~MPT2_CMD_PENDING;
        complete(&ioc->tm_cmds.done);
+       return 1;
 }
 
 /**
@@ -1785,18 +1836,18 @@ mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun,
        u16 smid = 0;
        u32 ioc_state;
        unsigned long timeleft;
-       u8 VF_ID = 0;
-       unsigned long flags;
 
-       spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
-       if (ioc->tm_cmds.status != MPT2_CMD_NOT_USED ||
-           ioc->shost_recovery) {
-               spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+       if (ioc->tm_cmds.status != MPT2_CMD_NOT_USED) {
+               printk(MPT2SAS_INFO_FMT "%s: tm_cmd busy!!!\n",
+                   __func__, ioc->name);
+               return;
+       }
+
+       if (ioc->shost_recovery) {
                printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n",
                    __func__, ioc->name);
                return;
        }
-       spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
 
        ioc_state = mpt2sas_base_get_iocstate(ioc, 0);
        if (ioc_state & MPI2_DOORBELL_USED) {
@@ -1811,7 +1862,7 @@ mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun,
                goto issue_host_reset;
        }
 
-       smid = mpt2sas_base_get_smid(ioc, ioc->tm_cb_idx);
+       smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_cb_idx);
        if (!smid) {
                printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
                    ioc->name, __func__);
@@ -1819,7 +1870,8 @@ mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun,
        }
 
        dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "sending tm: handle(0x%04x),"
-           " task_type(0x%02x), smid(%d)\n", ioc->name, handle, type, smid));
+           " task_type(0x%02x), smid(%d)\n", ioc->name, handle, type,
+           smid_task));
        ioc->tm_cmds.status = MPT2_CMD_PENDING;
        mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
        ioc->tm_cmds.smid = smid;
@@ -1828,9 +1880,12 @@ mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun,
        mpi_request->DevHandle = cpu_to_le16(handle);
        mpi_request->TaskType = type;
        mpi_request->TaskMID = cpu_to_le16(smid_task);
+       mpi_request->VP_ID = 0;  /* TODO */
+       mpi_request->VF_ID = 0;
        int_to_scsilun(lun, (struct scsi_lun *)mpi_request->LUN);
        mpt2sas_scsih_set_tm_flag(ioc, handle);
-       mpt2sas_base_put_smid_hi_priority(ioc, smid, VF_ID);
+       init_completion(&ioc->tm_cmds.done);
+       mpt2sas_base_put_smid_hi_priority(ioc, smid);
        timeleft = wait_for_completion_timeout(&ioc->tm_cmds.done, timeout*HZ);
        mpt2sas_scsih_clear_tm_flag(ioc, handle);
        if (!(ioc->tm_cmds.status & MPT2_CMD_COMPLETE)) {
@@ -1904,6 +1959,8 @@ _scsih_abort(struct scsi_cmnd *scmd)
                goto out;
        }
 
+       mpt2sas_halt_firmware(ioc);
+
        mutex_lock(&ioc->tm_cmds.mutex);
        handle = sas_device_priv_data->sas_target->handle;
        mpt2sas_scsih_issue_tm(ioc, handle, sas_device_priv_data->lun,
@@ -2068,7 +2125,7 @@ _scsih_target_reset(struct scsi_cmnd *scmd)
 }
 
 /**
- * _scsih_abort - eh threads main host reset routine
+ * _scsih_host_reset - eh threads main host reset routine
  * @sdev: scsi device struct
  *
  * Returns SUCCESS if command aborted else FAILED
@@ -2222,7 +2279,7 @@ _scsih_ublock_io_device(struct MPT2SAS_ADAPTER *ioc, u16 handle)
                            MPT2SAS_INFO_FMT "SDEV_RUNNING: "
                            "handle(0x%04x)\n", ioc->name, handle));
                        sas_device_priv_data->block = 0;
-                       scsi_device_set_state(sdev, SDEV_RUNNING);
+                       scsi_internal_device_unblock(sdev);
                }
        }
 }
@@ -2251,7 +2308,7 @@ _scsih_block_io_device(struct MPT2SAS_ADAPTER *ioc, u16 handle)
                            MPT2SAS_INFO_FMT "SDEV_BLOCK: "
                            "handle(0x%04x)\n", ioc->name, handle));
                        sas_device_priv_data->block = 1;
-                       scsi_device_set_state(sdev, SDEV_BLOCK);
+                       scsi_internal_device_block(sdev);
                }
        }
 }
@@ -2341,91 +2398,325 @@ _scsih_block_io_to_children_attached_directly(struct MPT2SAS_ADAPTER *ioc,
 }
 
 /**
- * _scsih_check_topo_delete_events - sanity check on topo events
+ * _scsih_tm_tr_send - send task management request
  * @ioc: per adapter object
- * @event_data: the event data payload
- *
- * This routine added to better handle cable breaker.
+ * @handle: device handle
+ * Context: interrupt time.
  *
- * This handles the case where driver recieves multiple expander
- * add and delete events in a single shot.  When there is a delete event
- * the routine will void any pending add events waiting in the event queue.
+ * This code is to initiate the device removal handshake protocal
+ * with controller firmware.  This function will issue target reset
+ * using high priority request queue.  It will send a sas iounit
+ * controll request (MPI2_SAS_OP_REMOVE_DEVICE) from this completion.
  *
- * Return nothing.
+ * This is designed to send muliple task management request at the same
+ * time to the fifo. If the fifo is full, we will append the request,
+ * and process it in a future completion.
  */
 static void
-_scsih_check_topo_delete_events(struct MPT2SAS_ADAPTER *ioc,
-    Mpi2EventDataSasTopologyChangeList_t *event_data)
+_scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle)
 {
-       struct fw_event_work *fw_event;
-       Mpi2EventDataSasTopologyChangeList_t *local_event_data;
-       u16 expander_handle;
-       struct _sas_node *sas_expander;
+       Mpi2SCSITaskManagementRequest_t *mpi_request;
+       struct MPT2SAS_TARGET *sas_target_priv_data;
+       u16 smid;
+       struct _sas_device *sas_device;
        unsigned long flags;
+       struct _tr_list *delayed_tr;
 
-       expander_handle = le16_to_cpu(event_data->ExpanderDevHandle);
-       if (expander_handle < ioc->sas_hba.num_phys) {
-               _scsih_block_io_to_children_attached_directly(ioc, event_data);
+       if (ioc->shost_recovery) {
+               printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n",
+                   __func__, ioc->name);
                return;
        }
 
-       if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING
-        || event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING) {
-               spin_lock_irqsave(&ioc->sas_node_lock, flags);
-               sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc,
-                   expander_handle);
-               spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
-               _scsih_block_io_to_children_attached_to_ex(ioc, sas_expander);
-       } else if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_RESPONDING)
-               _scsih_block_io_to_children_attached_directly(ioc, event_data);
+       spin_lock_irqsave(&ioc->sas_device_lock, flags);
+       sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
-       if (event_data->ExpStatus != MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING)
+       /* skip is hidden raid component */
+       if (sas_device && sas_device->hidden_raid_component)
                return;
 
-       /* mark ignore flag for pending events */
-       spin_lock_irqsave(&ioc->fw_event_lock, flags);
-       list_for_each_entry(fw_event, &ioc->fw_event_list, list) {
-               if (fw_event->event != MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST ||
-                   fw_event->ignore)
-                       continue;
-               local_event_data = fw_event->event_data;
-               if (local_event_data->ExpStatus ==
-                   MPI2_EVENT_SAS_TOPO_ES_ADDED ||
-                   local_event_data->ExpStatus ==
-                   MPI2_EVENT_SAS_TOPO_ES_RESPONDING) {
-                       if (le16_to_cpu(local_event_data->ExpanderDevHandle) ==
-                           expander_handle) {
-                               dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT
-                                   "setting ignoring flag\n", ioc->name));
-                               fw_event->ignore = 1;
-                       }
+       smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_tr_cb_idx);
+       if (!smid) {
+               delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC);
+               if (!delayed_tr)
+                       return;
+               INIT_LIST_HEAD(&delayed_tr->list);
+               delayed_tr->handle = handle;
+               delayed_tr->state = MPT2SAS_REQ_SAS_CNTRL;
+               list_add_tail(&delayed_tr->list,
+                   &ioc->delayed_tr_list);
+               if (sas_device && sas_device->starget) {
+                       dewtprintk(ioc, starget_printk(KERN_INFO,
+                           sas_device->starget, "DELAYED:tr:handle(0x%04x), "
+                           "(open)\n", handle));
+               } else {
+                       dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
+                           "DELAYED:tr:handle(0x%04x), (open)\n",
+                           ioc->name, handle));
                }
+               return;
        }
-       spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+
+       if (sas_device) {
+               sas_device->state |= MPTSAS_STATE_TR_SEND;
+               sas_device->state |= MPT2SAS_REQ_SAS_CNTRL;
+               if (sas_device->starget && sas_device->starget->hostdata) {
+                       sas_target_priv_data = sas_device->starget->hostdata;
+                       sas_target_priv_data->tm_busy = 1;
+                       dewtprintk(ioc, starget_printk(KERN_INFO,
+                           sas_device->starget, "tr:handle(0x%04x), (open)\n",
+                           handle));
+               }
+       } else {
+               dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
+                   "tr:handle(0x%04x), (open)\n", ioc->name, handle));
+       }
+
+       mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
+       memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t));
+       mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
+       mpi_request->DevHandle = cpu_to_le16(handle);
+       mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
+       mpt2sas_base_put_smid_hi_priority(ioc, smid);
 }
 
+
+
 /**
- * _scsih_queue_rescan - queue a topology rescan from user context
+ * _scsih_sas_control_complete - completion routine
  * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ * Context: interrupt time.
  *
- * Return nothing.
+ * This is the sas iounit controll completion routine.
+ * This code is part of the code to initiate the device removal
+ * handshake protocal with controller firmware.
+ *
+ * Return 1 meaning mf should be freed from _base_interrupt
+ *        0 means the mf is freed from this function.
  */
-static void
-_scsih_queue_rescan(struct MPT2SAS_ADAPTER *ioc)
+static u8
+_scsih_sas_control_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid,
+    u8 msix_index, u32 reply)
 {
-       struct fw_event_work *fw_event;
+       unsigned long flags;
+       u16 handle;
+       struct _sas_device *sas_device;
+       Mpi2SasIoUnitControlReply_t *mpi_reply =
+           mpt2sas_base_get_reply_virt_addr(ioc, reply);
 
-       if (ioc->wait_for_port_enable_to_complete)
-               return;
-       fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC);
-       if (!fw_event)
-               return;
-       fw_event->event = MPT2SAS_RESCAN_AFTER_HOST_RESET;
-       fw_event->ioc = ioc;
-       _scsih_fw_event_add(ioc, fw_event);
-}
+       handle = le16_to_cpu(mpi_reply->DevHandle);
 
-/**
+       spin_lock_irqsave(&ioc->sas_device_lock, flags);
+       sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+       if (sas_device) {
+               sas_device->state |= MPTSAS_STATE_CNTRL_COMPLETE;
+               if (sas_device->starget)
+                       dewtprintk(ioc, starget_printk(KERN_INFO,
+                           sas_device->starget,
+                           "sc_complete:handle(0x%04x), "
+                           "ioc_status(0x%04x), loginfo(0x%08x)\n",
+                           handle, le16_to_cpu(mpi_reply->IOCStatus),
+                           le32_to_cpu(mpi_reply->IOCLogInfo)));
+       } else {
+               dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
+                   "sc_complete:handle(0x%04x), "
+                   "ioc_status(0x%04x), loginfo(0x%08x)\n",
+                   ioc->name, handle, le16_to_cpu(mpi_reply->IOCStatus),
+                   le32_to_cpu(mpi_reply->IOCLogInfo)));
+       }
+
+       return 1;
+}
+
+/**
+ * _scsih_tm_tr_complete -
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ * Context: interrupt time.
+ *
+ * This is the target reset completion routine.
+ * This code is part of the code to initiate the device removal
+ * handshake protocal with controller firmware.
+ * It will send a sas iounit controll request (MPI2_SAS_OP_REMOVE_DEVICE)
+ *
+ * Return 1 meaning mf should be freed from _base_interrupt
+ *        0 means the mf is freed from this function.
+ */
+static u8
+_scsih_tm_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
+    u32 reply)
+{
+       unsigned long flags;
+       u16 handle;
+       struct _sas_device *sas_device;
+       Mpi2SCSITaskManagementReply_t *mpi_reply =
+           mpt2sas_base_get_reply_virt_addr(ioc, reply);
+       Mpi2SasIoUnitControlRequest_t *mpi_request;
+       u16 smid_sas_ctrl;
+       struct MPT2SAS_TARGET *sas_target_priv_data;
+       struct _tr_list *delayed_tr;
+       u8 rc;
+
+       handle = le16_to_cpu(mpi_reply->DevHandle);
+       spin_lock_irqsave(&ioc->sas_device_lock, flags);
+       sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+       if (sas_device) {
+               sas_device->state |= MPTSAS_STATE_TR_COMPLETE;
+               if (sas_device->starget) {
+                       dewtprintk(ioc, starget_printk(KERN_INFO,
+                           sas_device->starget, "tr_complete:handle(0x%04x), "
+                           "(%s) ioc_status(0x%04x), loginfo(0x%08x), "
+                           "completed(%d)\n", sas_device->handle,
+                           (sas_device->state & MPT2SAS_REQ_SAS_CNTRL) ?
+                           "open" : "active",
+                           le16_to_cpu(mpi_reply->IOCStatus),
+                           le32_to_cpu(mpi_reply->IOCLogInfo),
+                           le32_to_cpu(mpi_reply->TerminationCount)));
+                       if (sas_device->starget->hostdata) {
+                               sas_target_priv_data =
+                                   sas_device->starget->hostdata;
+                               sas_target_priv_data->tm_busy = 0;
+                       }
+               }
+       } else {
+               dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
+                   "tr_complete:handle(0x%04x), (open) ioc_status(0x%04x), "
+                   "loginfo(0x%08x), completed(%d)\n", ioc->name,
+                   handle, le16_to_cpu(mpi_reply->IOCStatus),
+                   le32_to_cpu(mpi_reply->IOCLogInfo),
+                   le32_to_cpu(mpi_reply->TerminationCount)));
+       }
+
+       if (!list_empty(&ioc->delayed_tr_list)) {
+               delayed_tr = list_entry(ioc->delayed_tr_list.next,
+                   struct _tr_list, list);
+               mpt2sas_base_free_smid(ioc, smid);
+               if (delayed_tr->state & MPT2SAS_REQ_SAS_CNTRL)
+                       _scsih_tm_tr_send(ioc, delayed_tr->handle);
+               list_del(&delayed_tr->list);
+               kfree(delayed_tr);
+               rc = 0; /* tells base_interrupt not to free mf */
+       } else
+               rc = 1;
+
+       if (sas_device && !(sas_device->state & MPT2SAS_REQ_SAS_CNTRL))
+               return rc;
+
+       if (ioc->shost_recovery) {
+               printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n",
+                   __func__, ioc->name);
+               return rc;
+       }
+
+       smid_sas_ctrl = mpt2sas_base_get_smid(ioc, ioc->tm_sas_control_cb_idx);
+       if (!smid_sas_ctrl) {
+               printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
+                   ioc->name, __func__);
+               return rc;
+       }
+
+       if (sas_device)
+               sas_device->state |= MPTSAS_STATE_CNTRL_SEND;
+
+       mpi_request = mpt2sas_base_get_msg_frame(ioc, smid_sas_ctrl);
+       memset(mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t));
+       mpi_request->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL;
+       mpi_request->Operation = MPI2_SAS_OP_REMOVE_DEVICE;
+       mpi_request->DevHandle = mpi_reply->DevHandle;
+       mpt2sas_base_put_smid_default(ioc, smid_sas_ctrl);
+       return rc;
+}
+
+/**
+ * _scsih_check_topo_delete_events - sanity check on topo events
+ * @ioc: per adapter object
+ * @event_data: the event data payload
+ *
+ * This routine added to better handle cable breaker.
+ *
+ * This handles the case where driver recieves multiple expander
+ * add and delete events in a single shot.  When there is a delete event
+ * the routine will void any pending add events waiting in the event queue.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_check_topo_delete_events(struct MPT2SAS_ADAPTER *ioc,
+    Mpi2EventDataSasTopologyChangeList_t *event_data)
+{
+       struct fw_event_work *fw_event;
+       Mpi2EventDataSasTopologyChangeList_t *local_event_data;
+       u16 expander_handle;
+       struct _sas_node *sas_expander;
+       unsigned long flags;
+       int i, reason_code;
+       u16 handle;
+
+       for (i = 0 ; i < event_data->NumEntries; i++) {
+               if (event_data->PHY[i].PhyStatus &
+                   MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT)
+                       continue;
+               handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle);
+               if (!handle)
+                       continue;
+               reason_code = event_data->PHY[i].PhyStatus &
+                   MPI2_EVENT_SAS_TOPO_RC_MASK;
+               if (reason_code == MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING)
+                       _scsih_tm_tr_send(ioc, handle);
+       }
+
+       expander_handle = le16_to_cpu(event_data->ExpanderDevHandle);
+       if (expander_handle < ioc->sas_hba.num_phys) {
+               _scsih_block_io_to_children_attached_directly(ioc, event_data);
+               return;
+       }
+
+       if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING
+        || event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING) {
+               spin_lock_irqsave(&ioc->sas_node_lock, flags);
+               sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc,
+                   expander_handle);
+               spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+               _scsih_block_io_to_children_attached_to_ex(ioc, sas_expander);
+       } else if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_RESPONDING)
+               _scsih_block_io_to_children_attached_directly(ioc, event_data);
+
+       if (event_data->ExpStatus != MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING)
+               return;
+
+       /* mark ignore flag for pending events */
+       spin_lock_irqsave(&ioc->fw_event_lock, flags);
+       list_for_each_entry(fw_event, &ioc->fw_event_list, list) {
+               if (fw_event->event != MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST ||
+                   fw_event->ignore)
+                       continue;
+               local_event_data = fw_event->event_data;
+               if (local_event_data->ExpStatus ==
+                   MPI2_EVENT_SAS_TOPO_ES_ADDED ||
+                   local_event_data->ExpStatus ==
+                   MPI2_EVENT_SAS_TOPO_ES_RESPONDING) {
+                       if (le16_to_cpu(local_event_data->ExpanderDevHandle) ==
+                           expander_handle) {
+                               dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT
+                                   "setting ignoring flag\n", ioc->name));
+                               fw_event->ignore = 1;
+                       }
+               }
+       }
+       spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+}
+
+/**
  * _scsih_flush_running_cmds - completing outstanding commands.
  * @ioc: per adapter object
  *
@@ -2441,8 +2732,8 @@ _scsih_flush_running_cmds(struct MPT2SAS_ADAPTER *ioc)
        u16 smid;
        u16 count = 0;
 
-       for (smid = 1; smid <= ioc->request_depth; smid++) {
-               scmd = _scsih_scsi_lookup_getclear(ioc, smid);
+       for (smid = 1; smid <= ioc->scsiio_depth; smid++) {
+               scmd = _scsih_scsi_lookup_get(ioc, smid);
                if (!scmd)
                        continue;
                count++;
@@ -2456,46 +2747,6 @@ _scsih_flush_running_cmds(struct MPT2SAS_ADAPTER *ioc)
 }
 
 /**
- * mpt2sas_scsih_reset_handler - reset callback handler (for scsih)
- * @ioc: per adapter object
- * @reset_phase: phase
- *
- * The handler for doing any required cleanup or initialization.
- *
- * The reset phase can be MPT2_IOC_PRE_RESET, MPT2_IOC_AFTER_RESET,
- * MPT2_IOC_DONE_RESET
- *
- * Return nothing.
- */
-void
-mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase)
-{
-       switch (reset_phase) {
-       case MPT2_IOC_PRE_RESET:
-               dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
-                   "MPT2_IOC_PRE_RESET\n", ioc->name, __func__));
-               _scsih_fw_event_off(ioc);
-               break;
-       case MPT2_IOC_AFTER_RESET:
-               dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
-                   "MPT2_IOC_AFTER_RESET\n", ioc->name, __func__));
-               if (ioc->tm_cmds.status & MPT2_CMD_PENDING) {
-                       ioc->tm_cmds.status |= MPT2_CMD_RESET;
-                       mpt2sas_base_free_smid(ioc, ioc->tm_cmds.smid);
-                       complete(&ioc->tm_cmds.done);
-               }
-               _scsih_fw_event_on(ioc);
-               _scsih_flush_running_cmds(ioc);
-               break;
-       case MPT2_IOC_DONE_RESET:
-               dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
-                   "MPT2_IOC_DONE_RESET\n", ioc->name, __func__));
-               _scsih_queue_rescan(ioc);
-               break;
-       }
-}
-
-/**
  * _scsih_setup_eedp - setup MPI request for EEDP transfer
  * @scmd: pointer to scsi command object
  * @mpi_request: pointer to the SCSI_IO reqest message frame
@@ -2523,8 +2774,6 @@ _scsih_setup_eedp(struct scsi_cmnd *scmd, Mpi2SCSIIORequest_t *mpi_request)
        else
                return;
 
-       mpi_request->EEDPBlockSize = scmd->device->sector_size;
-
        switch (prot_type) {
        case SCSI_PROT_DIF_TYPE1:
 
@@ -2532,8 +2781,7 @@ _scsih_setup_eedp(struct scsi_cmnd *scmd, Mpi2SCSIIORequest_t *mpi_request)
                * enable ref/guard checking
                * auto increment ref tag
                */
-               mpi_request->EEDPFlags = eedp_flags |
-                   MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG |
+               eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG |
                    MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG |
                    MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD;
                mpi_request->CDB.EEDP32.PrimaryReferenceTag =
@@ -2546,11 +2794,11 @@ _scsih_setup_eedp(struct scsi_cmnd *scmd, Mpi2SCSIIORequest_t *mpi_request)
                /*
                * enable guard checking
                */
-               mpi_request->EEDPFlags = eedp_flags |
-                   MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD;
-
+               eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD;
                break;
        }
+       mpi_request->EEDPBlockSize = cpu_to_le32(scmd->device->sector_size);
+       mpi_request->EEDPFlags = cpu_to_le16(eedp_flags);
 }
 
 /**
@@ -2615,7 +2863,6 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))
        Mpi2SCSIIORequest_t *mpi_request;
        u32 mpi_control;
        u16 smid;
-       unsigned long flags;
 
        scmd->scsi_done = done;
        sas_device_priv_data = scmd->device->hostdata;
@@ -2634,13 +2881,10 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))
        }
 
        /* see if we are busy with task managment stuff */
-       spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
-       if (sas_target_priv_data->tm_busy ||
-           ioc->shost_recovery || ioc->ioc_link_reset_in_progress) {
-               spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+       if (sas_device_priv_data->block || sas_target_priv_data->tm_busy)
+               return SCSI_MLQUEUE_DEVICE_BUSY;
+       else if (ioc->shost_recovery || ioc->ioc_link_reset_in_progress)
                return SCSI_MLQUEUE_HOST_BUSY;
-       }
-       spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
 
        if (scmd->sc_data_direction == DMA_FROM_DEVICE)
                mpi_control = MPI2_SCSIIO_CONTROL_READ;
@@ -2668,7 +2912,7 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))
        if ((sas_device_priv_data->flags & MPT_DEVICE_TLR_ON))
                mpi_control |= MPI2_SCSIIO_CONTROL_TLR_ON;
 
-       smid = mpt2sas_base_get_smid(ioc, ioc->scsi_io_cb_idx);
+       smid = mpt2sas_base_get_smid_scsiio(ioc, ioc->scsi_io_cb_idx, scmd);
        if (!smid) {
                printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
                    ioc->name, __func__);
@@ -2691,11 +2935,12 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))
        mpi_request->MsgFlags = MPI2_SCSIIO_MSGFLAGS_SYSTEM_SENSE_ADDR;
        mpi_request->SenseBufferLength = SCSI_SENSE_BUFFERSIZE;
        mpi_request->SenseBufferLowAddress =
-           (u32)mpt2sas_base_get_sense_buffer_dma(ioc, smid);
+           mpt2sas_base_get_sense_buffer_dma(ioc, smid);
        mpi_request->SGLOffset0 = offsetof(Mpi2SCSIIORequest_t, SGL) / 4;
        mpi_request->SGLFlags = cpu_to_le16(MPI2_SCSIIO_SGLFLAGS_TYPE_MPI +
            MPI2_SCSIIO_SGLFLAGS_SYSTEM_ADDR);
-
+       mpi_request->VF_ID = 0; /* TODO */
+       mpi_request->VP_ID = 0;
        int_to_scsilun(sas_device_priv_data->lun, (struct scsi_lun *)
            mpi_request->LUN);
        memcpy(mpi_request->CDB.CDB32, scmd->cmnd, scmd->cmd_len);
@@ -2709,8 +2954,7 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))
                }
        }
 
-       _scsih_scsi_lookup_set(ioc, smid, scmd);
-       mpt2sas_base_put_smid_scsi_io(ioc, smid, 0,
+       mpt2sas_base_put_smid_scsi_io(ioc, smid,
            sas_device_priv_data->sas_target->handle);
        return 0;
 
@@ -2743,7 +2987,7 @@ _scsih_normalize_sense(char *sense_buffer, struct sense_info *data)
 
 #ifdef CONFIG_SCSI_MPT2SAS_LOGGING
 /**
- * _scsih_scsi_ioc_info - translated non-succesfull SCSI_IO request
+ * _scsih_scsi_ioc_info - translated non-successfull SCSI_IO request
  * @ioc: per adapter object
  * @scmd: pointer to scsi command object
  * @mpi_reply: reply mf payload returned from firmware
@@ -2767,6 +3011,10 @@ _scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
        char *desc_ioc_state = NULL;
        char *desc_scsi_status = NULL;
        char *desc_scsi_state = ioc->tmp_string;
+       u32 log_info = le32_to_cpu(mpi_reply->IOCLogInfo);
+
+       if (log_info == 0x31170000)
+               return;
 
        switch (ioc_status) {
        case MPI2_IOCSTATUS_SUCCESS:
@@ -2904,7 +3152,7 @@ _scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
        if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) {
                response_info = le32_to_cpu(mpi_reply->ResponseInfo);
                response_bytes = (u8 *)&response_info;
-               _scsih_response_code(ioc, response_bytes[3]);
+               _scsih_response_code(ioc, response_bytes[0]);
        }
 }
 #endif
@@ -3002,15 +3250,16 @@ _scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle)
  * _scsih_io_done - scsi request callback
  * @ioc: per adapter object
  * @smid: system request message index
- * @VF_ID: virtual function id
+ * @msix_index: MSIX table index supplied by the OS
  * @reply: reply message frame(lower 32bit addr)
  *
- * Callback handler when using scsih_qcmd.
+ * Callback handler when using _scsih_qcmd.
  *
- * Return nothing.
+ * Return 1 meaning mf should be freed from _base_interrupt
+ *        0 means the mf is freed from this function.
  */
-static void
-_scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
+static u8
+_scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
 {
        Mpi2SCSIIORequest_t *mpi_request;
        Mpi2SCSIIOReply_t *mpi_reply;
@@ -3021,12 +3270,12 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
        u8 scsi_status;
        u32 log_info;
        struct MPT2SAS_DEVICE *sas_device_priv_data;
-       u32 response_code;
+       u32 response_code = 0;
 
        mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
-       scmd = _scsih_scsi_lookup_getclear(ioc, smid);
+       scmd = _scsih_scsi_lookup_get(ioc, smid);
        if (scmd == NULL)
-               return;
+               return 1;
 
        mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
 
@@ -3043,16 +3292,16 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
        }
 
        /* turning off TLR */
+       scsi_state = mpi_reply->SCSIState;
+       if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID)
+               response_code =
+                   le32_to_cpu(mpi_reply->ResponseInfo) & 0xFF;
        if (!sas_device_priv_data->tlr_snoop_check) {
                sas_device_priv_data->tlr_snoop_check++;
-               if (sas_device_priv_data->flags & MPT_DEVICE_TLR_ON) {
-                       response_code = (le32_to_cpu(mpi_reply->ResponseInfo)
-                           >> 24);
-                       if (response_code ==
-                           MPI2_SCSITASKMGMT_RSP_INVALID_FRAME)
-                               sas_device_priv_data->flags &=
-                                   ~MPT_DEVICE_TLR_ON;
-               }
+               if ((sas_device_priv_data->flags & MPT_DEVICE_TLR_ON) &&
+                   response_code == MPI2_SCSITASKMGMT_RSP_INVALID_FRAME)
+                       sas_device_priv_data->flags &=
+                           ~MPT_DEVICE_TLR_ON;
        }
 
        xfer_cnt = le32_to_cpu(mpi_reply->TransferCount);
@@ -3063,7 +3312,6 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
        else
                log_info = 0;
        ioc_status &= MPI2_IOCSTATUS_MASK;
-       scsi_state = mpi_reply->SCSIState;
        scsi_status = mpi_reply->SCSIStatus;
 
        if (ioc_status == MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN && xfer_cnt == 0 &&
@@ -3099,10 +3347,9 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
 
        case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED:
                if (sas_device_priv_data->block) {
-                       scmd->result = (DID_BUS_BUSY << 16);
-                       break;
+                       scmd->result = DID_TRANSPORT_DISRUPTED << 16;
+                       goto out;
                }
-
        case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED:
        case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED:
                scmd->result = DID_RESET << 16;
@@ -3148,8 +3395,10 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
        case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR:
        case MPI2_IOCSTATUS_SUCCESS:
                scmd->result = (DID_OK << 16) | scsi_status;
-               if (scsi_state & (MPI2_SCSI_STATE_AUTOSENSE_FAILED |
-                    MPI2_SCSI_STATE_NO_SCSI_STATUS))
+               if (response_code ==
+                   MPI2_SCSITASKMGMT_RSP_INVALID_FRAME ||
+                   (scsi_state & (MPI2_SCSI_STATE_AUTOSENSE_FAILED |
+                    MPI2_SCSI_STATE_NO_SCSI_STATUS)))
                        scmd->result = DID_SOFT_ERROR << 16;
                else if (scsi_state & MPI2_SCSI_STATE_TERMINATED)
                        scmd->result = DID_RESET << 16;
@@ -3182,31 +3431,12 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
  out:
        scsi_dma_unmap(scmd);
        scmd->scsi_done(scmd);
-}
-
-/**
- * _scsih_link_change - process phy link changes
- * @ioc: per adapter object
- * @handle: phy handle
- * @attached_handle: valid for devices attached to link
- * @phy_number: phy number
- * @link_rate: new link rate
- * Context: user.
- *
- * Return nothing.
- */
-static void
-_scsih_link_change(struct MPT2SAS_ADAPTER *ioc, u16 handle, u16 attached_handle,
-   u8 phy_number, u8 link_rate)
-{
-       mpt2sas_transport_update_phy_link_change(ioc, handle, attached_handle,
-           phy_number, link_rate);
+       return 1;
 }
 
 /**
  * _scsih_sas_host_refresh - refreshing sas host object contents
  * @ioc: per adapter object
- * @update: update link information
  * Context: user
  *
  * During port enable, fw will send topology events for every device. Its
@@ -3216,13 +3446,14 @@ _scsih_link_change(struct MPT2SAS_ADAPTER *ioc, u16 handle, u16 attached_handle,
  * Return nothing.
  */
 static void
-_scsih_sas_host_refresh(struct MPT2SAS_ADAPTER *ioc, u8 update)
+_scsih_sas_host_refresh(struct MPT2SAS_ADAPTER *ioc)
 {
        u16 sz;
        u16 ioc_status;
        int i;
        Mpi2ConfigReply_t mpi_reply;
        Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL;
+       u16 attached_handle;
 
        dtmprintk(ioc, printk(MPT2SAS_INFO_FMT
            "updating handles for sas_host(0x%016llx)\n",
@@ -3236,26 +3467,24 @@ _scsih_sas_host_refresh(struct MPT2SAS_ADAPTER *ioc, u8 update)
                    ioc->name, __FILE__, __LINE__, __func__);
                return;
        }
-       if (!(mpt2sas_config_get_sas_iounit_pg0(ioc, &mpi_reply,
-           sas_iounit_pg0, sz))) {
-               ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
-                   MPI2_IOCSTATUS_MASK;
-               if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
-                       goto out;
-               for (i = 0; i < ioc->sas_hba.num_phys ; i++) {
-                       ioc->sas_hba.phy[i].handle =
-                           le16_to_cpu(sas_iounit_pg0->PhyData[i].
-                               ControllerDevHandle);
-                       if (update)
-                               _scsih_link_change(ioc,
-                                   ioc->sas_hba.phy[i].handle,
-                                   le16_to_cpu(sas_iounit_pg0->PhyData[i].
-                                   AttachedDevHandle), i,
-                                   sas_iounit_pg0->PhyData[i].
-                                   NegotiatedLinkRate >> 4);
-               }
-       }
 
+       if ((mpt2sas_config_get_sas_iounit_pg0(ioc, &mpi_reply,
+           sas_iounit_pg0, sz)) != 0)
+               goto out;
+       ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK;
+       if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
+               goto out;
+       for (i = 0; i < ioc->sas_hba.num_phys ; i++) {
+               if (i == 0)
+                       ioc->sas_hba.handle = le16_to_cpu(sas_iounit_pg0->
+                           PhyData[0].ControllerDevHandle);
+               ioc->sas_hba.phy[i].handle = ioc->sas_hba.handle;
+               attached_handle = le16_to_cpu(sas_iounit_pg0->PhyData[i].
+                   AttachedDevHandle);
+               mpt2sas_transport_update_links(ioc, ioc->sas_hba.sas_address,
+                   attached_handle, i, sas_iounit_pg0->PhyData[i].
+                   NegotiatedLinkRate >> 4);
+       }
  out:
        kfree(sas_iounit_pg0);
 }
@@ -3368,19 +3597,21 @@ _scsih_sas_host_add(struct MPT2SAS_ADAPTER *ioc)
                            ioc->name, __FILE__, __LINE__, __func__);
                        goto out;
                }
-               ioc->sas_hba.phy[i].handle =
-                   le16_to_cpu(sas_iounit_pg0->PhyData[i].ControllerDevHandle);
+
+               if (i == 0)
+                       ioc->sas_hba.handle = le16_to_cpu(sas_iounit_pg0->
+                           PhyData[0].ControllerDevHandle);
+               ioc->sas_hba.phy[i].handle = ioc->sas_hba.handle;
                ioc->sas_hba.phy[i].phy_id = i;
                mpt2sas_transport_add_host_phy(ioc, &ioc->sas_hba.phy[i],
                    phy_pg0, ioc->sas_hba.parent_dev);
        }
        if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
-           MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, ioc->sas_hba.phy[0].handle))) {
+           MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, ioc->sas_hba.handle))) {
                printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
                    ioc->name, __FILE__, __LINE__, __func__);
                goto out;
        }
-       ioc->sas_hba.handle = le16_to_cpu(sas_device_pg0.DevHandle);
        ioc->sas_hba.enclosure_handle =
            le16_to_cpu(sas_device_pg0.EnclosureHandle);
        ioc->sas_hba.sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
@@ -3423,15 +3654,18 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
        Mpi2SasEnclosurePage0_t enclosure_pg0;
        u32 ioc_status;
        u16 parent_handle;
-       __le64 sas_address;
+       __le64 sas_address, sas_address_parent = 0;
        int i;
        unsigned long flags;
-       struct _sas_port *mpt2sas_port;
+       struct _sas_port *mpt2sas_port = NULL;
        int rc = 0;
 
        if (!handle)
                return -1;
 
+       if (ioc->shost_recovery)
+               return -1;
+
        if ((mpt2sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0,
            MPI2_SAS_EXPAND_PGAD_FORM_HNDL, handle))) {
                printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
@@ -3449,10 +3683,16 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
 
        /* handle out of order topology events */
        parent_handle = le16_to_cpu(expander_pg0.ParentDevHandle);
-       if (parent_handle >= ioc->sas_hba.num_phys) {
+       if (_scsih_get_sas_address(ioc, parent_handle, &sas_address_parent)
+           != 0) {
+               printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+                   ioc->name, __FILE__, __LINE__, __func__);
+               return -1;
+       }
+       if (sas_address_parent != ioc->sas_hba.sas_address) {
                spin_lock_irqsave(&ioc->sas_node_lock, flags);
-               sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc,
-                   parent_handle);
+               sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc,
+                   sas_address_parent);
                spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
                if (!sas_expander) {
                        rc = _scsih_expander_add(ioc, parent_handle);
@@ -3461,9 +3701,8 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
                }
        }
 
-       sas_address = le64_to_cpu(expander_pg0.SASAddress);
-
        spin_lock_irqsave(&ioc->sas_node_lock, flags);
+       sas_address = le64_to_cpu(expander_pg0.SASAddress);
        sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc,
            sas_address);
        spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
@@ -3481,14 +3720,12 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
 
        sas_expander->handle = handle;
        sas_expander->num_phys = expander_pg0.NumPhys;
-       sas_expander->parent_handle = parent_handle;
-       sas_expander->enclosure_handle =
-           le16_to_cpu(expander_pg0.EnclosureHandle);
+       sas_expander->sas_address_parent = sas_address_parent;
        sas_expander->sas_address = sas_address;
 
        printk(MPT2SAS_INFO_FMT "expander_add: handle(0x%04x),"
            " parent(0x%04x), sas_addr(0x%016llx), phys(%d)\n", ioc->name,
-           handle, sas_expander->parent_handle, (unsigned long long)
+           handle, parent_handle, (unsigned long long)
            sas_expander->sas_address, sas_expander->num_phys);
 
        if (!sas_expander->num_phys)
@@ -3504,7 +3741,7 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
 
        INIT_LIST_HEAD(&sas_expander->sas_port_list);
        mpt2sas_port = mpt2sas_transport_port_add(ioc, handle,
-           sas_expander->parent_handle);
+           sas_address_parent);
        if (!mpt2sas_port) {
                printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
                    ioc->name, __FILE__, __LINE__, __func__);
@@ -3518,12 +3755,20 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
                    &expander_pg1, i, handle))) {
                        printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
                            ioc->name, __FILE__, __LINE__, __func__);
-                       continue;
+                       rc = -1;
+                       goto out_fail;
                }
                sas_expander->phy[i].handle = handle;
                sas_expander->phy[i].phy_id = i;
-               mpt2sas_transport_add_expander_phy(ioc, &sas_expander->phy[i],
-                   expander_pg1, sas_expander->parent_dev);
+
+               if ((mpt2sas_transport_add_expander_phy(ioc,
+                   &sas_expander->phy[i], expander_pg1,
+                   sas_expander->parent_dev))) {
+                       printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+                           ioc->name, __FILE__, __LINE__, __func__);
+                       rc = -1;
+                       goto out_fail;
+               }
        }
 
        if (sas_expander->enclosure_handle) {
@@ -3540,27 +3785,66 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
 
  out_fail:
 
-       if (sas_expander)
-               kfree(sas_expander->phy);
+       if (mpt2sas_port)
+               mpt2sas_transport_port_remove(ioc, sas_expander->sas_address,
+                   sas_address_parent);
        kfree(sas_expander);
        return rc;
 }
 
 /**
+ * _scsih_done -  scsih callback handler.
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ *
+ * Callback handler when sending internal generated message frames.
+ * The callback index passed is `ioc->scsih_cb_idx`
+ *
+ * Return 1 meaning mf should be freed from _base_interrupt
+ *        0 means the mf is freed from this function.
+ */
+static u8
+_scsih_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
+{
+       MPI2DefaultReply_t *mpi_reply;
+
+       mpi_reply =  mpt2sas_base_get_reply_virt_addr(ioc, reply);
+       if (ioc->scsih_cmds.status == MPT2_CMD_NOT_USED)
+               return 1;
+       if (ioc->scsih_cmds.smid != smid)
+               return 1;
+       ioc->scsih_cmds.status |= MPT2_CMD_COMPLETE;
+       if (mpi_reply) {
+               memcpy(ioc->scsih_cmds.reply, mpi_reply,
+                   mpi_reply->MsgLength*4);
+               ioc->scsih_cmds.status |= MPT2_CMD_REPLY_VALID;
+       }
+       ioc->scsih_cmds.status &= ~MPT2_CMD_PENDING;
+       complete(&ioc->scsih_cmds.done);
+       return 1;
+}
+
+/**
  * _scsih_expander_remove - removing expander object
  * @ioc: per adapter object
- * @handle: expander handle
+ * @sas_address: expander sas_address
  *
  * Return nothing.
  */
 static void
-_scsih_expander_remove(struct MPT2SAS_ADAPTER *ioc, u16 handle)
+_scsih_expander_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address)
 {
        struct _sas_node *sas_expander;
        unsigned long flags;
 
+       if (ioc->shost_recovery)
+               return;
+
        spin_lock_irqsave(&ioc->sas_node_lock, flags);
-       sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc, handle);
+       sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc,
+           sas_address);
        spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
        _scsih_expander_node_remove(ioc, sas_expander);
 }
@@ -3652,8 +3936,11 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd)
        }
 
        sas_device->handle = handle;
-       sas_device->parent_handle =
-           le16_to_cpu(sas_device_pg0.ParentDevHandle);
+       if (_scsih_get_sas_address(ioc, le16_to_cpu
+               (sas_device_pg0.ParentDevHandle),
+               &sas_device->sas_address_parent) != 0)
+               printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+                   ioc->name, __FILE__, __LINE__, __func__);
        sas_device->enclosure_handle =
            le16_to_cpu(sas_device_pg0.EnclosureHandle);
        sas_device->slot =
@@ -3683,40 +3970,42 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd)
 /**
  * _scsih_remove_device -  removing sas device object
  * @ioc: per adapter object
- * @handle: sas device handle
+ * @sas_device: the sas_device object
  *
  * Return nothing.
  */
 static void
-_scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, u16 handle)
+_scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, struct _sas_device
+    *sas_device)
 {
        struct MPT2SAS_TARGET *sas_target_priv_data;
-       struct _sas_device *sas_device;
-       unsigned long flags;
        Mpi2SasIoUnitControlReply_t mpi_reply;
        Mpi2SasIoUnitControlRequest_t mpi_request;
-       u16 device_handle;
+       u16 device_handle, handle;
 
-       /* lookup sas_device */
-       spin_lock_irqsave(&ioc->sas_device_lock, flags);
-       sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
-       if (!sas_device) {
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+       if (!sas_device)
                return;
-       }
 
-       dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter: handle"
-           "(0x%04x)\n", ioc->name, __func__, handle));
+       handle = sas_device->handle;
+       dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter: handle(0x%04x),"
+           " sas_addr(0x%016llx)\n", ioc->name, __func__, handle,
+           (unsigned long long) sas_device->sas_address));
 
        if (sas_device->starget && sas_device->starget->hostdata) {
                sas_target_priv_data = sas_device->starget->hostdata;
                sas_target_priv_data->deleted = 1;
        }
-       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
-       if (ioc->remove_host)
+       if (ioc->remove_host || ioc->shost_recovery || !handle)
                goto out;
 
+       if ((sas_device->state & MPTSAS_STATE_TR_COMPLETE)) {
+               dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "\tskip "
+                  "target_reset handle(0x%04x)\n", ioc->name,
+                  handle));
+               goto skip_tr;
+       }
+
        /* Target Reset to flush out all the outstanding IO */
        device_handle = (sas_device->hidden_raid_component) ?
            sas_device->volume_handle : handle;
@@ -3730,6 +4019,15 @@ _scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, u16 handle)
                mutex_unlock(&ioc->tm_cmds.mutex);
                dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "issue target reset "
                    "done: handle(0x%04x)\n", ioc->name, device_handle));
+               if (ioc->shost_recovery)
+                       goto out;
+       }
+ skip_tr:
+
+       if ((sas_device->state & MPTSAS_STATE_CNTRL_COMPLETE)) {
+               dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "\tskip "
+                  "sas_cntrl handle(0x%04x)\n", ioc->name, handle));
+               goto out;
        }
 
        /* SAS_IO_UNIT_CNTR - send REMOVE_DEVICE */
@@ -3739,7 +4037,8 @@ _scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, u16 handle)
        mpi_request.Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL;
        mpi_request.Operation = MPI2_SAS_OP_REMOVE_DEVICE;
        mpi_request.DevHandle = handle;
-       mpi_request.VF_ID = 0;
+       mpi_request.VF_ID = 0; /* TODO */
+       mpi_request.VP_ID = 0;
        if ((mpt2sas_base_sas_iounit_control(ioc, &mpi_reply,
            &mpi_request)) != 0) {
                printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
@@ -3752,11 +4051,14 @@ _scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, u16 handle)
            le32_to_cpu(mpi_reply.IOCLogInfo)));
 
  out:
+
+       _scsih_ublock_io_device(ioc, handle);
+
        mpt2sas_transport_port_remove(ioc, sas_device->sas_address,
-           sas_device->parent_handle);
+           sas_device->sas_address_parent);
 
        printk(MPT2SAS_INFO_FMT "removing handle(0x%04x), sas_addr"
-           "(0x%016llx)\n", ioc->name, sas_device->handle,
+           "(0x%016llx)\n", ioc->name, handle,
            (unsigned long long) sas_device->sas_address);
        _scsih_sas_device_remove(ioc, sas_device);
 
@@ -3780,7 +4082,7 @@ _scsih_sas_topology_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
        u16 reason_code;
        u8 phy_number;
        char *status_str = NULL;
-       char link_rate[25];
+       u8 link_rate, prev_link_rate;
 
        switch (event_data->ExpStatus) {
        case MPI2_EVENT_SAS_TOPO_ES_ADDED:
@@ -3790,6 +4092,7 @@ _scsih_sas_topology_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
                status_str = "remove";
                break;
        case MPI2_EVENT_SAS_TOPO_ES_RESPONDING:
+       case 0:
                status_str =  "responding";
                break;
        case MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING:
@@ -3815,30 +4118,30 @@ _scsih_sas_topology_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
                    MPI2_EVENT_SAS_TOPO_RC_MASK;
                switch (reason_code) {
                case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED:
-                       snprintf(link_rate, 25, ": add, link(0x%02x)",
-                           (event_data->PHY[i].LinkRate >> 4));
-                       status_str = link_rate;
+                       status_str = "target add";
                        break;
                case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING:
-                       status_str = ": remove";
+                       status_str = "target remove";
                        break;
                case MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING:
-                       status_str = ": remove_delay";
+                       status_str = "delay target remove";
                        break;
                case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED:
-                       snprintf(link_rate, 25, ": link(0x%02x)",
-                           (event_data->PHY[i].LinkRate >> 4));
-                       status_str = link_rate;
+                       status_str = "link rate change";
                        break;
                case MPI2_EVENT_SAS_TOPO_RC_NO_CHANGE:
-                       status_str = ": responding";
+                       status_str = "target responding";
                        break;
                default:
-                       status_str = "unknown";
+                       status_str = "unknown";
                        break;
                }
-               printk(KERN_DEBUG "\tphy(%02d), attached_handle(0x%04x)%s\n",
-                   phy_number, handle, status_str);
+               link_rate = event_data->PHY[i].LinkRate >> 4;
+               prev_link_rate = event_data->PHY[i].LinkRate & 0xF;
+               printk(KERN_DEBUG "\tphy(%02d), attached_handle(0x%04x): %s:"
+                   " link rate: new(0x%02x), old(0x%02x)\n", phy_number,
+                   handle, status_str, link_rate, prev_link_rate);
+
        }
 }
 #endif
@@ -3846,15 +4149,12 @@ _scsih_sas_topology_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
 /**
  * _scsih_sas_topology_change_event - handle topology changes
  * @ioc: per adapter object
- * @VF_ID:
- * @event_data: event data payload
- * fw_event:
+ * @fw_event: The fw_event_work object
  * Context: user.
  *
  */
 static void
-_scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
-    Mpi2EventDataSasTopologyChangeList_t *event_data,
+_scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc,
     struct fw_event_work *fw_event)
 {
        int i;
@@ -3862,18 +4162,24 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
        u16 reason_code;
        u8 phy_number;
        struct _sas_node *sas_expander;
+       struct _sas_device *sas_device;
+       u64 sas_address;
        unsigned long flags;
-       u8 link_rate_;
+       u8 link_rate, prev_link_rate;
+       Mpi2EventDataSasTopologyChangeList_t *event_data = fw_event->event_data;
 
 #ifdef CONFIG_SCSI_MPT2SAS_LOGGING
        if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
                _scsih_sas_topology_change_event_debug(ioc, event_data);
 #endif
 
+       if (ioc->shost_recovery)
+               return;
+
        if (!ioc->sas_hba.num_phys)
                _scsih_sas_host_add(ioc);
        else
-               _scsih_sas_host_refresh(ioc, 0);
+               _scsih_sas_host_refresh(ioc);
 
        if (fw_event->ignore) {
                dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "ignoring expander "
@@ -3888,6 +4194,17 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
                if (_scsih_expander_add(ioc, parent_handle) != 0)
                        return;
 
+       spin_lock_irqsave(&ioc->sas_node_lock, flags);
+       sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc,
+           parent_handle);
+       spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+       if (sas_expander)
+               sas_address = sas_expander->sas_address;
+       else if (parent_handle < ioc->sas_hba.num_phys)
+               sas_address = ioc->sas_hba.sas_address;
+       else
+               return;
+
        /* handle siblings events */
        for (i = 0; i < event_data->NumEntries; i++) {
                if (fw_event->ignore) {
@@ -3895,59 +4212,59 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
                            "expander event\n", ioc->name));
                        return;
                }
-               if (event_data->PHY[i].PhyStatus &
-                   MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT)
+               if (ioc->shost_recovery)
+                       return;
+               phy_number = event_data->StartPhyNum + i;
+               reason_code = event_data->PHY[i].PhyStatus &
+                   MPI2_EVENT_SAS_TOPO_RC_MASK;
+               if ((event_data->PHY[i].PhyStatus &
+                   MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT) && (reason_code !=
+                   MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING))
                        continue;
                handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle);
                if (!handle)
                        continue;
-               phy_number = event_data->StartPhyNum + i;
-               reason_code = event_data->PHY[i].PhyStatus &
-                   MPI2_EVENT_SAS_TOPO_RC_MASK;
-               link_rate_ = event_data->PHY[i].LinkRate >> 4;
+               link_rate = event_data->PHY[i].LinkRate >> 4;
+               prev_link_rate = event_data->PHY[i].LinkRate & 0xF;
                switch (reason_code) {
                case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED:
+
+                       if (link_rate == prev_link_rate)
+                               break;
+
+                       mpt2sas_transport_update_links(ioc, sas_address,
+                           handle, phy_number, link_rate);
+
+                       if (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5)
+                               _scsih_ublock_io_device(ioc, handle);
+                       break;
                case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED:
-                       if (!parent_handle) {
-                               if (phy_number < ioc->sas_hba.num_phys)
-                                       _scsih_link_change(ioc,
-                                          ioc->sas_hba.phy[phy_number].handle,
-                                          handle, phy_number, link_rate_);
-                       } else {
-                               spin_lock_irqsave(&ioc->sas_node_lock, flags);
-                               sas_expander =
-                                   mpt2sas_scsih_expander_find_by_handle(ioc,
-                                       parent_handle);
-                               spin_unlock_irqrestore(&ioc->sas_node_lock,
-                                   flags);
-                               if (sas_expander) {
-                                       if (phy_number < sas_expander->num_phys)
-                                               _scsih_link_change(ioc,
-                                                  sas_expander->
-                                                  phy[phy_number].handle,
-                                                  handle, phy_number,
-                                                  link_rate_);
-                               }
-                       }
-                       if (reason_code == MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED) {
-                               if (link_rate_ >= MPI2_SAS_NEG_LINK_RATE_1_5)
-                                       _scsih_ublock_io_device(ioc, handle);
-                       }
-                       if (reason_code == MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED) {
-                               if (link_rate_ < MPI2_SAS_NEG_LINK_RATE_1_5)
-                                       break;
-                               _scsih_add_device(ioc, handle, phy_number, 0);
-                       }
+
+                       mpt2sas_transport_update_links(ioc, sas_address,
+                           handle, phy_number, link_rate);
+
+                       _scsih_add_device(ioc, handle, phy_number, 0);
                        break;
                case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING:
-                       _scsih_remove_device(ioc, handle);
+
+                       spin_lock_irqsave(&ioc->sas_device_lock, flags);
+                       sas_device = _scsih_sas_device_find_by_handle(ioc,
+                           handle);
+                       if (!sas_device) {
+                               spin_unlock_irqrestore(&ioc->sas_device_lock,
+                                   flags);
+                               break;
+                       }
+                       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+                       _scsih_remove_device(ioc, sas_device);
                        break;
                }
        }
 
        /* handle expander removal */
-       if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING)
-               _scsih_expander_remove(ioc, parent_handle);
+       if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING &&
+           sas_expander)
+               _scsih_expander_remove(ioc, sas_address);
 
 }
 
@@ -3999,6 +4316,12 @@ _scsih_sas_device_status_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
        case MPI2_EVENT_SAS_DEV_STAT_RC_ASYNC_NOTIFICATION:
                reason_str = "internal async notification";
                break;
+       case MPI2_EVENT_SAS_DEV_STAT_RC_EXPANDER_REDUCED_FUNCTIONALITY:
+               reason_str = "expander reduced functionality";
+               break;
+       case MPI2_EVENT_SAS_DEV_STAT_RC_CMP_EXPANDER_REDUCED_FUNCTIONALITY:
+               reason_str = "expander reduced functionality complete";
+               break;
        default:
                reason_str = "unknown reason";
                break;
@@ -4017,20 +4340,52 @@ _scsih_sas_device_status_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
 /**
  * _scsih_sas_device_status_change_event - handle device status change
  * @ioc: per adapter object
- * @VF_ID:
- * @event_data: event data payload
+ * @fw_event: The fw_event_work object
  * Context: user.
  *
  * Return nothing.
  */
 static void
-_scsih_sas_device_status_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
-    Mpi2EventDataSasDeviceStatusChange_t *event_data)
+_scsih_sas_device_status_change_event(struct MPT2SAS_ADAPTER *ioc,
+    struct fw_event_work *fw_event)
 {
+       struct MPT2SAS_TARGET *target_priv_data;
+       struct _sas_device *sas_device;
+       __le64 sas_address;
+       unsigned long flags;
+       Mpi2EventDataSasDeviceStatusChange_t *event_data =
+           fw_event->event_data;
+
 #ifdef CONFIG_SCSI_MPT2SAS_LOGGING
        if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
-               _scsih_sas_device_status_change_event_debug(ioc, event_data);
+               _scsih_sas_device_status_change_event_debug(ioc,
+                    event_data);
 #endif
+
+       if (!(event_data->ReasonCode ==
+           MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET &&
+          event_data->ReasonCode ==
+           MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET))
+               return;
+
+       spin_lock_irqsave(&ioc->sas_device_lock, flags);
+       sas_address = le64_to_cpu(event_data->SASAddress);
+       sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
+           sas_address);
+       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+       if (!sas_device || !sas_device->starget)
+               return;
+
+       target_priv_data = sas_device->starget->hostdata;
+       if (!target_priv_data)
+               return;
+
+       if (event_data->ReasonCode ==
+           MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET)
+               target_priv_data->tm_busy = 1;
+       else
+               target_priv_data->tm_busy = 0;
 }
 
 #ifdef CONFIG_SCSI_MPT2SAS_LOGGING
@@ -4072,34 +4427,33 @@ _scsih_sas_enclosure_dev_status_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
 /**
  * _scsih_sas_enclosure_dev_status_change_event - handle enclosure events
  * @ioc: per adapter object
- * @VF_ID:
- * @event_data: event data payload
+ * @fw_event: The fw_event_work object
  * Context: user.
  *
  * Return nothing.
  */
 static void
 _scsih_sas_enclosure_dev_status_change_event(struct MPT2SAS_ADAPTER *ioc,
-    u8 VF_ID, Mpi2EventDataSasEnclDevStatusChange_t *event_data)
+    struct fw_event_work *fw_event)
 {
 #ifdef CONFIG_SCSI_MPT2SAS_LOGGING
        if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
                _scsih_sas_enclosure_dev_status_change_event_debug(ioc,
-                    event_data);
+                    fw_event->event_data);
 #endif
 }
 
 /**
  * _scsih_sas_broadcast_primative_event - handle broadcast events
  * @ioc: per adapter object
- * @event_data: event data payload
+ * @fw_event: The fw_event_work object
  * Context: user.
  *
  * Return nothing.
  */
 static void
-_scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
-    Mpi2EventDataSasBroadcastPrimitive_t *event_data)
+_scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc,
+    struct fw_event_work *fw_event)
 {
        struct scsi_cmnd *scmd;
        u16 smid, handle;
@@ -4108,11 +4462,13 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
        u32 termination_count;
        u32 query_count;
        Mpi2SCSITaskManagementReply_t *mpi_reply;
-
+#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
+       Mpi2EventDataSasBroadcastPrimitive_t *event_data = fw_event->event_data;
+#endif
+       u16 ioc_status;
        dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "broadcast primative: "
            "phy number(%d), width(%d)\n", ioc->name, event_data->PhyNum,
            event_data->PortWidth));
-
        dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name,
            __func__));
 
@@ -4120,7 +4476,7 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
        termination_count = 0;
        query_count = 0;
        mpi_reply = ioc->tm_cmds.reply;
-       for (smid = 1; smid <= ioc->request_depth; smid++) {
+       for (smid = 1; smid <= ioc->scsiio_depth; smid++) {
                scmd = _scsih_scsi_lookup_get(ioc, smid);
                if (!scmd)
                        continue;
@@ -4143,8 +4499,9 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
                mpt2sas_scsih_issue_tm(ioc, handle, lun,
                    MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, smid, 30);
                ioc->tm_cmds.status = MPT2_CMD_NOT_USED;
-
-               if ((mpi_reply->IOCStatus == MPI2_IOCSTATUS_SUCCESS) &&
+               ioc_status = le16_to_cpu(mpi_reply->IOCStatus)
+                   & MPI2_IOCSTATUS_MASK;
+               if ((ioc_status == MPI2_IOCSTATUS_SUCCESS) &&
                    (mpi_reply->ResponseCode ==
                     MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED ||
                     mpi_reply->ResponseCode ==
@@ -4167,23 +4524,25 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
 /**
  * _scsih_sas_discovery_event - handle discovery events
  * @ioc: per adapter object
- * @event_data: event data payload
+ * @fw_event: The fw_event_work object
  * Context: user.
  *
  * Return nothing.
  */
 static void
-_scsih_sas_discovery_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
-    Mpi2EventDataSasDiscovery_t *event_data)
+_scsih_sas_discovery_event(struct MPT2SAS_ADAPTER *ioc,
+    struct fw_event_work *fw_event)
 {
+       Mpi2EventDataSasDiscovery_t *event_data = fw_event->event_data;
+
 #ifdef CONFIG_SCSI_MPT2SAS_LOGGING
        if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) {
                printk(MPT2SAS_DEBUG_FMT "discovery event: (%s)", ioc->name,
                    (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED) ?
                    "start" : "stop");
        if (event_data->DiscoveryStatus)
-               printk(MPT2SAS_DEBUG_FMT ", discovery_status(0x%08x)",
-                   ioc->name, le32_to_cpu(event_data->DiscoveryStatus));
+               printk("discovery_status(0x%08x)",
+                   le32_to_cpu(event_data->DiscoveryStatus));
        printk("\n");
        }
 #endif
@@ -4249,12 +4608,6 @@ _scsih_sas_volume_add(struct MPT2SAS_ADAPTER *ioc,
        u16 handle = le16_to_cpu(element->VolDevHandle);
        int rc;
 
-#if 0 /* RAID_HACKS */
-       if (le32_to_cpu(event_data->Flags) &
-           MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG)
-               return;
-#endif
-
        mpt2sas_config_get_volume_wwid(ioc, handle, &wwid);
        if (!wwid) {
                printk(MPT2SAS_ERR_FMT
@@ -4309,12 +4662,6 @@ _scsih_sas_volume_delete(struct MPT2SAS_ADAPTER *ioc,
        unsigned long flags;
        struct MPT2SAS_TARGET *sas_target_priv_data;
 
-#if 0 /* RAID_HACKS */
-       if (le32_to_cpu(event_data->Flags) &
-           MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG)
-               return;
-#endif
-
        spin_lock_irqsave(&ioc->raid_device_lock, flags);
        raid_device = _scsih_raid_device_find_by_handle(ioc, handle);
        spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
@@ -4409,7 +4756,7 @@ _scsih_sas_pd_delete(struct MPT2SAS_ADAPTER *ioc,
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
        if (!sas_device)
                return;
-       _scsih_remove_device(ioc, handle);
+       _scsih_remove_device(ioc, sas_device);
 }
 
 /**
@@ -4427,14 +4774,41 @@ _scsih_sas_pd_add(struct MPT2SAS_ADAPTER *ioc,
        struct _sas_device *sas_device;
        unsigned long flags;
        u16 handle = le16_to_cpu(element->PhysDiskDevHandle);
+       Mpi2ConfigReply_t mpi_reply;
+       Mpi2SasDevicePage0_t sas_device_pg0;
+       u32 ioc_status;
+       u64 sas_address;
+       u16 parent_handle;
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
        sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-       if (sas_device)
+       if (sas_device) {
                sas_device->hidden_raid_component = 1;
-       else
-               _scsih_add_device(ioc, handle, 0, 1);
+               return;
+       }
+
+       if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
+           MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
+               printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+                   ioc->name, __FILE__, __LINE__, __func__);
+               return;
+       }
+
+       ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+           MPI2_IOCSTATUS_MASK;
+       if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+               printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+                   ioc->name, __FILE__, __LINE__, __func__);
+               return;
+       }
+
+       parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle);
+       if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address))
+               mpt2sas_transport_update_links(ioc, sas_address, handle,
+                   sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5);
+
+       _scsih_add_device(ioc, handle, 0, 1);
 }
 
 #ifdef CONFIG_SCSI_MPT2SAS_LOGGING
@@ -4522,24 +4896,27 @@ _scsih_sas_ir_config_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
 /**
  * _scsih_sas_ir_config_change_event - handle ir configuration change events
  * @ioc: per adapter object
- * @VF_ID:
- * @event_data: event data payload
+ * @fw_event: The fw_event_work object
  * Context: user.
  *
  * Return nothing.
  */
 static void
-_scsih_sas_ir_config_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
-    Mpi2EventDataIrConfigChangeList_t *event_data)
+_scsih_sas_ir_config_change_event(struct MPT2SAS_ADAPTER *ioc,
+    struct fw_event_work *fw_event)
 {
        Mpi2EventIrConfigElement_t *element;
        int i;
+       u8 foreign_config;
+       Mpi2EventDataIrConfigChangeList_t *event_data = fw_event->event_data;
 
 #ifdef CONFIG_SCSI_MPT2SAS_LOGGING
        if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
                _scsih_sas_ir_config_change_event_debug(ioc, event_data);
 
 #endif
+       foreign_config = (le32_to_cpu(event_data->Flags) &
+           MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) ? 1 : 0;
 
        element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0];
        for (i = 0; i < event_data->NumElements; i++, element++) {
@@ -4547,11 +4924,13 @@ _scsih_sas_ir_config_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
                switch (element->ReasonCode) {
                case MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED:
                case MPI2_EVENT_IR_CHANGE_RC_ADDED:
-                       _scsih_sas_volume_add(ioc, element);
+                       if (!foreign_config)
+                               _scsih_sas_volume_add(ioc, element);
                        break;
                case MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED:
                case MPI2_EVENT_IR_CHANGE_RC_REMOVED:
-                       _scsih_sas_volume_delete(ioc, element);
+                       if (!foreign_config)
+                               _scsih_sas_volume_delete(ioc, element);
                        break;
                case MPI2_EVENT_IR_CHANGE_RC_PD_CREATED:
                        _scsih_sas_pd_hide(ioc, element);
@@ -4572,14 +4951,14 @@ _scsih_sas_ir_config_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
 /**
  * _scsih_sas_ir_volume_event - IR volume event
  * @ioc: per adapter object
- * @event_data: event data payload
+ * @fw_event: The fw_event_work object
  * Context: user.
  *
  * Return nothing.
  */
 static void
-_scsih_sas_ir_volume_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
-    Mpi2EventDataIrVolume_t *event_data)
+_scsih_sas_ir_volume_event(struct MPT2SAS_ADAPTER *ioc,
+    struct fw_event_work *fw_event)
 {
        u64 wwid;
        unsigned long flags;
@@ -4588,6 +4967,7 @@ _scsih_sas_ir_volume_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
        u32 state;
        int rc;
        struct MPT2SAS_TARGET *sas_target_priv_data;
+       Mpi2EventDataIrVolume_t *event_data = fw_event->event_data;
 
        if (event_data->ReasonCode != MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED)
                return;
@@ -4657,19 +5037,24 @@ _scsih_sas_ir_volume_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
 /**
  * _scsih_sas_ir_physical_disk_event - PD event
  * @ioc: per adapter object
- * @event_data: event data payload
+ * @fw_event: The fw_event_work object
  * Context: user.
  *
  * Return nothing.
  */
 static void
-_scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
-   Mpi2EventDataIrPhysicalDisk_t *event_data)
+_scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc,
+    struct fw_event_work *fw_event)
 {
-       u16 handle;
+       u16 handle, parent_handle;
        u32 state;
        struct _sas_device *sas_device;
        unsigned long flags;
+       Mpi2ConfigReply_t mpi_reply;
+       Mpi2SasDevicePage0_t sas_device_pg0;
+       u32 ioc_status;
+       Mpi2EventDataIrPhysicalDisk_t *event_data = fw_event->event_data;
+       u64 sas_address;
 
        if (event_data->ReasonCode != MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED)
                return;
@@ -4686,22 +5071,41 @@ _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
        switch (state) {
-#if 0
-       case MPI2_RAID_PD_STATE_OFFLINE:
-               if (sas_device)
-                       _scsih_remove_device(ioc, handle);
-               break;
-#endif
        case MPI2_RAID_PD_STATE_ONLINE:
        case MPI2_RAID_PD_STATE_DEGRADED:
        case MPI2_RAID_PD_STATE_REBUILDING:
        case MPI2_RAID_PD_STATE_OPTIMAL:
-               if (sas_device)
+               if (sas_device) {
                        sas_device->hidden_raid_component = 1;
-               else
-                       _scsih_add_device(ioc, handle, 0, 1);
+                       return;
+               }
+
+               if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply,
+                   &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE,
+                   handle))) {
+                       printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+                           ioc->name, __FILE__, __LINE__, __func__);
+                       return;
+               }
+
+               ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+                   MPI2_IOCSTATUS_MASK;
+               if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+                       printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+                           ioc->name, __FILE__, __LINE__, __func__);
+                       return;
+               }
+
+               parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle);
+               if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address))
+                       mpt2sas_transport_update_links(ioc, sas_address, handle,
+                           sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5);
+
+               _scsih_add_device(ioc, handle, 0, 1);
+
                break;
 
+       case MPI2_RAID_PD_STATE_OFFLINE:
        case MPI2_RAID_PD_STATE_NOT_CONFIGURED:
        case MPI2_RAID_PD_STATE_NOT_COMPATIBLE:
        case MPI2_RAID_PD_STATE_HOT_SPARE:
@@ -4735,11 +5139,17 @@ _scsih_sas_ir_operation_status_event_debug(struct MPT2SAS_ADAPTER *ioc,
        case MPI2_EVENT_IR_RAIDOP_CONSISTENCY_CHECK:
                reason_str = "consistency check";
                break;
-       default:
-               reason_str = "unknown reason";
+       case MPI2_EVENT_IR_RAIDOP_BACKGROUND_INIT:
+               reason_str = "background init";
+               break;
+       case MPI2_EVENT_IR_RAIDOP_MAKE_DATA_CONSISTENT:
+               reason_str = "make data consistent";
                break;
        }
 
+       if (!reason_str)
+               return;
+
        printk(MPT2SAS_INFO_FMT "raid operational status: (%s)"
            "\thandle(0x%04x), percent complete(%d)\n",
            ioc->name, reason_str,
@@ -4751,33 +5161,33 @@ _scsih_sas_ir_operation_status_event_debug(struct MPT2SAS_ADAPTER *ioc,
 /**
  * _scsih_sas_ir_operation_status_event - handle RAID operation events
  * @ioc: per adapter object
- * @VF_ID:
- * @event_data: event data payload
+ * @fw_event: The fw_event_work object
  * Context: user.
  *
  * Return nothing.
  */
 static void
-_scsih_sas_ir_operation_status_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
-    Mpi2EventDataIrOperationStatus_t *event_data)
+_scsih_sas_ir_operation_status_event(struct MPT2SAS_ADAPTER *ioc,
+    struct fw_event_work *fw_event)
 {
 #ifdef CONFIG_SCSI_MPT2SAS_LOGGING
        if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
-               _scsih_sas_ir_operation_status_event_debug(ioc, event_data);
+               _scsih_sas_ir_operation_status_event_debug(ioc,
+                    fw_event->event_data);
 #endif
 }
 
 /**
  * _scsih_task_set_full - handle task set full
  * @ioc: per adapter object
- * @event_data: event data payload
+ * @fw_event: The fw_event_work object
  * Context: user.
  *
  * Throttle back qdepth.
  */
 static void
-_scsih_task_set_full(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
-    Mpi2EventDataTaskSetFull_t *event_data)
+_scsih_task_set_full(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work
+       *fw_event)
 {
        unsigned long flags;
        struct _sas_device *sas_device;
@@ -4788,6 +5198,7 @@ _scsih_task_set_full(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
        u16 handle;
        int id, channel;
        u64 sas_address;
+       Mpi2EventDataTaskSetFull_t *event_data = fw_event->event_data;
 
        current_depth = le16_to_cpu(event_data->CurrentDepth);
        handle = le16_to_cpu(event_data->DevHandle);
@@ -4876,6 +5287,10 @@ _scsih_mark_responding_sas_device(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
                if (sas_device->sas_address == sas_address &&
                    sas_device->slot == slot && sas_device->starget) {
                        sas_device->responding = 1;
+                       sas_device->state = 0;
+                       starget = sas_device->starget;
+                       sas_target_priv_data = starget->hostdata;
+                       sas_target_priv_data->tm_busy = 0;
                        starget_printk(KERN_INFO, sas_device->starget,
                            "handle(0x%04x), sas_addr(0x%016llx), enclosure "
                            "logical id(0x%016llx), slot(%d)\n", handle,
@@ -4888,8 +5303,6 @@ _scsih_mark_responding_sas_device(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
                        printk(KERN_INFO "\thandle changed from(0x%04x)!!!\n",
                            sas_device->handle);
                        sas_device->handle = handle;
-                       starget = sas_device->starget;
-                       sas_target_priv_data = starget->hostdata;
                        sas_target_priv_data->handle = handle;
                        goto out;
                }
@@ -5036,18 +5449,23 @@ _scsih_mark_responding_expander(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
 {
        struct _sas_node *sas_expander;
        unsigned long flags;
+       int i;
 
        spin_lock_irqsave(&ioc->sas_node_lock, flags);
        list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) {
-               if (sas_expander->sas_address == sas_address) {
-                       sas_expander->responding = 1;
-                       if (sas_expander->handle != handle) {
-                               printk(KERN_INFO "old handle(0x%04x)\n",
-                                   sas_expander->handle);
-                               sas_expander->handle = handle;
-                       }
+               if (sas_expander->sas_address != sas_address)
+                       continue;
+               sas_expander->responding = 1;
+               if (sas_expander->handle == handle)
                        goto out;
-               }
+               printk(KERN_INFO "\texpander(0x%016llx): handle changed"
+                   " from(0x%04x) to (0x%04x)!!!\n",
+                   (unsigned long long)sas_expander->sas_address,
+                   sas_expander->handle, handle);
+               sas_expander->handle = handle;
+               for (i = 0 ; i < sas_expander->num_phys ; i++)
+                       sas_expander->phy[i].handle = handle;
+               goto out;
        }
  out:
        spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
@@ -5105,22 +5523,9 @@ static void
 _scsih_remove_unresponding_devices(struct MPT2SAS_ADAPTER *ioc)
 {
        struct _sas_device *sas_device, *sas_device_next;
-       struct _sas_node *sas_expander, *sas_expander_next;
+       struct _sas_node *sas_expander;
        struct _raid_device *raid_device, *raid_device_next;
-       unsigned long flags;
 
-       _scsih_search_responding_sas_devices(ioc);
-       _scsih_search_responding_raid_devices(ioc);
-       _scsih_search_responding_expanders(ioc);
-
-       spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
-       ioc->shost_recovery = 0;
-       if (ioc->shost->shost_state == SHOST_RECOVERY) {
-               printk(MPT2SAS_INFO_FMT "putting controller into "
-                   "SHOST_RUNNING\n", ioc->name);
-               scsi_host_set_state(ioc->shost, SHOST_RUNNING);
-       }
-       spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
 
        list_for_each_entry_safe(sas_device, sas_device_next,
            &ioc->sas_device_list, list) {
@@ -5137,7 +5542,9 @@ _scsih_remove_unresponding_devices(struct MPT2SAS_ADAPTER *ioc)
                            (unsigned long long)
                            sas_device->enclosure_logical_id,
                            sas_device->slot);
-               _scsih_remove_device(ioc, sas_device->handle);
+               /* invalidate the device handle */
+               sas_device->handle = 0;
+               _scsih_remove_device(ioc, sas_device);
        }
 
        list_for_each_entry_safe(raid_device, raid_device_next,
@@ -5156,16 +5563,63 @@ _scsih_remove_unresponding_devices(struct MPT2SAS_ADAPTER *ioc)
                _scsih_raid_device_remove(ioc, raid_device);
        }
 
-       list_for_each_entry_safe(sas_expander, sas_expander_next,
-           &ioc->sas_expander_list, list) {
+ retry_expander_search:
+       sas_expander = NULL;
+       list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) {
                if (sas_expander->responding) {
                        sas_expander->responding = 0;
                        continue;
                }
-               printk("\tremoving expander: handle(0x%04x), "
-                   " sas_addr(0x%016llx)\n", sas_expander->handle,
-                   (unsigned long long)sas_expander->sas_address);
-               _scsih_expander_remove(ioc, sas_expander->handle);
+               _scsih_expander_remove(ioc, sas_expander->sas_address);
+               goto retry_expander_search;
+       }
+}
+
+/**
+ * mpt2sas_scsih_reset_handler - reset callback handler (for scsih)
+ * @ioc: per adapter object
+ * @reset_phase: phase
+ *
+ * The handler for doing any required cleanup or initialization.
+ *
+ * The reset phase can be MPT2_IOC_PRE_RESET, MPT2_IOC_AFTER_RESET,
+ * MPT2_IOC_DONE_RESET
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase)
+{
+       switch (reset_phase) {
+       case MPT2_IOC_PRE_RESET:
+               dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
+                   "MPT2_IOC_PRE_RESET\n", ioc->name, __func__));
+               _scsih_fw_event_off(ioc);
+               break;
+       case MPT2_IOC_AFTER_RESET:
+               dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
+                   "MPT2_IOC_AFTER_RESET\n", ioc->name, __func__));
+               if (ioc->tm_cmds.status & MPT2_CMD_PENDING) {
+                       ioc->tm_cmds.status |= MPT2_CMD_RESET;
+                       mpt2sas_base_free_smid(ioc, ioc->tm_cmds.smid);
+                       complete(&ioc->tm_cmds.done);
+               }
+               _scsih_fw_event_on(ioc);
+               _scsih_flush_running_cmds(ioc);
+               break;
+       case MPT2_IOC_DONE_RESET:
+               dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
+                   "MPT2_IOC_DONE_RESET\n", ioc->name, __func__));
+               _scsih_sas_host_refresh(ioc);
+               _scsih_search_responding_sas_devices(ioc);
+               _scsih_search_responding_raid_devices(ioc);
+               _scsih_search_responding_expanders(ioc);
+               break;
+       case MPT2_IOC_RUNNING:
+               dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
+                   "MPT2_IOC_RUNNING\n", ioc->name, __func__));
+               _scsih_remove_unresponding_devices(ioc);
+               break;
        }
 }
 
@@ -5185,14 +5639,6 @@ _firmware_event_work(struct work_struct *work)
        unsigned long flags;
        struct MPT2SAS_ADAPTER *ioc = fw_event->ioc;
 
-       /* This is invoked by calling _scsih_queue_rescan(). */
-       if (fw_event->event == MPT2SAS_RESCAN_AFTER_HOST_RESET) {
-               _scsih_fw_event_free(ioc, fw_event);
-               _scsih_sas_host_refresh(ioc, 1);
-               _scsih_remove_unresponding_devices(ioc);
-               return;
-       }
-
        /* the queue is being flushed so ignore this event */
        spin_lock_irqsave(&ioc->fw_event_lock, flags);
        if (ioc->fw_events_off || ioc->remove_host) {
@@ -5202,54 +5648,45 @@ _firmware_event_work(struct work_struct *work)
        }
        spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
 
-       spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
        if (ioc->shost_recovery) {
-               spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
                _scsih_fw_event_requeue(ioc, fw_event, 1000);
                return;
        }
-       spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
 
        switch (fw_event->event) {
        case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
-               _scsih_sas_topology_change_event(ioc, fw_event->VF_ID,
-                   fw_event->event_data, fw_event);
+               _scsih_sas_topology_change_event(ioc, fw_event);
                break;
        case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE:
-               _scsih_sas_device_status_change_event(ioc, fw_event->VF_ID,
-                   fw_event->event_data);
+               _scsih_sas_device_status_change_event(ioc,
+                   fw_event);
                break;
        case MPI2_EVENT_SAS_DISCOVERY:
-               _scsih_sas_discovery_event(ioc, fw_event->VF_ID,
-                   fw_event->event_data);
+               _scsih_sas_discovery_event(ioc,
+                   fw_event);
                break;
        case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE:
-               _scsih_sas_broadcast_primative_event(ioc, fw_event->VF_ID,
-                   fw_event->event_data);
+               _scsih_sas_broadcast_primative_event(ioc,
+                   fw_event);
                break;
        case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE:
                _scsih_sas_enclosure_dev_status_change_event(ioc,
-                   fw_event->VF_ID, fw_event->event_data);
+                   fw_event);
                break;
        case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST:
-               _scsih_sas_ir_config_change_event(ioc, fw_event->VF_ID,
-                   fw_event->event_data);
+               _scsih_sas_ir_config_change_event(ioc, fw_event);
                break;
        case MPI2_EVENT_IR_VOLUME:
-               _scsih_sas_ir_volume_event(ioc, fw_event->VF_ID,
-                   fw_event->event_data);
+               _scsih_sas_ir_volume_event(ioc, fw_event);
                break;
        case MPI2_EVENT_IR_PHYSICAL_DISK:
-               _scsih_sas_ir_physical_disk_event(ioc, fw_event->VF_ID,
-                   fw_event->event_data);
+               _scsih_sas_ir_physical_disk_event(ioc, fw_event);
                break;
        case MPI2_EVENT_IR_OPERATION_STATUS:
-               _scsih_sas_ir_operation_status_event(ioc, fw_event->VF_ID,
-                   fw_event->event_data);
+               _scsih_sas_ir_operation_status_event(ioc, fw_event);
                break;
        case MPI2_EVENT_TASK_SET_FULL:
-               _scsih_task_set_full(ioc, fw_event->VF_ID,
-                   fw_event->event_data);
+               _scsih_task_set_full(ioc, fw_event);
                break;
        }
        _scsih_fw_event_free(ioc, fw_event);
@@ -5258,17 +5695,19 @@ _firmware_event_work(struct work_struct *work)
 /**
  * mpt2sas_scsih_event_callback - firmware event handler (called at ISR time)
  * @ioc: per adapter object
- * @VF_ID: virtual function id
+ * @msix_index: MSIX table index supplied by the OS
  * @reply: reply message frame(lower 32bit addr)
  * Context: interrupt.
  *
  * This function merely adds a new work task into ioc->firmware_event_thread.
  * The tasks are worked from _firmware_event_work in user context.
  *
- * Return nothing.
+ * Return 1 meaning mf should be freed from _base_interrupt
+ *        0 means the mf is freed from this function.
  */
-void
-mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply)
+u8
+mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index,
+       u32 reply)
 {
        struct fw_event_work *fw_event;
        Mpi2EventNotificationReply_t *mpi_reply;
@@ -5279,11 +5718,11 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply)
        spin_lock_irqsave(&ioc->fw_event_lock, flags);
        if (ioc->fw_events_off || ioc->remove_host) {
                spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
-               return;
+               return 1;
        }
        spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
 
-       mpi_reply =  mpt2sas_base_get_reply_virt_addr(ioc, reply);
+       mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
        event = le16_to_cpu(mpi_reply->Event);
 
        switch (event) {
@@ -5297,7 +5736,7 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply)
                if (baen_data->Primitive !=
                    MPI2_EVENT_PRIMITIVE_ASYNCHRONOUS_EVENT ||
                    ioc->broadcast_aen_busy)
-                       return;
+                       return 1;
                ioc->broadcast_aen_busy = 1;
                break;
        }
@@ -5319,14 +5758,14 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply)
                break;
 
        default: /* ignore the rest */
-               return;
+               return 1;
        }
 
        fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC);
        if (!fw_event) {
                printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
                    ioc->name, __FILE__, __LINE__, __func__);
-               return;
+               return 1;
        }
        fw_event->event_data =
            kzalloc(mpi_reply->EventDataLength*4, GFP_ATOMIC);
@@ -5334,15 +5773,17 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply)
                printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
                    ioc->name, __FILE__, __LINE__, __func__);
                kfree(fw_event);
-               return;
+               return 1;
        }
 
        memcpy(fw_event->event_data, mpi_reply->EventData,
            mpi_reply->EventDataLength*4);
        fw_event->ioc = ioc;
-       fw_event->VF_ID = VF_ID;
+       fw_event->VF_ID = mpi_reply->VF_ID;
+       fw_event->VP_ID = mpi_reply->VP_ID;
        fw_event->event = event;
        _scsih_fw_event_add(ioc, fw_event);
+       return 1;
 }
 
 /* shost template */
@@ -5409,7 +5850,9 @@ _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc,
                        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
                        if (!sas_device)
                                continue;
-                       _scsih_remove_device(ioc, sas_device->handle);
+                       _scsih_remove_device(ioc, sas_device);
+                       if (ioc->shost_recovery)
+                               return;
                        goto retry_device_search;
                }
        }
@@ -5430,13 +5873,16 @@ _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc,
                        spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
                        if (!expander_sibling)
                                continue;
-                       _scsih_expander_remove(ioc, expander_sibling->handle);
+                       _scsih_expander_remove(ioc,
+                           expander_sibling->sas_address);
+                       if (ioc->shost_recovery)
+                               return;
                        goto retry_expander_search;
                }
        }
 
        mpt2sas_transport_port_remove(ioc, sas_expander->sas_address,
-           sas_expander->parent_handle);
+           sas_expander->sas_address_parent);
 
        printk(MPT2SAS_INFO_FMT "expander_remove: handle"
           "(0x%04x), sas_addr(0x%016llx)\n", ioc->name,
@@ -5449,9 +5895,99 @@ _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc,
 }
 
 /**
+ * _scsih_ir_shutdown - IR shutdown notification
+ * @ioc: per adapter object
+ *
+ * Sending RAID Action to alert the Integrated RAID subsystem of the IOC that
+ * the host system is shutting down.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_ir_shutdown(struct MPT2SAS_ADAPTER *ioc)
+{
+       Mpi2RaidActionRequest_t *mpi_request;
+       Mpi2RaidActionReply_t *mpi_reply;
+       u16 smid;
+
+       /* is IR firmware build loaded ? */
+       if (!ioc->ir_firmware)
+               return;
+
+       /* are there any volumes ? */
+       if (list_empty(&ioc->raid_device_list))
+               return;
+
+       mutex_lock(&ioc->scsih_cmds.mutex);
+
+       if (ioc->scsih_cmds.status != MPT2_CMD_NOT_USED) {
+               printk(MPT2SAS_ERR_FMT "%s: scsih_cmd in use\n",
+                   ioc->name, __func__);
+               goto out;
+       }
+       ioc->scsih_cmds.status = MPT2_CMD_PENDING;
+
+       smid = mpt2sas_base_get_smid(ioc, ioc->scsih_cb_idx);
+       if (!smid) {
+               printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
+                   ioc->name, __func__);
+               ioc->scsih_cmds.status = MPT2_CMD_NOT_USED;
+               goto out;
+       }
+
+       mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
+       ioc->scsih_cmds.smid = smid;
+       memset(mpi_request, 0, sizeof(Mpi2RaidActionRequest_t));
+
+       mpi_request->Function = MPI2_FUNCTION_RAID_ACTION;
+       mpi_request->Action = MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED;
+
+       printk(MPT2SAS_INFO_FMT "IR shutdown (sending)\n", ioc->name);
+       init_completion(&ioc->scsih_cmds.done);
+       mpt2sas_base_put_smid_default(ioc, smid);
+       wait_for_completion_timeout(&ioc->scsih_cmds.done, 10*HZ);
+
+       if (!(ioc->scsih_cmds.status & MPT2_CMD_COMPLETE)) {
+               printk(MPT2SAS_ERR_FMT "%s: timeout\n",
+                   ioc->name, __func__);
+               goto out;
+       }
+
+       if (ioc->scsih_cmds.status & MPT2_CMD_REPLY_VALID) {
+               mpi_reply = ioc->scsih_cmds.reply;
+
+               printk(MPT2SAS_INFO_FMT "IR shutdown (complete): "
+                   "ioc_status(0x%04x), loginfo(0x%08x)\n",
+                   ioc->name, le16_to_cpu(mpi_reply->IOCStatus),
+                   le32_to_cpu(mpi_reply->IOCLogInfo));
+       }
+
+ out:
+       ioc->scsih_cmds.status = MPT2_CMD_NOT_USED;
+       mutex_unlock(&ioc->scsih_cmds.mutex);
+}
+
+/**
+ * _scsih_shutdown - routine call during system shutdown
+ * @pdev: PCI device struct
+ *
+ * Return nothing.
+ */
+static void
+_scsih_shutdown(struct pci_dev *pdev)
+{
+       struct Scsi_Host *shost = pci_get_drvdata(pdev);
+       struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+
+       _scsih_ir_shutdown(ioc);
+       mpt2sas_base_detach(ioc);
+}
+
+/**
  * _scsih_remove - detach and remove add host
  * @pdev: PCI device struct
  *
+ * Routine called when unloading the driver.
  * Return nothing.
  */
 static void __devexit
@@ -5485,7 +6021,7 @@ _scsih_remove(struct pci_dev *pdev)
                            mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
                           mpt2sas_port->remote_identify.sas_address);
                        if (sas_device) {
-                               _scsih_remove_device(ioc, sas_device->handle);
+                               _scsih_remove_device(ioc, sas_device);
                                goto retry_again;
                        }
                } else {
@@ -5494,7 +6030,7 @@ _scsih_remove(struct pci_dev *pdev)
                            mpt2sas_port->remote_identify.sas_address);
                        if (expander_sibling) {
                                _scsih_expander_remove(ioc,
-                                   expander_sibling->handle);
+                                   expander_sibling->sas_address);
                                goto retry_again;
                        }
                }
@@ -5508,7 +6044,7 @@ _scsih_remove(struct pci_dev *pdev)
        }
 
        sas_remove_host(shost);
-       mpt2sas_base_detach(ioc);
+       _scsih_shutdown(pdev);
        list_del(&ioc->list);
        scsi_remove_host(shost);
        scsi_host_put(shost);
@@ -5529,7 +6065,8 @@ _scsih_probe_boot_devices(struct MPT2SAS_ADAPTER *ioc)
        void *device;
        struct _sas_device *sas_device;
        struct _raid_device *raid_device;
-       u16 handle, parent_handle;
+       u16 handle;
+       u64 sas_address_parent;
        u64 sas_address;
        unsigned long flags;
        int rc;
@@ -5558,17 +6095,17 @@ _scsih_probe_boot_devices(struct MPT2SAS_ADAPTER *ioc)
        } else {
                sas_device = device;
                handle = sas_device->handle;
-               parent_handle = sas_device->parent_handle;
+               sas_address_parent = sas_device->sas_address_parent;
                sas_address = sas_device->sas_address;
                spin_lock_irqsave(&ioc->sas_device_lock, flags);
                list_move_tail(&sas_device->list, &ioc->sas_device_list);
                spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
                if (!mpt2sas_transport_port_add(ioc, sas_device->handle,
-                   sas_device->parent_handle)) {
+                   sas_device->sas_address_parent)) {
                        _scsih_sas_device_remove(ioc, sas_device);
                } else if (!sas_device->starget) {
                        mpt2sas_transport_port_remove(ioc, sas_address,
-                           parent_handle);
+                           sas_address_parent);
                        _scsih_sas_device_remove(ioc, sas_device);
                }
        }
@@ -5598,7 +6135,7 @@ _scsih_probe_raid(struct MPT2SAS_ADAPTER *ioc)
 }
 
 /**
- * _scsih_probe_sas - reporting raid volumes to sas transport
+ * _scsih_probe_sas - reporting sas devices to sas transport
  * @ioc: per adapter object
  *
  * Called during initial loading of the driver.
@@ -5608,8 +6145,6 @@ _scsih_probe_sas(struct MPT2SAS_ADAPTER *ioc)
 {
        struct _sas_device *sas_device, *next;
        unsigned long flags;
-       u16 handle, parent_handle;
-       u64 sas_address;
 
        /* SAS Device List */
        list_for_each_entry_safe(sas_device, next, &ioc->sas_device_init_list,
@@ -5618,14 +6153,13 @@ _scsih_probe_sas(struct MPT2SAS_ADAPTER *ioc)
                list_move_tail(&sas_device->list, &ioc->sas_device_list);
                spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
-               handle = sas_device->handle;
-               parent_handle = sas_device->parent_handle;
-               sas_address = sas_device->sas_address;
-               if (!mpt2sas_transport_port_add(ioc, handle, parent_handle)) {
+               if (!mpt2sas_transport_port_add(ioc, sas_device->handle,
+                   sas_device->sas_address_parent)) {
                        _scsih_sas_device_remove(ioc, sas_device);
                } else if (!sas_device->starget) {
-                       mpt2sas_transport_port_remove(ioc, sas_address,
-                           parent_handle);
+                       mpt2sas_transport_port_remove(ioc,
+                           sas_device->sas_address,
+                           sas_device->sas_address_parent);
                        _scsih_sas_device_remove(ioc, sas_device);
                }
        }
@@ -5694,7 +6228,10 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        ioc->ctl_cb_idx = ctl_cb_idx;
        ioc->base_cb_idx = base_cb_idx;
        ioc->transport_cb_idx = transport_cb_idx;
+       ioc->scsih_cb_idx = scsih_cb_idx;
        ioc->config_cb_idx = config_cb_idx;
+       ioc->tm_tr_cb_idx = tm_tr_cb_idx;
+       ioc->tm_sas_control_cb_idx = tm_sas_control_cb_idx;
        ioc->logging_level = logging_level;
        /* misc semaphores and spin locks */
        spin_lock_init(&ioc->ioc_reset_in_progress_lock);
@@ -5710,6 +6247,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        INIT_LIST_HEAD(&ioc->fw_event_list);
        INIT_LIST_HEAD(&ioc->raid_device_list);
        INIT_LIST_HEAD(&ioc->sas_hba.sas_port_list);
+       INIT_LIST_HEAD(&ioc->delayed_tr_list);
 
        /* init shost parameters */
        shost->max_cmd_len = 16;
@@ -5726,6 +6264,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
        scsi_host_set_prot(shost, SHOST_DIF_TYPE1_PROTECTION
            | SHOST_DIF_TYPE3_PROTECTION);
+       scsi_host_set_guard(shost, SHOST_DIX_GUARD_CRC);
 
        /* event thread */
        snprintf(ioc->firmware_event_name, sizeof(ioc->firmware_event_name),
@@ -5773,6 +6312,7 @@ _scsih_suspend(struct pci_dev *pdev, pm_message_t state)
        struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
        u32 device_state;
 
+       mpt2sas_base_stop_watchdog(ioc);
        flush_scheduled_work();
        scsi_block_requests(shost);
        device_state = pci_choose_state(pdev, state);
@@ -5815,6 +6355,7 @@ _scsih_resume(struct pci_dev *pdev)
 
        mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, SOFT_RESET);
        scsi_unblock_requests(shost);
+       mpt2sas_base_start_watchdog(ioc);
        return 0;
 }
 #endif /* CONFIG_PM */
@@ -5825,6 +6366,7 @@ static struct pci_driver scsih_driver = {
        .id_table       = scsih_pci_table,
        .probe          = _scsih_probe,
        .remove         = __devexit_p(_scsih_remove),
+       .shutdown       = _scsih_shutdown,
 #ifdef CONFIG_PM
        .suspend        = _scsih_suspend,
        .resume         = _scsih_resume,
@@ -5866,6 +6408,9 @@ _scsih_init(void)
        transport_cb_idx = mpt2sas_base_register_callback_handler(
            mpt2sas_transport_done);
 
+       /* scsih internal commands callback handler */
+       scsih_cb_idx = mpt2sas_base_register_callback_handler(_scsih_done);
+
        /* configuration page API internal commands callback handler */
        config_cb_idx = mpt2sas_base_register_callback_handler(
            mpt2sas_config_done);
@@ -5873,6 +6418,11 @@ _scsih_init(void)
        /* ctl module callback handler */
        ctl_cb_idx = mpt2sas_base_register_callback_handler(mpt2sas_ctl_done);
 
+       tm_tr_cb_idx = mpt2sas_base_register_callback_handler(
+           _scsih_tm_tr_complete);
+       tm_sas_control_cb_idx = mpt2sas_base_register_callback_handler(
+           _scsih_sas_control_complete);
+
        mpt2sas_ctl_init();
 
        error = pci_register_driver(&scsih_driver);
@@ -5900,9 +6450,13 @@ _scsih_exit(void)
        mpt2sas_base_release_callback_handler(tm_cb_idx);
        mpt2sas_base_release_callback_handler(base_cb_idx);
        mpt2sas_base_release_callback_handler(transport_cb_idx);
+       mpt2sas_base_release_callback_handler(scsih_cb_idx);
        mpt2sas_base_release_callback_handler(config_cb_idx);
        mpt2sas_base_release_callback_handler(ctl_cb_idx);
 
+       mpt2sas_base_release_callback_handler(tm_tr_cb_idx);
+       mpt2sas_base_release_callback_handler(tm_sas_control_cb_idx);
+
        mpt2sas_ctl_exit();
 }