hpsa: fix hpsa_adjust_hpsa_scsi_table
[cascardo/linux.git] / drivers / scsi / hpsa.c
index 40669f8..382cb5d 100644 (file)
@@ -230,6 +230,7 @@ static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
        int cmd_type);
 static void hpsa_free_cmd_pool(struct ctlr_info *h);
 #define VPD_PAGE (1 << 8)
+#define HPSA_SIMPLE_ERROR_BITS 0x03
 
 static int hpsa_scsi_queue_command(struct Scsi_Host *h, struct scsi_cmnd *cmd);
 static void hpsa_scan_start(struct Scsi_Host *);
@@ -243,7 +244,7 @@ static int hpsa_slave_alloc(struct scsi_device *sdev);
 static int hpsa_slave_configure(struct scsi_device *sdev);
 static void hpsa_slave_destroy(struct scsi_device *sdev);
 
-static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno);
+static void hpsa_update_scsi_devices(struct ctlr_info *h);
 static int check_for_unit_attention(struct ctlr_info *h,
        struct CommandList *c);
 static void check_ioctl_unit_attention(struct ctlr_info *h,
@@ -1134,9 +1135,12 @@ static int hpsa_find_target_lun(struct ctlr_info *h,
        return !found;
 }
 
-static inline void hpsa_show_dev_msg(const char *level, struct ctlr_info *h,
+static void hpsa_show_dev_msg(const char *level, struct ctlr_info *h,
        struct hpsa_scsi_dev_t *dev, char *description)
 {
+       if (h == NULL || h->pdev == NULL || h->scsi_host == NULL)
+               return;
+
        dev_printk(level, &h->pdev->dev,
                        "scsi %d:%d:%d:%d: %s %s %.8s %.16s RAID-%s SSDSmartPathCap%c En%c Exp=%d\n",
                        h->scsi_host->host_no, dev->bus, dev->target, dev->lun,
@@ -1152,7 +1156,7 @@ static inline void hpsa_show_dev_msg(const char *level, struct ctlr_info *h,
 }
 
 /* Add an entry into h->dev[] array. */
-static int hpsa_scsi_add_entry(struct ctlr_info *h, int hostno,
+static int hpsa_scsi_add_entry(struct ctlr_info *h,
                struct hpsa_scsi_dev_t *device,
                struct hpsa_scsi_dev_t *added[], int *nadded)
 {
@@ -1228,7 +1232,7 @@ lun_assigned:
 }
 
 /* Update an entry in h->dev[] array. */
-static void hpsa_scsi_update_entry(struct ctlr_info *h, int hostno,
+static void hpsa_scsi_update_entry(struct ctlr_info *h,
        int entry, struct hpsa_scsi_dev_t *new_entry)
 {
        int offload_enabled;
@@ -1276,7 +1280,7 @@ static void hpsa_scsi_update_entry(struct ctlr_info *h, int hostno,
 }
 
 /* Replace an entry from h->dev[] array. */
-static void hpsa_scsi_replace_entry(struct ctlr_info *h, int hostno,
+static void hpsa_scsi_replace_entry(struct ctlr_info *h,
        int entry, struct hpsa_scsi_dev_t *new_entry,
        struct hpsa_scsi_dev_t *added[], int *nadded,
        struct hpsa_scsi_dev_t *removed[], int *nremoved)
@@ -1304,7 +1308,7 @@ static void hpsa_scsi_replace_entry(struct ctlr_info *h, int hostno,
 }
 
 /* Remove an entry from h->dev[] array. */
-static void hpsa_scsi_remove_entry(struct ctlr_info *h, int hostno, int entry,
+static void hpsa_scsi_remove_entry(struct ctlr_info *h, int entry,
        struct hpsa_scsi_dev_t *removed[], int *nremoved)
 {
        /* assumes h->devlock is held */
@@ -1415,6 +1419,9 @@ static int hpsa_scsi_find_entry(struct hpsa_scsi_dev_t *needle,
 #define DEVICE_CHANGED 1
 #define DEVICE_SAME 2
 #define DEVICE_UPDATED 3
+       if (needle == NULL)
+               return DEVICE_NOT_FOUND;
+
        for (i = 0; i < haystack_size; i++) {
                if (haystack[i] == NULL) /* previously removed. */
                        continue;
@@ -1577,6 +1584,8 @@ static void hpsa_figure_phys_disk_ptrs(struct ctlr_info *h,
                if (!logical_drive->offload_config)
                        continue;
                for (j = 0; j < ndevices; j++) {
+                       if (dev[j] == NULL)
+                               continue;
                        if (dev[j]->devtype != TYPE_DISK)
                                continue;
                        if (is_logical_dev_addr_mode(dev[j]->scsi3addr))
@@ -1620,6 +1629,8 @@ static void hpsa_update_log_drive_phys_drive_ptrs(struct ctlr_info *h,
        int i;
 
        for (i = 0; i < ndevices; i++) {
+               if (dev[i] == NULL)
+                       continue;
                if (dev[i]->devtype != TYPE_DISK)
                        continue;
                if (!is_logical_dev_addr_mode(dev[i]->scsi3addr))
@@ -1638,7 +1649,7 @@ static void hpsa_update_log_drive_phys_drive_ptrs(struct ctlr_info *h,
        }
 }
 
-static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
+static void adjust_hpsa_scsi_table(struct ctlr_info *h,
        struct hpsa_scsi_dev_t *sd[], int nsds)
 {
        /* sd contains scsi3 addresses and devtypes, and inquiry
@@ -1652,6 +1663,15 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
        int nadded, nremoved;
        struct Scsi_Host *sh = NULL;
 
+       /*
+        * A reset can cause a device status to change
+        * re-schedule the scan to see what happened.
+        */
+       if (h->reset_in_progress) {
+               h->drv_req_rescan = 1;
+               return;
+       }
+
        added = kzalloc(sizeof(*added) * HPSA_MAX_DEVICES, GFP_KERNEL);
        removed = kzalloc(sizeof(*removed) * HPSA_MAX_DEVICES, GFP_KERNEL);
 
@@ -1678,19 +1698,18 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
                device_change = hpsa_scsi_find_entry(csd, sd, nsds, &entry);
                if (device_change == DEVICE_NOT_FOUND) {
                        changes++;
-                       hpsa_scsi_remove_entry(h, hostno, i,
-                               removed, &nremoved);
+                       hpsa_scsi_remove_entry(h, i, removed, &nremoved);
                        continue; /* remove ^^^, hence i not incremented */
                } else if (device_change == DEVICE_CHANGED) {
                        changes++;
-                       hpsa_scsi_replace_entry(h, hostno, i, sd[entry],
+                       hpsa_scsi_replace_entry(h, i, sd[entry],
                                added, &nadded, removed, &nremoved);
                        /* Set it to NULL to prevent it from being freed
                         * at the bottom of hpsa_update_scsi_devices()
                         */
                        sd[entry] = NULL;
                } else if (device_change == DEVICE_UPDATED) {
-                       hpsa_scsi_update_entry(h, hostno, i, sd[entry]);
+                       hpsa_scsi_update_entry(h, i, sd[entry]);
                }
                i++;
        }
@@ -1718,8 +1737,7 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
                                        h->ndevices, &entry);
                if (device_change == DEVICE_NOT_FOUND) {
                        changes++;
-                       if (hpsa_scsi_add_entry(h, hostno, sd[i],
-                               added, &nadded) != 0)
+                       if (hpsa_scsi_add_entry(h, sd[i], added, &nadded) != 0)
                                break;
                        sd[i] = NULL; /* prevent from being freed later. */
                } else if (device_change == DEVICE_CHANGED) {
@@ -1735,8 +1753,11 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
        /* Now that h->dev[]->phys_disk[] is coherent, we can enable
         * any logical drives that need it enabled.
         */
-       for (i = 0; i < h->ndevices; i++)
+       for (i = 0; i < h->ndevices; i++) {
+               if (h->dev[i] == NULL)
+                       continue;
                h->dev[i]->offload_enabled = h->dev[i]->offload_to_be_enabled;
+       }
 
        spin_unlock_irqrestore(&h->devlock, flags);
 
@@ -1755,12 +1776,18 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
         * (or if there are no changes) scsi_scan_host will do it later the
         * first time through.
         */
-       if (hostno == -1 || !changes)
+       if (!changes)
                goto free_and_out;
 
        sh = h->scsi_host;
+       if (sh == NULL) {
+               dev_warn(&h->pdev->dev, "%s: scsi_host is null\n", __func__);
+               goto free_and_out;
+       }
        /* Notify scsi mid layer of any removed devices */
        for (i = 0; i < nremoved; i++) {
+               if (removed[i] == NULL)
+                       continue;
                if (removed[i]->expose_state & HPSA_SCSI_ADD) {
                        struct scsi_device *sdev =
                                scsi_device_lookup(sh, removed[i]->bus,
@@ -1784,18 +1811,19 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
 
        /* Notify scsi mid layer of any added devices */
        for (i = 0; i < nadded; i++) {
+               if (added[i] == NULL)
+                       continue;
                if (!(added[i]->expose_state & HPSA_SCSI_ADD))
                        continue;
                if (scsi_add_device(sh, added[i]->bus,
                        added[i]->target, added[i]->lun) == 0)
                        continue;
-               hpsa_show_dev_msg(KERN_WARNING, h, added[i],
-                                       "addition failed, device not added.");
+               dev_warn(&h->pdev->dev, "addition failed, device not added.");
                /* now we have to remove it from h->dev,
                 * since it didn't get added to scsi mid layer
                 */
                fixup_botched_add(h, added[i]);
-               added[i] = NULL;
+               h->drv_req_rescan = 1;
        }
 
 free_and_out:
@@ -3351,10 +3379,13 @@ static int hpsa_update_device_info(struct ctlr_info *h,
 
        unsigned char *inq_buff;
        unsigned char *obdr_sig;
+       int rc = 0;
 
        inq_buff = kzalloc(OBDR_TAPE_INQ_SIZE, GFP_KERNEL);
-       if (!inq_buff)
+       if (!inq_buff) {
+               rc = -ENOMEM;
                goto bail_out;
+       }
 
        /* Do an inquiry to the device to see what it is. */
        if (hpsa_scsi_do_inquiry(h, scsi3addr, 0, inq_buff,
@@ -3362,6 +3393,7 @@ static int hpsa_update_device_info(struct ctlr_info *h,
                /* Inquiry failed (msg printed already) */
                dev_err(&h->pdev->dev,
                        "hpsa_update_device_info: inquiry failed\n");
+               rc = -EIO;
                goto bail_out;
        }
 
@@ -3411,7 +3443,7 @@ static int hpsa_update_device_info(struct ctlr_info *h,
 
 bail_out:
        kfree(inq_buff);
-       return 1;
+       return rc;
 }
 
 static void hpsa_update_device_supports_aborts(struct ctlr_info *h,
@@ -3673,8 +3705,6 @@ static void hpsa_get_ioaccel_drive_info(struct ctlr_info *h,
                                DRIVE_CMDS_RESERVED_FOR_FW;
        else
                dev->queue_depth = DRIVE_QUEUE_DEPTH; /* conservative */
-       atomic_set(&dev->ioaccel_cmds_out, 0);
-       atomic_set(&dev->reset_cmds_out, 0);
 }
 
 static void hpsa_get_path_info(struct hpsa_scsi_dev_t *this_device,
@@ -3702,7 +3732,7 @@ static void hpsa_get_path_info(struct hpsa_scsi_dev_t *this_device,
                sizeof(this_device->bay));
 }
 
-static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
+static void hpsa_update_scsi_devices(struct ctlr_info *h)
 {
        /* the idea here is we could get notified
         * that some devices have changed, so we do a report
@@ -3739,9 +3769,13 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
        }
        memset(lunzerobits, 0, sizeof(lunzerobits));
 
+       h->drv_req_rescan = 0; /* cancel scheduled rescan - we're doing it. */
+
        if (hpsa_gather_lun_info(h, physdev_list, &nphysicals,
-                       logdev_list, &nlogicals))
+                       logdev_list, &nlogicals)) {
+               h->drv_req_rescan = 1;
                goto out;
+       }
 
        /* We might see up to the maximum number of logical and physical disks
         * plus external target devices, and a device for the local RAID
@@ -3762,6 +3796,7 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
                if (!currentsd[i]) {
                        dev_warn(&h->pdev->dev, "out of memory at %s:%d\n",
                                __FILE__, __LINE__);
+                       h->drv_req_rescan = 1;
                        goto out;
                }
                ndev_allocated++;
@@ -3776,6 +3811,7 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
        n_ext_target_devs = 0;
        for (i = 0; i < nphysicals + nlogicals + 1; i++) {
                u8 *lunaddrbytes, is_OBDR = 0;
+               int rc = 0;
 
                /* Figure out where the LUN ID info is coming from */
                lunaddrbytes = figure_lunaddrbytes(h, raid_ctlr_position,
@@ -3788,9 +3824,20 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
                                continue;
 
                /* Get device type, vendor, model, device id */
-               if (hpsa_update_device_info(h, lunaddrbytes, tmpdevice,
-                                                       &is_OBDR))
-                       continue; /* skip it if we can't talk to it. */
+               rc = hpsa_update_device_info(h, lunaddrbytes, tmpdevice,
+                                                       &is_OBDR);
+               if (rc == -ENOMEM) {
+                       dev_warn(&h->pdev->dev,
+                               "Out of memory, rescan deferred.\n");
+                       h->drv_req_rescan = 1;
+                       goto out;
+               }
+               if (rc) {
+                       dev_warn(&h->pdev->dev,
+                               "Inquiry failed, skipping device.\n");
+                       continue;
+               }
+
                figure_bus_target_lun(h, lunaddrbytes, tmpdevice);
                hpsa_update_device_supports_aborts(h, tmpdevice, lunaddrbytes);
                this_device = currentsd[ncurrent];
@@ -3865,7 +3912,7 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
                if (ncurrent >= HPSA_MAX_DEVICES)
                        break;
        }
-       adjust_hpsa_scsi_table(h, hostno, currentsd, ncurrent);
+       adjust_hpsa_scsi_table(h, currentsd, ncurrent);
 out:
        kfree(tmpdevice);
        for (i = 0; i < ndev_allocated; i++)
@@ -3978,19 +4025,14 @@ static int fixup_ioaccel_cdb(u8 *cdb, int *cdb_len)
        case READ_6:
        case READ_12:
                if (*cdb_len == 6) {
-                       block = (((u32) cdb[2]) << 8) | cdb[3];
+                       block = get_unaligned_be16(&cdb[2]);
                        block_cnt = cdb[4];
+                       if (block_cnt == 0)
+                               block_cnt = 256;
                } else {
                        BUG_ON(*cdb_len != 12);
-                       block = (((u32) cdb[2]) << 24) |
-                               (((u32) cdb[3]) << 16) |
-                               (((u32) cdb[4]) << 8) |
-                               cdb[5];
-                       block_cnt =
-                               (((u32) cdb[6]) << 24) |
-                               (((u32) cdb[7]) << 16) |
-                               (((u32) cdb[8]) << 8) |
-                               cdb[9];
+                       block = get_unaligned_be32(&cdb[2]);
+                       block_cnt = get_unaligned_be32(&cdb[6]);
                }
                if (block_cnt > 0xffff)
                        return IO_ACCEL_INELIGIBLE;
@@ -4376,9 +4418,7 @@ static int hpsa_scsi_ioaccel_raid_map(struct ctlr_info *h,
        case WRITE_6:
                is_write = 1;
        case READ_6:
-               first_block =
-                       (((u64) cmd->cmnd[2]) << 8) |
-                       cmd->cmnd[3];
+               first_block = get_unaligned_be16(&cmd->cmnd[2]);
                block_cnt = cmd->cmnd[4];
                if (block_cnt == 0)
                        block_cnt = 256;
@@ -4947,7 +4987,7 @@ static void hpsa_scan_start(struct Scsi_Host *sh)
        if (unlikely(lockup_detected(h)))
                return hpsa_scan_complete(h);
 
-       hpsa_update_scsi_devices(h, h->scsi_host->host_no);
+       hpsa_update_scsi_devices(h);
 
        hpsa_scan_complete(h);
 }
@@ -5207,12 +5247,15 @@ static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd)
 
        hpsa_show_dev_msg(KERN_WARNING, h, dev, "resetting");
 
+       h->reset_in_progress = 1;
+
        /* send a reset to the SCSI LUN which the command was sent to */
        rc = hpsa_do_reset(h, dev, dev->scsi3addr, HPSA_RESET_TYPE_LUN,
                           DEFAULT_REPLY_QUEUE);
        snprintf(msg, sizeof(msg), "reset %s",
                 rc == 0 ? "completed successfully" : "failed");
        hpsa_show_dev_msg(KERN_WARNING, h, dev, msg);
+       h->reset_in_progress = 0;
        return rc == 0 ? SUCCESS : FAILED;
 }
 
@@ -6440,16 +6483,6 @@ static inline void finish_cmd(struct CommandList *c)
                complete(c->waiting);
 }
 
-
-static inline u32 hpsa_tag_discard_error_bits(struct ctlr_info *h, u32 tag)
-{
-#define HPSA_PERF_ERROR_BITS ((1 << DIRECT_LOOKUP_SHIFT) - 1)
-#define HPSA_SIMPLE_ERROR_BITS 0x03
-       if (unlikely(!(h->transMethod & CFGTBL_Trans_Performant)))
-               return tag & ~HPSA_SIMPLE_ERROR_BITS;
-       return tag & ~HPSA_PERF_ERROR_BITS;
-}
-
 /* process completion of an indexed ("direct lookup") command */
 static inline void process_indexed_cmd(struct ctlr_info *h,
        u32 raw_tag)
@@ -7860,6 +7893,11 @@ static void hpsa_ack_ctlr_events(struct ctlr_info *h)
  */
 static int hpsa_ctlr_needs_rescan(struct ctlr_info *h)
 {
+       if (h->drv_req_rescan) {
+               h->drv_req_rescan = 0;
+               return 1;
+       }
+
        if (!(h->fw_support & MISC_FW_EVENT_NOTIFY))
                return 0;