]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/scsi/mpt3sas/mpt3sas_scsih.c
mpt3sas: Fix unprotected list lookup in v4.3-rc0 changes
[karo-tx-linux.git] / drivers / scsi / mpt3sas / mpt3sas_scsih.c
index 5a97e3286719d8150a6dbf24bcd062f2effa4373..9e6843275fdf8a78c7dc007dc4814526a736441e 100644 (file)
@@ -175,6 +175,7 @@ struct sense_info {
  * @VP_ID: virtual port id
  * @ignore: flag meaning this event has been marked to ignore
  * @event: firmware event MPI2_EVENT_XXX defined in mpt2_ioc.h
+ * @refcount: reference count for fw_event_work
  * @event_data: reply event data payload follows
  *
  * This object stored on ioc->fw_event_list.
@@ -191,9 +192,37 @@ struct fw_event_work {
        u8                      VP_ID;
        u8                      ignore;
        u16                     event;
+       struct kref             refcount;
        char                    event_data[0] __aligned(4);
 };
 
+static void fw_event_work_free(struct kref *r)
+{
+       kfree(container_of(r, struct fw_event_work, refcount));
+}
+
+static void fw_event_work_get(struct fw_event_work *fw_work)
+{
+       kref_get(&fw_work->refcount);
+}
+
+static void fw_event_work_put(struct fw_event_work *fw_work)
+{
+       kref_put(&fw_work->refcount, fw_event_work_free);
+}
+
+static struct fw_event_work *alloc_fw_event_work(int len)
+{
+       struct fw_event_work *fw_event;
+
+       fw_event = kzalloc(sizeof(*fw_event) + len, GFP_ATOMIC);
+       if (!fw_event)
+               return NULL;
+
+       kref_init(&fw_event->refcount);
+       return fw_event;
+}
+
 /* raid transport support */
 static struct raid_template *mpt3sas_raid_template;
 
@@ -518,8 +547,61 @@ _scsih_determine_boot_device(struct MPT3SAS_ADAPTER *ioc,
        }
 }
 
+static struct _sas_device *
+__mpt3sas_get_sdev_from_target(struct MPT3SAS_ADAPTER *ioc,
+       struct MPT3SAS_TARGET *tgt_priv)
+{
+       struct _sas_device *ret;
+
+       assert_spin_locked(&ioc->sas_device_lock);
+
+       ret = tgt_priv->sdev;
+       if (ret)
+               sas_device_get(ret);
+
+       return ret;
+}
+
+static struct _sas_device *
+mpt3sas_get_sdev_from_target(struct MPT3SAS_ADAPTER *ioc,
+       struct MPT3SAS_TARGET *tgt_priv)
+{
+       struct _sas_device *ret;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ioc->sas_device_lock, flags);
+       ret = __mpt3sas_get_sdev_from_target(ioc, tgt_priv);
+       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+       return ret;
+}
+
+struct _sas_device *
+__mpt3sas_get_sdev_by_addr(struct MPT3SAS_ADAPTER *ioc,
+       u64 sas_address)
+{
+       struct _sas_device *sas_device;
+
+       assert_spin_locked(&ioc->sas_device_lock);
+
+       list_for_each_entry(sas_device, &ioc->sas_device_list, list)
+               if (sas_device->sas_address == sas_address)
+                       goto found_device;
+
+       list_for_each_entry(sas_device, &ioc->sas_device_init_list, list)
+               if (sas_device->sas_address == sas_address)
+                       goto found_device;
+
+       return NULL;
+
+found_device:
+       sas_device_get(sas_device);
+       return sas_device;
+}
+
+
 /**
- * mpt3sas_scsih_sas_device_find_by_sas_address - sas device search
+ * mpt3sas_get_sdev_by_addr - sas device search
  * @ioc: per adapter object
  * @sas_address: sas address
  * Context: Calling function should acquire ioc->sas_device_lock
@@ -528,24 +610,44 @@ _scsih_determine_boot_device(struct MPT3SAS_ADAPTER *ioc,
  * object.
  */
 struct _sas_device *
-mpt3sas_scsih_sas_device_find_by_sas_address(struct MPT3SAS_ADAPTER *ioc,
+mpt3sas_get_sdev_by_addr(struct MPT3SAS_ADAPTER *ioc,
        u64 sas_address)
 {
        struct _sas_device *sas_device;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ioc->sas_device_lock, flags);
+       sas_device = __mpt3sas_get_sdev_by_addr(ioc,
+                       sas_address);
+       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+       return sas_device;
+}
+
+static struct _sas_device *
+__mpt3sas_get_sdev_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+       struct _sas_device *sas_device;
+
+       assert_spin_locked(&ioc->sas_device_lock);
 
        list_for_each_entry(sas_device, &ioc->sas_device_list, list)
-               if (sas_device->sas_address == sas_address)
-                       return sas_device;
+               if (sas_device->handle == handle)
+                       goto found_device;
 
        list_for_each_entry(sas_device, &ioc->sas_device_init_list, list)
-               if (sas_device->sas_address == sas_address)
-                       return sas_device;
+               if (sas_device->handle == handle)
+                       goto found_device;
 
        return NULL;
+
+found_device:
+       sas_device_get(sas_device);
+       return sas_device;
 }
 
 /**
- * _scsih_sas_device_find_by_handle - sas device search
+ * mpt3sas_get_sdev_by_handle - sas device search
  * @ioc: per adapter object
  * @handle: sas device handle (assigned by firmware)
  * Context: Calling function should acquire ioc->sas_device_lock
@@ -554,19 +656,16 @@ mpt3sas_scsih_sas_device_find_by_sas_address(struct MPT3SAS_ADAPTER *ioc,
  * object.
  */
 static struct _sas_device *
