hpsa: correct skipping masked peripherals
authorDon Brace <don.brace@microsemi.com>
Fri, 1 Jul 2016 18:37:31 +0000 (13:37 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 28 Oct 2016 07:01:33 +0000 (03:01 -0400)
commit 64ce60cab24603ac0fcd59c9fbc3be78f4c4d229 upstream.

The SA controller spins down RAID drive spares.

A REGNEWD event causes an inquiry to be sent to all physical
drives. This causes the SA controller to spin up the spare.

The controller suspends all I/O to a logical volume until
the spare is spun up. The spin-up can take over 50 seconds.

This can result in one or both of the following:
 - SML sends down aborts and resets to the logical volume
   and can cause the logical volume to be off-lined.
 - a negative impact on the logical volume's I/O performance
   each time a REGNEWD is triggered.

Reviewed-by: Scott Teel <scott.teel@microsemi.com>
Reviewed-by: Kevin Barnett <kevin.barnett@microsemi.com>
Signed-off-by: Don Brace <don.brace@microsemi.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: Juerg Haefliger <juerg.haefliger@hpe.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/scsi/hpsa.c

index a3860367b568aa06adf4290bf84717458c241e77..e9ce74afd13f4672e941d620f16ab602c647d33e 100644 (file)
@@ -3930,6 +3930,70 @@ static int hpsa_set_local_logical_count(struct ctlr_info *h,
        return rc;
 }
 
+static bool hpsa_is_disk_spare(struct ctlr_info *h, u8 *lunaddrbytes)
+{
+       struct bmic_identify_physical_device *id_phys;
+       bool is_spare = false;
+       int rc;
+
+       id_phys = kzalloc(sizeof(*id_phys), GFP_KERNEL);
+       if (!id_phys)
+               return false;
+
+       rc = hpsa_bmic_id_physical_device(h,
+                                       lunaddrbytes,
+                                       GET_BMIC_DRIVE_NUMBER(lunaddrbytes),
+                                       id_phys, sizeof(*id_phys));
+       if (rc == 0)
+               is_spare = (id_phys->more_flags >> 6) & 0x01;
+
+       kfree(id_phys);
+       return is_spare;
+}
+
+#define RPL_DEV_FLAG_NON_DISK                           0x1
+#define RPL_DEV_FLAG_UNCONFIG_DISK_REPORTING_SUPPORTED  0x2
+#define RPL_DEV_FLAG_UNCONFIG_DISK                      0x4
+
+#define BMIC_DEVICE_TYPE_ENCLOSURE  6
+
+static bool hpsa_skip_device(struct ctlr_info *h, u8 *lunaddrbytes,
+                               struct ext_report_lun_entry *rle)
+{
+       u8 device_flags;
+       u8 device_type;
+
+       if (!MASKED_DEVICE(lunaddrbytes))
+               return false;
+
+       device_flags = rle->device_flags;
+       device_type = rle->device_type;
+
+       if (device_flags & RPL_DEV_FLAG_NON_DISK) {
+               if (device_type == BMIC_DEVICE_TYPE_ENCLOSURE)
+                       return false;
+               return true;
+       }
+
+       if (!(device_flags & RPL_DEV_FLAG_UNCONFIG_DISK_REPORTING_SUPPORTED))
+               return false;
+
+       if (device_flags & RPL_DEV_FLAG_UNCONFIG_DISK)
+               return false;
+
+       /*
+        * Spares may be spun down, we do not want to
+        * do an Inquiry to a RAID set spare drive as
+        * that would have them spun up, that is a
+        * performance hit because I/O to the RAID device
+        * stops while the spin up occurs which can take
+        * over 50 seconds.
+        */
+       if (hpsa_is_disk_spare(h, lunaddrbytes))
+               return true;
+
+       return false;
+}
 
 static void hpsa_update_scsi_devices(struct ctlr_info *h)
 {
@@ -4023,6 +4087,7 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h)
                u8 *lunaddrbytes, is_OBDR = 0;
                int rc = 0;
                int phys_dev_index = i - (raid_ctlr_position == 0);
+               bool skip_device = false;
 
                physical_device = i < nphysicals + (raid_ctlr_position == 0);
 
@@ -4030,10 +4095,15 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h)
                lunaddrbytes = figure_lunaddrbytes(h, raid_ctlr_position,
                        i, nphysicals, nlogicals, physdev_list, logdev_list);
 
-               /* skip masked non-disk devices */
-               if (MASKED_DEVICE(lunaddrbytes) && physical_device &&
-                       (physdev_list->LUN[phys_dev_index].device_flags & 0x01))
-                       continue;
+               /*
+                * Skip over some devices such as a spare.
+                */
+               if (!tmpdevice->external && physical_device) {
+                       skip_device = hpsa_skip_device(h, lunaddrbytes,
+                                       &physdev_list->LUN[phys_dev_index]);
+                       if (skip_device)
+                               continue;
+               }
 
                /* Get device type, vendor, model, device id */
                rc = hpsa_update_device_info(h, lunaddrbytes, tmpdevice,