-_scsih_sas_device_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+mpt3sas_get_sdev_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
 {
        struct _sas_device *sas_device;
+       unsigned long flags;
 
-       list_for_each_entry(sas_device, &ioc->sas_device_list, list)
-               if (sas_device->handle == handle)
-                       return sas_device;
-
-       list_for_each_entry(sas_device, &ioc->sas_device_init_list, list)
-               if (sas_device->handle == handle)
-                       return sas_device;
+       spin_lock_irqsave(&ioc->sas_device_lock, flags);
+       sas_device = __mpt3sas_get_sdev_by_handle(ioc, handle);
+       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
-       return NULL;
+       return sas_device;
 }
 
 /**
@@ -575,7 +674,7 @@ _scsih_sas_device_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
  * @sas_device: the sas_device object
  * Context: This function will acquire ioc->sas_device_lock.
  *
- * Removing object and freeing associated memory from the ioc->sas_device_list.
+ * If sas_device is on the list, remove it and decrement its reference count.
  */
 static void
 _scsih_sas_device_remove(struct MPT3SAS_ADAPTER *ioc,
@@ -585,10 +684,31 @@ _scsih_sas_device_remove(struct MPT3SAS_ADAPTER *ioc,
 
        if (!sas_device)
                return;
+       pr_info(MPT3SAS_FMT
+           "removing handle(0x%04x), sas_addr(0x%016llx)\n",
+           ioc->name, sas_device->handle,
+           (unsigned long long) sas_device->sas_address);
+
+       if (sas_device->enclosure_handle != 0)
+               pr_info(MPT3SAS_FMT
+                  "removing enclosure logical id(0x%016llx), slot(%d)\n",
+                  ioc->name, (unsigned long long)
+                  sas_device->enclosure_logical_id, sas_device->slot);
 
+       if (sas_device->connector_name[0] != '\0')
+               pr_info(MPT3SAS_FMT
+                  "removing enclosure level(0x%04x), connector name( %s)\n",
+                  ioc->name, sas_device->enclosure_level,
+                  sas_device->connector_name);
+       /*
+        * The lock serializes access to the list, but we still need to verify
+        * that nobody removed the entry while we were waiting on the lock.
+        */
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
-       list_del(&sas_device->list);
-       kfree(sas_device);
+       if (!list_empty(&sas_device->list)) {
+               list_del_init(&sas_device->list);
+               sas_device_put(sas_device);
+       }
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 }
 
@@ -609,12 +729,17 @@ _scsih_device_remove_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
                return;
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
-       sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
-       if (sas_device)
-               list_del(&sas_device->list);
+       sas_device = __mpt3sas_get_sdev_by_handle(ioc, handle);
+       if (sas_device) {
+               list_del_init(&sas_device->list);
+               sas_device_put(sas_device);
+       }
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-       if (sas_device)
+
+       if (sas_device) {
                _scsih_remove_device(ioc, sas_device);
+               sas_device_put(sas_device);
+       }
 }
 
 /**
@@ -635,13 +760,17 @@ mpt3sas_device_remove_by_sas_address(struct MPT3SAS_ADAPTER *ioc,
                return;
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
-       sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
-           sas_address);
-       if (sas_device)
-               list_del(&sas_device->list);
+       sas_device = __mpt3sas_get_sdev_by_addr(ioc, sas_address);
+       if (sas_device) {
+               list_del_init(&sas_device->list);
+               sas_device_put(sas_device);
+       }
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-       if (sas_device)
+
+       if (sas_device) {
                _scsih_remove_device(ioc, sas_device);
+               sas_device_put(sas_device);
+       }
 }
 
 /**
@@ -663,7 +792,20 @@ _scsih_sas_device_add(struct MPT3SAS_ADAPTER *ioc,
                ioc->name, __func__, sas_device->handle,
                (unsigned long long)sas_device->sas_address));
 
+       if (sas_device->enclosure_handle != 0)
+               dewtprintk(ioc, pr_info(MPT3SAS_FMT
+                   "%s: enclosure logical id(0x%016llx), slot( %d)\n",
+                   ioc->name, __func__, (unsigned long long)
+                   sas_device->enclosure_logical_id, sas_device->slot));
+
+       if (sas_device->connector_name[0] != '\0')
+               dewtprintk(ioc, pr_info(MPT3SAS_FMT
+                   "%s: enclosure level(0x%04x), connector name( %s)\n",
+                   ioc->name, __func__,
+                   sas_device->enclosure_level, sas_device->connector_name));
+
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
+       sas_device_get(sas_device);
        list_add_tail(&sas_device->list, &ioc->sas_device_list);
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
@@ -704,7 +846,20 @@ _scsih_sas_device_init_add(struct MPT3SAS_ADAPTER *ioc,
                __func__, sas_device->handle,
                (unsigned long long)sas_device->sas_address));
 
+       if (sas_device->enclosure_handle != 0)
+               dewtprintk(ioc, pr_info(MPT3SAS_FMT
+                   "%s: enclosure logical id(0x%016llx), slot( %d)\n",
+                   ioc->name, __func__, (unsigned long long)
+                   sas_device->enclosure_logical_id, sas_device->slot));
+
+       if (sas_device->connector_name[0] != '\0')
+               dewtprintk(ioc, pr_info(MPT3SAS_FMT
+                   "%s: enclosure level(0x%04x), connector name( %s)\n",
+                   ioc->name, __func__, sas_device->enclosure_level,
+                   sas_device->connector_name));
+
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
+       sas_device_get(sas_device);
        list_add_tail(&sas_device->list, &ioc->sas_device_init_list);
        _scsih_determine_boot_device(ioc, sas_device, 0);
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
@@ -1083,12 +1238,15 @@ _scsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
                goto not_sata;
        if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME))
                goto not_sata;
+
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
-       sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
-          sas_device_priv_data->sas_target->sas_address);
-       if (sas_device && sas_device->device_info &
-           MPI2_SAS_DEVICE_INFO_SATA_DEVICE)
-               max_depth = MPT3SAS_SATA_QUEUE_DEPTH;
+       sas_device = __mpt3sas_get_sdev_from_target(ioc, sas_target_priv_data);
+       if (sas_device) {
+               if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE)
+                       max_depth = MPT3SAS_SATA_QUEUE_DEPTH;
+
+               sas_device_put(sas_device);
+       }
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
  not_sata:
@@ -1145,12 +1303,13 @@ _scsih_target_alloc(struct scsi_target *starget)
        /* sas/sata devices */
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
        rphy = dev_to_rphy(starget->dev.parent);
-       sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
+       sas_device = __mpt3sas_get_sdev_by_addr(ioc,
           rphy->identify.sas_address);
 
        if (sas_device) {
                sas_target_priv_data->handle = sas_device->handle;
                sas_target_priv_data->sas_address = sas_device->sas_address;
+               sas_target_priv_data->sdev = sas_device;
                sas_device->starget = starget;
                sas_device->id = starget->id;
                sas_device->channel = starget->channel;
@@ -1200,13 +1359,21 @@ _scsih_target_destroy(struct scsi_target *starget)
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
        rphy = dev_to_rphy(starget->dev.parent);
-       sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
-          rphy->identify.sas_address);
+       sas_device = __mpt3sas_get_sdev_from_target(ioc, sas_target_priv_data);
        if (sas_device && (sas_device->starget == starget) &&
            (sas_device->id == starget->id) &&
            (sas_device->channel == starget->channel))
                sas_device->starget = NULL;
 
+       if (sas_device) {
+               /*
+                * Corresponding get() is in _scsih_target_alloc()
+                */
+               sas_target_priv_data->sdev = NULL;
+               sas_device_put(sas_device);
+
+               sas_device_put(sas_device);
+       }
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
  out:
@@ -1262,7 +1429,7 @@ _scsih_slave_alloc(struct scsi_device *sdev)
 
        if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) {
                spin_lock_irqsave(&ioc->sas_device_lock, flags);
-               sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
+               sas_device = __mpt3sas_get_sdev_by_addr(ioc,
                                        sas_target_priv_data->sas_address);
                if (sas_device && (sas_device->starget == NULL)) {
                        sdev_printk(KERN_INFO, sdev,
@@ -1270,6 +1437,10 @@ _scsih_slave_alloc(struct scsi_device *sdev)
                                __func__, __LINE__);
                        sas_device->starget = starget;
                }
+
+               if (sas_device)
+                       sas_device_put(sas_device);
+
                spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
        }
 
@@ -1304,10 +1475,14 @@ _scsih_slave_destroy(struct scsi_device *sdev)
 
        if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) {
                spin_lock_irqsave(&ioc->sas_device_lock, flags);
-               sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
-                  sas_target_priv_data->sas_address);
+               sas_device = __mpt3sas_get_sdev_from_target(ioc,
+                               sas_target_priv_data);
                if (sas_device && !sas_target_priv_data->num_luns)
                        sas_device->starget = NULL;
+
+               if (sas_device)
+                       sas_device_put(sas_device);
+
                spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
        }
 
@@ -1743,7 +1918,7 @@ _scsih_slave_configure(struct scsi_device *sdev)
        }
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
-       sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
+       sas_device = __mpt3sas_get_sdev_by_addr(ioc,
           sas_device_priv_data->sas_target->sas_address);
        if (!sas_device) {
                spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
@@ -1772,17 +1947,23 @@ _scsih_slave_configure(struct scsi_device *sdev)
            "sas_addr(0x%016llx), phy(%d), device_name(0x%016llx)\n",
            ds, handle, (unsigned long long)sas_device->sas_address,
            sas_device->phy, (unsigned long long)sas_device->device_name);
-       sdev_printk(KERN_INFO, sdev,
-               "%s: enclosure_logical_id(0x%016llx), slot(%d)\n",
-               ds, (unsigned long long)
-           sas_device->enclosure_logical_id, sas_device->slot);
+       if (sas_device->enclosure_handle != 0)
+               sdev_printk(KERN_INFO, sdev,
+                    "%s: enclosure_logical_id(0x%016llx), slot(%d)\n",
+                    ds, (unsigned long long)
+                    sas_device->enclosure_logical_id, sas_device->slot);
+       if (sas_device->connector_name[0] != '\0')
+               sdev_printk(KERN_INFO, sdev,
+                    "%s: enclosure level(0x%04x), connector name( %s)\n",
+                    ds, sas_device->enclosure_level,
+                    sas_device->connector_name);
 
+       sas_device_put(sas_device);
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
        if (!ssp_target)
                _scsih_display_sata_capabilities(ioc, handle, sdev);
 
-
        _scsih_change_queue_depth(sdev, qdepth);
 
        if (ssp_target) {
@@ -2173,8 +2354,7 @@ _scsih_tm_display_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd)
                    device_str, (unsigned long long)priv_target->sas_address);
        } else {
                spin_lock_irqsave(&ioc->sas_device_lock, flags);
-               sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
-                   priv_target->sas_address);
+               sas_device = __mpt3sas_get_sdev_from_target(ioc, priv_target);
                if (sas_device) {
                        if (priv_target->flags &
                            MPT_TARGET_FLAGS_RAID_COMPONENT) {
@@ -2189,12 +2369,20 @@ _scsih_tm_display_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd)
                            sas_device->handle,
                            (unsigned long long)sas_device->sas_address,
                            sas_device->phy);
-                       starget_printk(KERN_INFO, starget,
-                           "enclosure_logical_id(0x%016llx), slot(%d)\n",
-                          (unsigned long long)sas_device->enclosure_logical_id,
-                           sas_device->slot);
+                       if (sas_device->enclosure_handle != 0)
+                               starget_printk(KERN_INFO, starget,
+                                "enclosure_logical_id(0x%016llx), slot(%d)\n",
+                                (unsigned long long)
+                                sas_device->enclosure_logical_id,
+                                sas_device->slot);
+                       if (sas_device->connector_name)
+                               starget_printk(KERN_INFO, starget,
+                               "enclosure level(0x%04x),connector name(%s)\n",
+                                sas_device->enclosure_level,
+                                sas_device->connector_name);
+
+                       sas_device_put(sas_device);
                }
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
        }
 }
 
@@ -2268,10 +2456,11 @@ _scsih_dev_reset(struct scsi_cmnd *scmd)
 {
        struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
        struct MPT3SAS_DEVICE *sas_device_priv_data;
-       struct _sas_device *sas_device;
-       unsigned long flags;
+       struct _sas_device *sas_device = NULL;
        u16     handle;
        int r;
+       struct scsi_target *starget = scmd->device->sdev_target;
+       struct MPT3SAS_TARGET *target_priv_data = starget->hostdata;
 
        sdev_printk(KERN_INFO, scmd->device,
                "attempting device reset! scmd(%p)\n", scmd);
@@ -2291,12 +2480,10 @@ _scsih_dev_reset(struct scsi_cmnd *scmd)
        handle = 0;
        if (sas_device_priv_data->sas_target->flags &
            MPT_TARGET_FLAGS_RAID_COMPONENT) {
-               spin_lock_irqsave(&ioc->sas_device_lock, flags);
-               sas_device = _scsih_sas_device_find_by_handle(ioc,
-                  sas_device_priv_data->sas_target->handle);
+               sas_device = mpt3sas_get_sdev_from_target(ioc,
+                               target_priv_data);
                if (sas_device)
                        handle = sas_device->volume_handle;
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
        } else
                handle = sas_device_priv_data->sas_target->handle;
 
@@ -2313,6 +2500,10 @@ _scsih_dev_reset(struct scsi_cmnd *scmd)
  out:
        sdev_printk(KERN_INFO, scmd->device, "device reset: %s scmd(%p)\n",
            ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
+
+       if (sas_device)
+               sas_device_put(sas_device);
+
        return r;
 }
 
@@ -2327,11 +2518,11 @@ _scsih_target_reset(struct scsi_cmnd *scmd)
 {
        struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
        struct MPT3SAS_DEVICE *sas_device_priv_data;
-       struct _sas_device *sas_device;
-       unsigned long flags;
+       struct _sas_device *sas_device = NULL;
        u16     handle;
        int r;
        struct scsi_target *starget = scmd->device->sdev_target;
+       struct MPT3SAS_TARGET *target_priv_data = starget->hostdata;
 
        starget_printk(KERN_INFO, starget, "attempting target reset! scmd(%p)\n",
                scmd);
@@ -2351,12 +2542,10 @@ _scsih_target_reset(struct scsi_cmnd *scmd)
        handle = 0;
        if (sas_device_priv_data->sas_target->flags &
            MPT_TARGET_FLAGS_RAID_COMPONENT) {
-               spin_lock_irqsave(&ioc->sas_device_lock, flags);
-               sas_device = _scsih_sas_device_find_by_handle(ioc,
-                  sas_device_priv_data->sas_target->handle);
+               sas_device = mpt3sas_get_sdev_from_target(ioc,
+                               target_priv_data);
                if (sas_device)
                        handle = sas_device->volume_handle;
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
        } else
                handle = sas_device_priv_data->sas_target->handle;
 
@@ -2373,6 +2562,10 @@ _scsih_target_reset(struct scsi_cmnd *scmd)
  out:
        starget_printk(KERN_INFO, starget, "target reset: %s scmd(%p)\n",
            ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
+
+       if (sas_device)
+               sas_device_put(sas_device);
+
        return r;
 }
 
@@ -2430,32 +2623,36 @@ _scsih_fw_event_add(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event)
                return;
 
        spin_lock_irqsave(&ioc->fw_event_lock, flags);
+       fw_event_work_get(fw_event);
        INIT_LIST_HEAD(&fw_event->list);
        list_add_tail(&fw_event->list, &ioc->fw_event_list);
        INIT_WORK(&fw_event->work, _firmware_event_work);
+       fw_event_work_get(fw_event);
        queue_work(ioc->firmware_event_thread, &fw_event->work);
        spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
 }
 
 /**
- * _scsih_fw_event_free - delete fw_event
+ * _scsih_fw_event_del_from_list - delete fw_event from the list
  * @ioc: per adapter object
  * @fw_event: object describing the event
  * Context: This function will acquire ioc->fw_event_lock.
  *
- * This removes firmware event object from link list, frees associated memory.
+ * If the fw_event is on the fw_event_list, remove it and do a put.
  *
  * Return nothing.
  */
 static void
-_scsih_fw_event_free(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work
+_scsih_fw_event_del_from_list(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work
        *fw_event)
 {
        unsigned long flags;
 
        spin_lock_irqsave(&ioc->fw_event_lock, flags);
-       list_del(&fw_event->list);
-       kfree(fw_event);
+       if (!list_empty(&fw_event->list)) {
+               list_del_init(&fw_event->list);
+               fw_event_work_put(fw_event);
+       }
        spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
 }
 
@@ -2475,14 +2672,14 @@ mpt3sas_send_trigger_data_event(struct MPT3SAS_ADAPTER *ioc,
 
        if (ioc->is_driver_loading)
                return;
-       fw_event = kzalloc(sizeof(*fw_event) + sizeof(*event_data),
-                          GFP_ATOMIC);
+       fw_event = alloc_fw_event_work(sizeof(*event_data));
        if (!fw_event)
                return;
        fw_event->event = MPT3SAS_PROCESS_TRIGGER_DIAG;
        fw_event->ioc = ioc;
        memcpy(fw_event->event_data, event_data, sizeof(*event_data));
        _scsih_fw_event_add(ioc, fw_event);
+       fw_event_work_put(fw_event);
 }
 
 /**
@@ -2498,12 +2695,13 @@ _scsih_error_recovery_delete_devices(struct MPT3SAS_ADAPTER *ioc)
 
        if (ioc->is_driver_loading)
                return;
-       fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC);
+       fw_event = alloc_fw_event_work(0);
        if (!fw_event)
                return;
        fw_event->event = MPT3SAS_REMOVE_UNRESPONDING_DEVICES;
        fw_event->ioc = ioc;
        _scsih_fw_event_add(ioc, fw_event);
+       fw_event_work_put(fw_event);
 }
 
 /**
@@ -2517,12 +2715,29 @@ mpt3sas_port_enable_complete(struct MPT3SAS_ADAPTER *ioc)
 {
        struct fw_event_work *fw_event;
 
-       fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC);
+       fw_event = alloc_fw_event_work(0);
        if (!fw_event)
                return;
        fw_event->event = MPT3SAS_PORT_ENABLE_COMPLETE;
        fw_event->ioc = ioc;
        _scsih_fw_event_add(ioc, fw_event);
+       fw_event_work_put(fw_event);
+}
+
+static struct fw_event_work *dequeue_next_fw_event(struct MPT3SAS_ADAPTER *ioc)
+{
+       unsigned long flags;
+       struct fw_event_work *fw_event = NULL;
+
+       spin_lock_irqsave(&ioc->fw_event_lock, flags);
+       if (!list_empty(&ioc->fw_event_list)) {
+               fw_event = list_first_entry(&ioc->fw_event_list,
+                               struct fw_event_work, list);
+               list_del_init(&fw_event->list);
+       }
+       spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+
+       return fw_event;
 }
 
 /**
@@ -2537,17 +2752,94 @@ mpt3sas_port_enable_complete(struct MPT3SAS_ADAPTER *ioc)
 static void
 _scsih_fw_event_cleanup_queue(struct MPT3SAS_ADAPTER *ioc)
 {
-       struct fw_event_work *fw_event, *next;
+       struct fw_event_work *fw_event;
 
        if (list_empty(&ioc->fw_event_list) ||
             !ioc->firmware_event_thread || in_interrupt())
                return;
 
-       list_for_each_entry_safe(fw_event, next, &ioc->fw_event_list, list) {
-               if (cancel_delayed_work_sync(&fw_event->delayed_work)) {
-                       _scsih_fw_event_free(ioc, fw_event);
-                       continue;
-               }
+       while ((fw_event = dequeue_next_fw_event(ioc))) {
+               /*
+                * Wait on the fw_event to complete. If this returns 1, then
+                * the event was never executed, and we need a put for the
+                * reference the delayed_work had on the fw_event.
+                *
+                * If it did execute, we wait for it to finish, and the put will
+                * happen from _firmware_event_work()
+                */
+               if (cancel_work_sync(&fw_event->work))
+                       fw_event_work_put(fw_event);
+
+               fw_event_work_put(fw_event);
+       }
+}
+
+/**
+ * _scsih_internal_device_block - block the sdev device
+ * @sdev: per device object
+ * @sas_device_priv_data : per device driver private data
+ *
+ * make sure device is blocked without error, if not
+ * print an error
+ */
+static void
+_scsih_internal_device_block(struct scsi_device *sdev,
+                       struct MPT3SAS_DEVICE *sas_device_priv_data)
+{
+       int r = 0;
+
+       sdev_printk(KERN_INFO, sdev, "device_block, handle(0x%04x)\n",
+           sas_device_priv_data->sas_target->handle);
+       sas_device_priv_data->block = 1;
+
+       r = scsi_internal_device_block(sdev);
+       if (r == -EINVAL)
+               sdev_printk(KERN_WARNING, sdev,
+                   "device_block failed with return(%d) for handle(0x%04x)\n",
+                   sas_device_priv_data->sas_target->handle, r);
+}
+
+/**
+ * _scsih_internal_device_unblock - unblock the sdev device
+ * @sdev: per device object
+ * @sas_device_priv_data : per device driver private data
+ * make sure device is unblocked without error, if not retry
+ * by blocking and then unblocking
+ */
+
+static void
+_scsih_internal_device_unblock(struct scsi_device *sdev,
+                       struct MPT3SAS_DEVICE *sas_device_priv_data)
+{
+       int r = 0;
+
+       sdev_printk(KERN_WARNING, sdev, "device_unblock and setting to running, "
+           "handle(0x%04x)\n", sas_device_priv_data->sas_target->handle);
+       sas_device_priv_data->block = 0;
+       r = scsi_internal_device_unblock(sdev, SDEV_RUNNING);
+       if (r == -EINVAL) {
+               /* The device has been set to SDEV_RUNNING by SD layer during
+                * device addition but the request queue is still stopped by
+                * our earlier block call. We need to perform a block again
+                * to get the device to SDEV_BLOCK and then to SDEV_RUNNING */
+
+               sdev_printk(KERN_WARNING, sdev,
+                   "device_unblock failed with return(%d) for handle(0x%04x) "
+                   "performing a block followed by an unblock\n",
+                   sas_device_priv_data->sas_target->handle, r);
+               sas_device_priv_data->block = 1;
+               r = scsi_internal_device_block(sdev);
+               if (r)
+                       sdev_printk(KERN_WARNING, sdev, "retried device_block "
+                           "failed with return(%d) for handle(0x%04x)\n",
+                           sas_device_priv_data->sas_target->handle, r);
+
+               sas_device_priv_data->block = 0;
+               r = scsi_internal_device_unblock(sdev, SDEV_RUNNING);
+               if (r)
+                       sdev_printk(KERN_WARNING, sdev, "retried device_unblock"
+                           " failed with return(%d) for handle(0x%04x)\n",
+                           sas_device_priv_data->sas_target->handle, r);
        }
 }
 
@@ -2570,11 +2862,10 @@ _scsih_ublock_io_all_device(struct MPT3SAS_ADAPTER *ioc)
                if (!sas_device_priv_data->block)
                        continue;
 
-               sas_device_priv_data->block = 0;
                dewtprintk(ioc, sdev_printk(KERN_INFO, sdev,
                        "device_running, handle(0x%04x)\n",
                    sas_device_priv_data->sas_target->handle));
-               scsi_internal_device_unblock(sdev, SDEV_RUNNING);
+               _scsih_internal_device_unblock(sdev, sas_device_priv_data);
        }
 }
 
@@ -2599,10 +2890,9 @@ _scsih_ublock_io_device(struct MPT3SAS_ADAPTER *ioc, u64 sas_address)
                if (sas_device_priv_data->sas_target->sas_address
                    != sas_address)
                        continue;
-               if (sas_device_priv_data->block) {
-                       sas_device_priv_data->block = 0;
-                       scsi_internal_device_unblock(sdev, SDEV_RUNNING);
-               }
+               if (sas_device_priv_data->block)
+                       _scsih_internal_device_unblock(sdev,
+                               sas_device_priv_data);
        }
 }
 
@@ -2625,10 +2915,7 @@ _scsih_block_io_all_device(struct MPT3SAS_ADAPTER *ioc)
                        continue;
                if (sas_device_priv_data->block)
                        continue;
-               sas_device_priv_data->block = 1;
-               scsi_internal_device_block(sdev);
-               sdev_printk(KERN_INFO, sdev, "device_blocked, handle(0x%04x)\n",
-                   sas_device_priv_data->sas_target->handle);
+               _scsih_internal_device_block(sdev, sas_device_priv_data);
        }
 }
 
@@ -2644,6 +2931,11 @@ _scsih_block_io_device(struct MPT3SAS_ADAPTER *ioc, u16 handle)
 {
        struct MPT3SAS_DEVICE *sas_device_priv_data;
        struct scsi_device *sdev;
+       struct _sas_device *sas_device;
+
+       sas_device = mpt3sas_get_sdev_by_handle(ioc, handle);
+       if (!sas_device)
+               return;
 
        shost_for_each_device(sdev, ioc->shost) {
                sas_device_priv_data = sdev->hostdata;
@@ -2653,11 +2945,12 @@ _scsih_block_io_device(struct MPT3SAS_ADAPTER *ioc, u16 handle)
                        continue;
                if (sas_device_priv_data->block)
                        continue;
-               sas_device_priv_data->block = 1;
-               scsi_internal_device_block(sdev);
-               sdev_printk(KERN_INFO, sdev,
-                       "device_blocked, handle(0x%04x)\n", handle);
+               if (sas_device->pend_sas_rphy_add)
+                       continue;
+               _scsih_internal_device_block(sdev, sas_device_priv_data);
        }
+
+       sas_device_put(sas_device);
 }
 
 /**
@@ -2683,15 +2976,15 @@ _scsih_block_io_to_children_attached_to_ex(struct MPT3SAS_ADAPTER *ioc,
 
        list_for_each_entry(mpt3sas_port,
           &sas_expander->sas_port_list, port_list) {
-               if (mpt3sas_port->remote_identify.device_type ==
-                   SAS_END_DEVICE) {
+               if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE) {
                        spin_lock_irqsave(&ioc->sas_device_lock, flags);
-                       sas_device =
-                           mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
-                          mpt3sas_port->remote_identify.sas_address);
-                       if (sas_device)
+                       sas_device = __mpt3sas_get_sdev_by_addr(ioc,
+                                       mpt3sas_port->remote_identify.sas_address);
+                       if (sas_device) {
                                set_bit(sas_device->handle,
-                                   ioc->blocking_handles);
+                                       ioc->blocking_handles);
+                               sas_device_put(sas_device);
+                       }
                        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
                }
        }
@@ -2759,7 +3052,7 @@ _scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle)
 {
        Mpi2SCSITaskManagementRequest_t *mpi_request;
        u16 smid;
-       struct _sas_device *sas_device;
+       struct _sas_device *sas_device = NULL;
        struct MPT3SAS_TARGET *sas_target_priv_data = NULL;
        u64 sas_address = 0;
        unsigned long flags;
@@ -2792,7 +3085,7 @@ _scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle)
                return;
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
-       sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+       sas_device = __mpt3sas_get_sdev_by_handle(ioc, handle);
        if (sas_device && sas_device->starget &&
            sas_device->starget->hostdata) {
                sas_target_priv_data = sas_device->starget->hostdata;
@@ -2806,6 +3099,18 @@ _scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle)
                        "setting delete flag: handle(0x%04x), sas_addr(0x%016llx)\n",
                        ioc->name, handle,
                    (unsigned long long)sas_address));
+               if (sas_device->enclosure_handle != 0)
+                       dewtprintk(ioc, pr_info(MPT3SAS_FMT
+                        "setting delete flag:enclosure logical id(0x%016llx),"
+                        " slot(%d)\n", ioc->name, (unsigned long long)
+                         sas_device->enclosure_logical_id,
+                         sas_device->slot));
+               if (sas_device->connector_name)
+                       dewtprintk(ioc, pr_info(MPT3SAS_FMT
+                        "setting delete flag: enclosure level(0x%04x),"
+                        " connector name( %s)\n", ioc->name,
+                         sas_device->enclosure_level,
+                         sas_device->connector_name));
                _scsih_ublock_io_device(ioc, sas_address);
                sas_target_priv_data->handle = MPT3SAS_INVALID_DEVICE_HANDLE;
        }
@@ -2814,14 +3119,14 @@ _scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle)
        if (!smid) {
                delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC);
                if (!delayed_tr)
-                       return;
+                       goto out;
                INIT_LIST_HEAD(&delayed_tr->list);
                delayed_tr->handle = handle;
                list_add_tail(&delayed_tr->list, &ioc->delayed_tr_list);
                dewtprintk(ioc, pr_info(MPT3SAS_FMT
                    "DELAYED:tr:handle(0x%04x), (open)\n",
                    ioc->name, handle));
-               return;
+               goto out;
        }
 
        dewtprintk(ioc, pr_info(MPT3SAS_FMT
@@ -2835,6 +3140,9 @@ _scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle)
        mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
        mpt3sas_base_put_smid_hi_priority(ioc, smid);
        mpt3sas_trigger_master(ioc, MASTER_TRIGGER_DEVICE_REMOVAL);
+out:
+       if (sas_device)
+               sas_device_put(sas_device);
 }
 
 /**
@@ -3685,7 +3993,6 @@ _scsih_scsi_ioc_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
        char *desc_scsi_state = ioc->tmp_string;
        u32 log_info = le32_to_cpu(mpi_reply->IOCLogInfo);
        struct _sas_device *sas_device = NULL;
-       unsigned long flags;
        struct scsi_target *starget = scmd->device->sdev_target;
        struct MPT3SAS_TARGET *priv_target = starget->hostdata;
        char *device_str = NULL;
@@ -3813,20 +4120,28 @@ _scsih_scsi_ioc_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
                pr_warn(MPT3SAS_FMT "\t%s wwid(0x%016llx)\n", ioc->name,
                    device_str, (unsigned long long)priv_target->sas_address);
        } else {
-               spin_lock_irqsave(&ioc->sas_device_lock, flags);
-               sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
-                   priv_target->sas_address);
+               sas_device = mpt3sas_get_sdev_from_target(ioc, priv_target);
                if (sas_device) {
                        pr_warn(MPT3SAS_FMT
                                "\tsas_address(0x%016llx), phy(%d)\n",
                                ioc->name, (unsigned long long)
                            sas_device->sas_address, sas_device->phy);
-                       pr_warn(MPT3SAS_FMT
-                           "\tenclosure_logical_id(0x%016llx), slot(%d)\n",
-                           ioc->name, (unsigned long long)
-                           sas_device->enclosure_logical_id, sas_device->slot);
+                       if (sas_device->enclosure_handle != 0)
+                               pr_warn(MPT3SAS_FMT
+                                 "\tenclosure_logical_id(0x%016llx),"
+                                 "slot(%d)\n", ioc->name,
+                                 (unsigned long long)
+                                 sas_device->enclosure_logical_id,
+                                 sas_device->slot);
+                       if (sas_device->connector_name[0])
+                               pr_warn(MPT3SAS_FMT
+                                 "\tenclosure level(0x%04x),"
+                                 " connector name( %s)\n", ioc->name,
+                                 sas_device->enclosure_level,
+                                 sas_device->connector_name);
+
+                       sas_device_put(sas_device);
                }
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
        }
 
        pr_warn(MPT3SAS_FMT
@@ -3878,7 +4193,7 @@ _scsih_turn_on_pfa_led(struct MPT3SAS_ADAPTER *ioc, u16 handle)
        Mpi2SepRequest_t mpi_request;
        struct _sas_device *sas_device;
 
-       sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+       sas_device = mpt3sas_get_sdev_by_handle(ioc, handle);
        if (!sas_device)
                return;
 
@@ -3893,7 +4208,7 @@ _scsih_turn_on_pfa_led(struct MPT3SAS_ADAPTER *ioc, u16 handle)
            &mpi_request)) != 0) {
                pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ioc->name,
                __FILE__, __LINE__, __func__);
-               return;
+               goto out;
        }
        sas_device->pfa_led_on = 1;
 
@@ -3902,8 +4217,10 @@ _scsih_turn_on_pfa_led(struct MPT3SAS_ADAPTER *ioc, u16 handle)
                        "enclosure_processor: ioc_status (0x%04x), loginfo(0x%08x)\n",
                        ioc->name, le16_to_cpu(mpi_reply.IOCStatus),
                    le32_to_cpu(mpi_reply.IOCLogInfo)));
-               return;
+               goto out;
        }
+out:
+       sas_device_put(sas_device);
 }
 /**
  * _scsih_turn_off_pfa_led - turn off Fault LED
@@ -3956,13 +4273,14 @@ _scsih_send_event_to_turn_on_pfa_led(struct MPT3SAS_ADAPTER *ioc, u16 handle)
 {
        struct fw_event_work *fw_event;
 
-       fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC);
+       fw_event = alloc_fw_event_work(0);
        if (!fw_event)
                return;
        fw_event->event = MPT3SAS_TURN_ON_PFA_LED;
        fw_event->device_handle = handle;
        fw_event->ioc = ioc;
        _scsih_fw_event_add(ioc, fw_event);
+       fw_event_work_put(fw_event);
 }
 
 /**
@@ -3986,20 +4304,27 @@ _scsih_smart_predicted_fault(struct MPT3SAS_ADAPTER *ioc, u16 handle)
 
        /* only handle non-raid devices */
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
-       sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+       sas_device = __mpt3sas_get_sdev_by_handle(ioc, handle);
        if (!sas_device) {
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-               return;
+               goto out_unlock;
        }
        starget = sas_device->starget;
        sas_target_priv_data = starget->hostdata;
 
        if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT) ||
-          ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME))) {
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-               return;
-       }
-       starget_printk(KERN_WARNING, starget, "predicted fault\n");
+          ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)))
+               goto out_unlock;
+
+       if (sas_device->enclosure_handle != 0)
+               starget_printk(KERN_INFO, starget, "predicted fault, "
+                       "enclosure logical id(0x%016llx), slot(%d)\n",
+                       (unsigned long long)sas_device->enclosure_logical_id,
+                       sas_device->slot);
+       if (sas_device->connector_name[0] != '\0')
+               starget_printk(KERN_WARNING, starget, "predicted fault, "
+                       "enclosure level(0x%04x), connector name( %s)\n",
+                       sas_device->enclosure_level,
+                       sas_device->connector_name);
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
        if (ioc->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM)
@@ -4012,7 +4337,7 @@ _scsih_smart_predicted_fault(struct MPT3SAS_ADAPTER *ioc, u16 handle)
        if (!event_reply) {
                pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
                    ioc->name, __FILE__, __LINE__, __func__);
-               return;
+               goto out;
        }
 
        event_reply->Function = MPI2_FUNCTION_EVENT_NOTIFICATION;
@@ -4029,6 +4354,14 @@ _scsih_smart_predicted_fault(struct MPT3SAS_ADAPTER *ioc, u16 handle)
        event_data->SASAddress = cpu_to_le64(sas_target_priv_data->sas_address);
        mpt3sas_ctl_add_to_event_log(ioc, event_reply);
        kfree(event_reply);
+out:
+       if (sas_device)
+               sas_device_put(sas_device);
+       return;
+
+out_unlock:
+       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+       goto out;
 }
 
 /**
@@ -4119,8 +4452,15 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
                        _scsih_smart_predicted_fault(ioc,
                            le16_to_cpu(mpi_reply->DevHandle));
                mpt3sas_trigger_scsi(ioc, data.skey, data.asc, data.ascq);
-       }
 
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+               if (!(ioc->logging_level & MPT_DEBUG_REPLY) &&
+                    ((scmd->sense_buffer[2] == UNIT_ATTENTION) ||
+                    (scmd->sense_buffer[2] == MEDIUM_ERROR) ||
+                    (scmd->sense_buffer[2] == HARDWARE_ERROR)))
+                       _scsih_scsi_ioc_info(ioc, scmd, mpi_reply, smid);
+#endif
+       }
        switch (ioc_status) {
        case MPI2_IOCSTATUS_BUSY:
        case MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES:
@@ -4146,6 +4486,9 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
                                scmd->device->expecting_cc_ua = 1;
                        }
                        break;
+               } else if (log_info == VIRTUAL_IO_FAILED_RETRY) {
+                       scmd->result = DID_RESET << 16;
+                       break;
                }
                scmd->result = DID_SOFT_ERROR << 16;
                break;
@@ -4772,13 +5115,11 @@ _scsih_check_device(struct MPT3SAS_ADAPTER *ioc,
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
        sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
-       sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
+       sas_device = __mpt3sas_get_sdev_by_addr(ioc,
            sas_address);
 
-       if (!sas_device) {
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-               return;
-       }
+       if (!sas_device)
+               goto out_unlock;
 
        if (unlikely(sas_device->handle != handle)) {
                starget = sas_device->starget;
@@ -4788,6 +5129,16 @@ _scsih_check_device(struct MPT3SAS_ADAPTER *ioc,
                        sas_device->handle, handle);
                sas_target_priv_data->handle = handle;
                sas_device->handle = handle;
+               if (sas_device_pg0.Flags &
+                    MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) {
+                       sas_device->enclosure_level =
+                               le16_to_cpu(sas_device_pg0.EnclosureLevel);
+                       memcpy(&sas_device->connector_name[0],
+                               &sas_device_pg0.ConnectorName[0], 4);
+               } else {
+                       sas_device->enclosure_level = 0;
+                       sas_device->connector_name[0] = '\0';
+               }
        }
 
        /* check if device is present */
@@ -4796,20 +5147,24 @@ _scsih_check_device(struct MPT3SAS_ADAPTER *ioc,
                pr_err(MPT3SAS_FMT
                        "device is not present handle(0x%04x), flags!!!\n",
                        ioc->name, handle);
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-               return;
+               goto out_unlock;
        }
 
        /* check if there were any issues with discovery */
        if (_scsih_check_access_status(ioc, sas_address, handle,
-           sas_device_pg0.AccessStatus)) {
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-               return;
-       }
+           sas_device_pg0.AccessStatus))
+               goto out_unlock;
 
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
        _scsih_ublock_io_device(ioc, sas_address);
+       if (sas_device)
+               sas_device_put(sas_device);
+       return;
 
+out_unlock:
+       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+       if (sas_device)
+               sas_device_put(sas_device);
 }
 
 /**
@@ -4834,7 +5189,6 @@ _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num,
        u32 ioc_status;
        u64 sas_address;
        u32 device_info;
-       unsigned long flags;
 
        if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
            MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
@@ -4870,13 +5224,11 @@ _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num,
            sas_device_pg0.AccessStatus))
                return -1;
 
-       spin_lock_irqsave(&ioc->sas_device_lock, flags);
-       sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
-           sas_address);
-       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-
-       if (sas_device)
+       sas_device = mpt3sas_get_sdev_by_addr(ioc, sas_address);
+       if (sas_device) {
+               sas_device_put(sas_device);
                return -1;
+       }
 
        sas_device = kzalloc(sizeof(struct _sas_device),
            GFP_KERNEL);
@@ -4886,6 +5238,7 @@ _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num,
                return 0;
        }
 
+       kref_init(&sas_device->refcount);
        sas_device->handle = handle;
        if (_scsih_get_sas_address(ioc,
            le16_to_cpu(sas_device_pg0.ParentDevHandle),
@@ -4894,14 +5247,24 @@ _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num,
                    ioc->name, __FILE__, __LINE__, __func__);
        sas_device->enclosure_handle =
            le16_to_cpu(sas_device_pg0.EnclosureHandle);
-       sas_device->slot =
-           le16_to_cpu(sas_device_pg0.Slot);
+       if (sas_device->enclosure_handle != 0)
+               sas_device->slot =
+                   le16_to_cpu(sas_device_pg0.Slot);
        sas_device->device_info = device_info;
        sas_device->sas_address = sas_address;
        sas_device->phy = sas_device_pg0.PhyNum;
        sas_device->fast_path = (le16_to_cpu(sas_device_pg0.Flags) &
            MPI25_SAS_DEVICE0_FLAGS_FAST_PATH_CAPABLE) ? 1 : 0;
 
+       if (sas_device_pg0.Flags & MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) {
+               sas_device->enclosure_level =
+                       le16_to_cpu(sas_device_pg0.EnclosureLevel);
+               memcpy(&sas_device->connector_name[0],
+                       &sas_device_pg0.ConnectorName[0], 4);
+       } else {
+               sas_device->enclosure_level = 0;
+               sas_device->connector_name[0] = '\0';
+       }
        /* get enclosure_logical_id */
        if (sas_device->enclosure_handle && !(mpt3sas_config_get_enclosure_pg0(
           ioc, &mpi_reply, &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE,
@@ -4917,6 +5280,7 @@ _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num,
        else
                _scsih_sas_device_add(ioc, sas_device);
 
+       sas_device_put(sas_device);
        return 0;
 }
 
@@ -4943,6 +5307,18 @@ _scsih_remove_device(struct MPT3SAS_ADAPTER *ioc,
                ioc->name, __func__,
            sas_device->handle, (unsigned long long)
            sas_device->sas_address));
+       if (sas_device->enclosure_handle != 0)
+               dewtprintk(ioc, pr_info(MPT3SAS_FMT
+                   "%s: enter: enclosure logical id(0x%016llx), slot(%d)\n",
+                   ioc->name, __func__,
+                   (unsigned long long)sas_device->enclosure_logical_id,
+                   sas_device->slot));
+       if (sas_device->connector_name[0] != '\0')
+               dewtprintk(ioc, pr_info(MPT3SAS_FMT
+                 "%s: enter: enclosure level(0x%04x), connector name( %s)\n",
+                 ioc->name, __func__,
+                 sas_device->enclosure_level,
+                 sas_device->connector_name));
 
        if (sas_device->starget && sas_device->starget->hostdata) {
                sas_target_priv_data = sas_device->starget->hostdata;
@@ -4959,12 +5335,34 @@ _scsih_remove_device(struct MPT3SAS_ADAPTER *ioc,
                "removing handle(0x%04x), sas_addr(0x%016llx)\n",
                ioc->name, sas_device->handle,
            (unsigned long long) sas_device->sas_address);
+       if (sas_device->enclosure_handle != 0)
+               pr_info(MPT3SAS_FMT
+                 "removing : enclosure logical id(0x%016llx), slot(%d)\n",
+                 ioc->name,
+                 (unsigned long long)sas_device->enclosure_logical_id,
+                 sas_device->slot);
+       if (sas_device->connector_name[0] != '\0')
+               pr_info(MPT3SAS_FMT
+                 "removing enclosure level(0x%04x), connector name( %s)\n",
+                 ioc->name, sas_device->enclosure_level,
+                 sas_device->connector_name);
 
        dewtprintk(ioc, pr_info(MPT3SAS_FMT
                "%s: exit: handle(0x%04x), sas_addr(0x%016llx)\n",
                ioc->name, __func__,
-           sas_device->handle, (unsigned long long)
-           sas_device->sas_address));
+               sas_device->handle, (unsigned long long)
+               sas_device->sas_address));
+       if (sas_device->enclosure_handle != 0)
+               dewtprintk(ioc, pr_info(MPT3SAS_FMT
+                   "%s: exit: enclosure logical id(0x%016llx), slot(%d)\n",
+                   ioc->name, __func__,
+                   (unsigned long long)sas_device->enclosure_logical_id,
+                   sas_device->slot));
+       if (sas_device->connector_name[0] != '\0')
+               dewtprintk(ioc, pr_info(MPT3SAS_FMT
+                   "%s: exit: enclosure level(0x%04x), connector name(%s)\n",
+                   ioc->name, __func__, sas_device->enclosure_level,
+                   sas_device->connector_name));
 
        kfree(sas_device);
 }
@@ -5291,25 +5689,25 @@ _scsih_sas_device_status_change_event(struct MPT3SAS_ADAPTER *ioc,
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
        sas_address = le64_to_cpu(event_data->SASAddress);
-       sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
-           sas_address);
+       sas_device = __mpt3sas_get_sdev_by_addr(ioc, sas_address);
 
-       if (!sas_device || !sas_device->starget) {
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-               return;
-       }
+       if (!sas_device || !sas_device->starget)
+               goto out;
 
        target_priv_data = sas_device->starget->hostdata;
-       if (!target_priv_data) {
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-               return;
-       }
+       if (!target_priv_data)
+               goto out;
 
        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;
+
+out:
+       if (sas_device)
+               sas_device_put(sas_device);
+
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 }
 
@@ -5793,7 +6191,7 @@ _scsih_sas_pd_expose(struct MPT3SAS_ADAPTER *ioc,
        u16 handle = le16_to_cpu(element->PhysDiskDevHandle);
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
-       sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+       sas_device = __mpt3sas_get_sdev_by_handle(ioc, handle);
        if (sas_device) {
                sas_device->volume_handle = 0;
                sas_device->volume_wwid = 0;
@@ -5812,6 +6210,8 @@ _scsih_sas_pd_expose(struct MPT3SAS_ADAPTER *ioc,
        /* exposing raid component */
        if (starget)
                starget_for_each_device(starget, NULL, _scsih_reprobe_lun);
+
+       sas_device_put(sas_device);
 }
 
 /**
@@ -5840,7 +6240,7 @@ _scsih_sas_pd_hide(struct MPT3SAS_ADAPTER *ioc,
                    &volume_wwid);
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
-       sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+       sas_device = __mpt3sas_get_sdev_by_handle(ioc, handle);
        if (sas_device) {
                set_bit(handle, ioc->pd_handles);
                if (sas_device->starget && sas_device->starget->hostdata) {
@@ -5860,6 +6260,8 @@ _scsih_sas_pd_hide(struct MPT3SAS_ADAPTER *ioc,
        _scsih_ir_fastpath(ioc, handle, element->PhysDiskNum);
        if (starget)
                starget_for_each_device(starget, (void *)1, _scsih_reprobe_lun);
+
+       sas_device_put(sas_device);
 }
 
 /**
@@ -5892,7 +6294,6 @@ _scsih_sas_pd_add(struct MPT3SAS_ADAPTER *ioc,
        Mpi2EventIrConfigElement_t *element)
 {
        struct _sas_device *sas_device;
-       unsigned long flags;
        u16 handle = le16_to_cpu(element->PhysDiskDevHandle);
        Mpi2ConfigReply_t mpi_reply;
        Mpi2SasDevicePage0_t sas_device_pg0;
@@ -5902,11 +6303,10 @@ _scsih_sas_pd_add(struct MPT3SAS_ADAPTER *ioc,
 
        set_bit(handle, ioc->pd_handles);
 
-       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);
+       sas_device = mpt3sas_get_sdev_by_handle(ioc, handle);
        if (sas_device) {
                _scsih_ir_fastpath(ioc, handle, element->PhysDiskNum);
+               sas_device_put(sas_device);
                return;
        }
 
@@ -6183,7 +6583,6 @@ _scsih_sas_ir_physical_disk_event(struct MPT3SAS_ADAPTER *ioc,
        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;
@@ -6212,12 +6611,12 @@ _scsih_sas_ir_physical_disk_event(struct MPT3SAS_ADAPTER *ioc,
        case MPI2_RAID_PD_STATE_HOT_SPARE:
 
                set_bit(handle, ioc->pd_handles);
-               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 = mpt3sas_get_sdev_by_handle(ioc, handle);
+               if (sas_device) {
+                       sas_device_put(sas_device);
                        return;
+               }
 
                if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply,
                    &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE,
@@ -6357,9 +6756,7 @@ _scsih_prep_device_scan(struct MPT3SAS_ADAPTER *ioc)
 /**
  * _scsih_mark_responding_sas_device - mark a sas_devices as responding
  * @ioc: per adapter object
- * @sas_address: sas address
- * @slot: enclosure slot id
- * @handle: device handle
+ * @sas_device_pg0: SAS Device page 0
  *
  * After host reset, find out whether devices are still responding.
  * Used in _scsih_remove_unresponsive_sas_devices.
@@ -6367,8 +6764,8 @@ _scsih_prep_device_scan(struct MPT3SAS_ADAPTER *ioc)
  * Return nothing.
  */
 static void
-_scsih_mark_responding_sas_device(struct MPT3SAS_ADAPTER *ioc, u64 sas_address,
-       u16 slot, u16 handle)
+_scsih_mark_responding_sas_device(struct MPT3SAS_ADAPTER *ioc,
+Mpi2SasDevicePage0_t *sas_device_pg0)
 {
        struct MPT3SAS_TARGET *sas_target_priv_data = NULL;
        struct scsi_target *starget;
@@ -6377,8 +6774,8 @@ _scsih_mark_responding_sas_device(struct MPT3SAS_ADAPTER *ioc, u64 sas_address,
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
        list_for_each_entry(sas_device, &ioc->sas_device_list, list) {
-               if (sas_device->sas_address == sas_address &&
-                   sas_device->slot == slot) {
+               if ((sas_device->sas_address == sas_device_pg0->SASAddress) &&
+                       (sas_device->slot == sas_device_pg0->Slot)) {
                        sas_device->responding = 1;
                        starget = sas_device->starget;
                        if (starget && starget->hostdata) {
@@ -6387,22 +6784,40 @@ _scsih_mark_responding_sas_device(struct MPT3SAS_ADAPTER *ioc, u64 sas_address,
                                sas_target_priv_data->deleted = 0;
                        } else
                                sas_target_priv_data = NULL;
-                       if (starget)
+                       if (starget) {
                                starget_printk(KERN_INFO, starget,
-                                   "handle(0x%04x), sas_addr(0x%016llx), "
-                                   "enclosure logical id(0x%016llx), "
-                                   "slot(%d)\n", handle,
-                                   (unsigned long long)sas_device->sas_address,
+                                   "handle(0x%04x), sas_addr(0x%016llx)\n",
+                                   sas_device_pg0->DevHandle,
                                    (unsigned long long)
-                                   sas_device->enclosure_logical_id,
-                                   sas_device->slot);
-                       if (sas_device->handle == handle)
+                                   sas_device->sas_address);
+
+                               if (sas_device->enclosure_handle != 0)
+                                       starget_printk(KERN_INFO, starget,
+                                        "enclosure logical id(0x%016llx),"
+                                        " slot(%d)\n",
+                                        (unsigned long long)
+                                        sas_device->enclosure_logical_id,
+                                        sas_device->slot);
+                       }
+                       if (sas_device_pg0->Flags &
+                             MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) {
+                               sas_device->enclosure_level =
+                                  le16_to_cpu(sas_device_pg0->EnclosureLevel);
+                               memcpy(&sas_device->connector_name[0],
+                                       &sas_device_pg0->ConnectorName[0], 4);
+                       } else {
+                               sas_device->enclosure_level = 0;
+                               sas_device->connector_name[0] = '\0';
+                       }
+
+                       if (sas_device->handle == sas_device_pg0->DevHandle)
                                goto out;
                        pr_info("\thandle changed from(0x%04x)!!!\n",
                            sas_device->handle);
-                       sas_device->handle = handle;
+                       sas_device->handle = sas_device_pg0->DevHandle;
                        if (sas_target_priv_data)
-                               sas_target_priv_data->handle = handle;
+                               sas_target_priv_data->handle =
+                                       sas_device_pg0->DevHandle;
                        goto out;
                }
        }
@@ -6441,13 +6856,15 @@ _scsih_search_responding_sas_devices(struct MPT3SAS_ADAPTER *ioc)
                    MPI2_IOCSTATUS_MASK;
                if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
                        break;
-               handle = le16_to_cpu(sas_device_pg0.DevHandle);
+               handle = sas_device_pg0.DevHandle =
+                               le16_to_cpu(sas_device_pg0.DevHandle);
                device_info = le32_to_cpu(sas_device_pg0.DeviceInfo);
                if (!(_scsih_is_end_device(device_info)))
                        continue;
-               _scsih_mark_responding_sas_device(ioc,
-                   le64_to_cpu(sas_device_pg0.SASAddress),
-                   le16_to_cpu(sas_device_pg0.Slot), handle);
+               sas_device_pg0.SASAddress =
+                               le64_to_cpu(sas_device_pg0.SASAddress);
+               sas_device_pg0.Slot = le16_to_cpu(sas_device_pg0.Slot);
+               _scsih_mark_responding_sas_device(ioc, &sas_device_pg0);
        }
 
  out:
@@ -6673,6 +7090,7 @@ _scsih_remove_unresponding_sas_devices(struct MPT3SAS_ADAPTER *ioc)
        struct _raid_device *raid_device, *raid_device_next;
        struct list_head tmp_list;
        unsigned long flags;
+       LIST_HEAD(head);
 
        pr_info(MPT3SAS_FMT "removing unresponding devices: start\n",
            ioc->name);
@@ -6680,14 +7098,29 @@ _scsih_remove_unresponding_sas_devices(struct MPT3SAS_ADAPTER *ioc)
        /* removing unresponding end devices */
        pr_info(MPT3SAS_FMT "removing unresponding devices: end-devices\n",
            ioc->name);
+
+       /*
+        * Iterate, pulling off devices marked as non-responding. We become the
+        * owner for the reference the list had on any object we prune.
+        */
+       spin_lock_irqsave(&ioc->sas_device_lock, flags);
        list_for_each_entry_safe(sas_device, sas_device_next,
-           &ioc->sas_device_list, list) {
+                                &ioc->sas_device_list, list) {
                if (!sas_device->responding)
-                       mpt3sas_device_remove_by_sas_address(ioc,
-                           sas_device->sas_address);
+                       list_move_tail(&sas_device->list, &head);
                else
                        sas_device->responding = 0;
        }
+       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+       /*
+        * Now, uninitialize and remove the unresponding devices we pruned.
+        */
+       list_for_each_entry_safe(sas_device, sas_device_next, &head, list) {
+               _scsih_remove_device(ioc, sas_device);
+               list_del_init(&sas_device->list);
+               sas_device_put(sas_device);
+       }
 
        /* removing unresponding volumes */
        if (ioc->ir_firmware) {
@@ -6841,11 +7274,11 @@ _scsih_scan_for_devices_after_reset(struct MPT3SAS_ADAPTER *ioc)
                }
                phys_disk_num = pd_pg0.PhysDiskNum;
                handle = le16_to_cpu(pd_pg0.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 = mpt3sas_get_sdev_by_handle(ioc, handle);
+               if (sas_device) {
+                       sas_device_put(sas_device);
                        continue;
+               }
                if (mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply,
                    &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE,
                    handle) != 0)
@@ -6966,12 +7399,12 @@ _scsih_scan_for_devices_after_reset(struct MPT3SAS_ADAPTER *ioc)
                if (!(_scsih_is_end_device(
                    le32_to_cpu(sas_device_pg0.DeviceInfo))))
                        continue;
-               spin_lock_irqsave(&ioc->sas_device_lock, flags);
-               sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
+               sas_device = mpt3sas_get_sdev_by_addr(ioc,
                    le64_to_cpu(sas_device_pg0.SASAddress));
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-               if (sas_device)
+               if (sas_device) {
+                       sas_device_put(sas_device);
                        continue;
+               }
                parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle);
                if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address)) {
                        pr_info(MPT3SAS_FMT "\tBEFORE adding end device: " \
@@ -7063,10 +7496,11 @@ mpt3sas_scsih_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase)
 static void
 _mpt3sas_fw_work(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event)
 {
+       _scsih_fw_event_del_from_list(ioc, fw_event);
+
        /* the queue is being flushed so ignore this event */
-       if (ioc->remove_host ||
-           ioc->pci_error_recovery) {
-               _scsih_fw_event_free(ioc, fw_event);
+       if (ioc->remove_host || ioc->pci_error_recovery) {
+               fw_event_work_put(fw_event);
                return;
        }
 
@@ -7077,8 +7511,17 @@ _mpt3sas_fw_work(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event)
                        fw_event->event_data);
                break;
        case MPT3SAS_REMOVE_UNRESPONDING_DEVICES:
-               while (scsi_host_in_recovery(ioc->shost) || ioc->shost_recovery)
+               while (scsi_host_in_recovery(ioc->shost) ||
+                               ioc->shost_recovery) {
+                       /*
+                        * If we're unloading, bail. Otherwise, this can become
+                        * an infinite loop.
+                        */
+                       if (ioc->remove_host)
+                               goto out;
+
                        ssleep(1);
+               }
                _scsih_remove_unresponding_sas_devices(ioc);
                _scsih_scan_for_devices_after_reset(ioc);
                break;
@@ -7123,7 +7566,8 @@ _mpt3sas_fw_work(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event)
                _scsih_sas_ir_operation_status_event(ioc, fw_event);
                break;
        }
-       _scsih_fw_event_free(ioc, fw_event);
+out:
+       fw_event_work_put(fw_event);
 }
 
 /**
@@ -7239,7 +7683,7 @@ mpt3sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index,
        }
 
        sz = le16_to_cpu(mpi_reply->EventDataLength) * 4;
-       fw_event = kzalloc(sizeof(*fw_event) + sz, GFP_ATOMIC);
+       fw_event = alloc_fw_event_work(sz);
        if (!fw_event) {
                pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
                    ioc->name, __FILE__, __LINE__, __func__);
@@ -7252,6 +7696,7 @@ mpt3sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index,
        fw_event->VP_ID = mpi_reply->VP_ID;
        fw_event->event = event;
        _scsih_fw_event_add(ioc, fw_event);
+       fw_event_work_put(fw_event);
        return 1;
 }
 
@@ -7598,6 +8043,48 @@ _scsih_probe_raid(struct MPT3SAS_ADAPTER *ioc)
        }
 }
 
+static struct _sas_device *get_next_sas_device(struct MPT3SAS_ADAPTER *ioc)
+{
+       struct _sas_device *sas_device = NULL;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ioc->sas_device_lock, flags);
+       if (!list_empty(&ioc->sas_device_init_list)) {
+               sas_device = list_first_entry(&ioc->sas_device_init_list,
+                               struct _sas_device, list);
+               sas_device_get(sas_device);
+       }
+       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+       return sas_device;
+}
+
+static void sas_device_make_active(struct MPT3SAS_ADAPTER *ioc,
+               struct _sas_device *sas_device)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&ioc->sas_device_lock, flags);
+
+       /*
+        * Since we dropped the lock during the call to port_add(), we need to
+        * be careful here that somebody else didn't move or delete this item
+        * while we were busy with other things.
+        *
+        * If it was on the list, we need a put() for the reference the list
+        * had. Either way, we need a get() for the destination list.
+        */
+       if (!list_empty(&sas_device->list)) {
+               list_del_init(&sas_device->list);
+               sas_device_put(sas_device);
+       }
+
+       sas_device_get(sas_device);
+       list_add_tail(&sas_device->list, &ioc->sas_device_list);
+
+       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+}
+
 /**
  * _scsih_probe_sas - reporting sas devices to sas transport
  * @ioc: per adapter object
@@ -7607,17 +8094,13 @@ _scsih_probe_raid(struct MPT3SAS_ADAPTER *ioc)
 static void
 _scsih_probe_sas(struct MPT3SAS_ADAPTER *ioc)
 {
-       struct _sas_device *sas_device, *next;
-       unsigned long flags;
-
-       /* SAS Device List */
-       list_for_each_entry_safe(sas_device, next, &ioc->sas_device_init_list,
-           list) {
+       struct _sas_device *sas_device;
 
+       while ((sas_device = get_next_sas_device(ioc))) {
                if (!mpt3sas_transport_port_add(ioc, sas_device->handle,
-                   sas_device->sas_address_parent)) {
-                       list_del(&sas_device->list);
-                       kfree(sas_device);
+                               sas_device->sas_address_parent)) {
+                       _scsih_sas_device_remove(ioc, sas_device);
+                       sas_device_put(sas_device);
                        continue;
                } else if (!sas_device->starget) {
                        /*
@@ -7628,17 +8111,16 @@ _scsih_probe_sas(struct MPT3SAS_ADAPTER *ioc)
                         */
                        if (!ioc->is_driver_loading) {
                                mpt3sas_transport_port_remove(ioc,
-                                   sas_device->sas_address,
-                                   sas_device->sas_address_parent);
-                               list_del(&sas_device->list);
-                               kfree(sas_device);
+                                               sas_device->sas_address,
+                                               sas_device->sas_address_parent);
+                               _scsih_sas_device_remove(ioc, sas_device);
+                               sas_device_put(sas_device);
                                continue;
                        }
                }
 
-               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);
+               sas_device_make_active(ioc, sas_device);
+               sas_device_put(sas_device);
        }
 }
 
@@ -7854,8 +8336,8 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        /* event thread */
        snprintf(ioc->firmware_event_name, sizeof(ioc->firmware_event_name),
            "fw_event%d", ioc->id);
-       ioc->firmware_event_thread = create_singlethread_workqueue(
-           ioc->firmware_event_name);
+       ioc->firmware_event_thread = alloc_ordered_workqueue(
+           ioc->firmware_event_name, WQ_MEM_RECLAIM);
        if (!ioc->firmware_event_thread) {
                pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
                    ioc->name, __FILE__, __LINE__, __func__);