block: Rename blk_queue_max_sectors to blk_queue_max_hw_sectors
[safe/jmp/linux-2.6] / drivers / block / cciss.c
index 4d4d5e0..030e52d 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/pci.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
+#include <linux/smp_lock.h>
 #include <linux/delay.h>
 #include <linux/major.h>
 #include <linux/fs.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 #include <linux/init.h>
+#include <linux/jiffies.h>
 #include <linux/hdreg.h>
 #include <linux/spinlock.h>
 #include <linux/compat.h>
-#include <linux/blktrace_api.h>
+#include <linux/mutex.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
 
@@ -66,6 +68,12 @@ MODULE_SUPPORTED_DEVICE("HP SA5i SA5i+ SA532 SA5300 SA5312 SA641 SA642 SA6400"
 MODULE_VERSION("3.6.20");
 MODULE_LICENSE("GPL");
 
+static int cciss_allow_hpsa;
+module_param(cciss_allow_hpsa, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(cciss_allow_hpsa,
+       "Prevent cciss driver from accessing hardware known to be "
+       " supported by the hpsa driver");
+
 #include "cciss_cmd.h"
 #include "cciss.h"
 #include <linux/cciss_ioctl.h>
@@ -99,8 +107,6 @@ static const struct pci_device_id cciss_pci_device_id[] = {
        {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSE,     0x103C, 0x3249},
        {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSE,     0x103C, 0x324A},
        {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSE,     0x103C, 0x324B},
-       {PCI_VENDOR_ID_HP,     PCI_ANY_ID,      PCI_ANY_ID, PCI_ANY_ID,
-               PCI_CLASS_STORAGE_RAID << 8, 0xffff << 8, 0},
        {0,}
 };
 
@@ -121,8 +127,6 @@ static struct board_type products[] = {
        {0x409D0E11, "Smart Array 6400 EM", &SA5_access},
        {0x40910E11, "Smart Array 6i", &SA5_access},
        {0x3225103C, "Smart Array P600", &SA5_access},
-       {0x3223103C, "Smart Array P800", &SA5_access},
-       {0x3234103C, "Smart Array P400", &SA5_access},
        {0x3235103C, "Smart Array P400i", &SA5_access},
        {0x3211103C, "Smart Array E200i", &SA5_access},
        {0x3212103C, "Smart Array E200", &SA5_access},
@@ -130,6 +134,10 @@ static struct board_type products[] = {
        {0x3214103C, "Smart Array E200i", &SA5_access},
        {0x3215103C, "Smart Array E200i", &SA5_access},
        {0x3237103C, "Smart Array E500", &SA5_access},
+/* controllers below this line are also supported by the hpsa driver. */
+#define HPSA_BOUNDARY 0x3223103C
+       {0x3223103C, "Smart Array P800", &SA5_access},
+       {0x3234103C, "Smart Array P400", &SA5_access},
        {0x323D103C, "Smart Array P700m", &SA5_access},
        {0x3241103C, "Smart Array P212", &SA5_access},
        {0x3243103C, "Smart Array P410", &SA5_access},
@@ -138,7 +146,6 @@ static struct board_type products[] = {
        {0x3249103C, "Smart Array P812", &SA5_access},
        {0x324A103C, "Smart Array P712m", &SA5_access},
        {0x324B103C, "Smart Array P711m", &SA5_access},
-       {0xFFFF103C, "Unknown Smart Array", &SA5_access},
 };
 
 /* How long to wait (in milliseconds) for board to go into simple mode */
@@ -155,6 +162,10 @@ static struct board_type products[] = {
 
 static ctlr_info_t *hba[MAX_CTLR];
 
+static struct task_struct *cciss_scan_thread;
+static DEFINE_MUTEX(scan_mutex);
+static LIST_HEAD(scan_q);
+
 static void do_cciss_request(struct request_queue *q);
 static irqreturn_t do_cciss_intr(int irq, void *dev_id);
 static int cciss_open(struct block_device *bdev, fmode_t mode);
@@ -164,31 +175,36 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
 static int cciss_getgeo(struct block_device *bdev, struct hd_geometry *geo);
 
 static int cciss_revalidate(struct gendisk *disk);
-static int rebuild_lun_table(ctlr_info_t *h, int first_time);
+static int rebuild_lun_table(ctlr_info_t *h, int first_time, int via_ioctl);
 static int deregister_disk(ctlr_info_t *h, int drv_index,
-                          int clear_all);
+                          int clear_all, int via_ioctl);
 
-static void cciss_read_capacity(int ctlr, int logvol, int withirq,
+static void cciss_read_capacity(int ctlr, int logvol,
                        sector_t *total_size, unsigned int *block_size);
-static void cciss_read_capacity_16(int ctlr, int logvol, int withirq,
+static void cciss_read_capacity_16(int ctlr, int logvol,
                        sector_t *total_size, unsigned int *block_size);
 static void cciss_geometry_inquiry(int ctlr, int logvol,
-                       int withirq, sector_t total_size,
+                       sector_t total_size,
                        unsigned int block_size, InquiryData_struct *inq_buff,
                                   drive_info_struct *drv);
 static void __devinit cciss_interrupt_mode(ctlr_info_t *, struct pci_dev *,
                                           __u32);
 static void start_io(ctlr_info_t *h);
-static int sendcmd(__u8 cmd, int ctlr, void *buff, size_t size,
-                  unsigned int use_unit_num, unsigned int log_unit,
-                  __u8 page_code, unsigned char *scsi3addr, int cmd_type);
 static int sendcmd_withirq(__u8 cmd, int ctlr, void *buff, size_t size,
-                          unsigned int use_unit_num, unsigned int log_unit,
-                          __u8 page_code, int cmd_type);
+                       __u8 page_code, unsigned char scsi3addr[],
+                       int cmd_type);
+static int sendcmd_withirq_core(ctlr_info_t *h, CommandList_struct *c,
+       int attempt_retry);
+static int process_sendcmd_error(ctlr_info_t *h, CommandList_struct *c);
 
 static void fail_all_cmds(unsigned long ctlr);
+static int add_to_scan_list(struct ctlr_info *h);
 static int scan_thread(void *data);
 static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c);
+static void cciss_hba_release(struct device *dev);
+static void cciss_device_release(struct device *dev);
+static void cciss_free_gendisk(ctlr_info_t *h, int drv_index);
+static void cciss_free_drive_info(ctlr_info_t *h, int drv_index);
 
 #ifdef CONFIG_PROC_FS
 static void cciss_procinit(int i);
@@ -203,7 +219,7 @@ static int cciss_compat_ioctl(struct block_device *, fmode_t,
                              unsigned, unsigned long);
 #endif
 
-static struct block_device_operations cciss_fops = {
+static const struct block_device_operations cciss_fops = {
        .owner = THIS_MODULE,
        .open = cciss_open,
        .release = cciss_release,
@@ -225,15 +241,28 @@ static inline void addQ(struct hlist_head *list, CommandList_struct *c)
 
 static inline void removeQ(CommandList_struct *c)
 {
-       if (WARN_ON(hlist_unhashed(&c->list)))
+       /*
+        * After kexec/dump some commands might still
+        * be in flight, which the firmware will try
+        * to complete. Resetting the firmware doesn't work
+        * with old fw revisions, so we have to mark
+        * them off as 'stale' to prevent the driver from
+        * falling over.
+        */
+       if (WARN_ON(hlist_unhashed(&c->list))) {
+               c->cmd_type = CMD_MSG_STALE;
                return;
+       }
 
        hlist_del_init(&c->list);
 }
 
 #include "cciss_scsi.c"                /* For SCSI tape support */
 
-#define RAID_UNKNOWN 6
+static const char *raid_label[] = { "0", "4", "1(1+0)", "5", "5+1", "ADG",
+       "UNKNOWN"
+};
+#define RAID_UNKNOWN (sizeof(raid_label) / sizeof(raid_label[0])-1)
 
 #ifdef CONFIG_PROC_FS
 
@@ -243,9 +272,6 @@ static inline void removeQ(CommandList_struct *c)
 #define ENG_GIG 1000000000
 #define ENG_GIG_FACTOR (ENG_GIG/512)
 #define ENGAGE_SCSI    "engage scsi"
-static const char *raid_label[] = { "0", "4", "1(1+0)", "5", "5+1", "ADG",
-       "UNKNOWN"
-};
 
 static struct proc_dir_entry *proc_cciss;
 
@@ -306,11 +332,14 @@ static int cciss_seq_show(struct seq_file *seq, void *v)
        ctlr_info_t *h = seq->private;
        unsigned ctlr = h->ctlr;
        loff_t *pos = v;
-       drive_info_struct *drv = &h->drv[*pos];
+       drive_info_struct *drv = h->drv[*pos];
 
        if (*pos > h->highest_lun)
                return 0;
 
+       if (drv == NULL) /* it's possible for h->drv[] to have holes. */
+               return 0;
+
        if (drv->heads == 0)
                return 0;
 
@@ -319,7 +348,7 @@ static int cciss_seq_show(struct seq_file *seq, void *v)
        vol_sz_frac *= 100;
        sector_div(vol_sz_frac, ENG_GIG_FACTOR);
 
-       if (drv->raid_level > 5)
+       if (drv->raid_level < 0 || drv->raid_level > RAID_UNKNOWN)
                drv->raid_level = RAID_UNKNOWN;
        seq_printf(seq, "cciss/c%dd%d:"
                        "\t%4u.%02uGB\tRAID %s\n",
@@ -351,7 +380,7 @@ static void cciss_seq_stop(struct seq_file *seq, void *v)
        h->busy_configuring = 0;
 }
 
-static struct seq_operations cciss_seq_ops = {
+static const struct seq_operations cciss_seq_ops = {
        .start = cciss_seq_start,
        .show  = cciss_seq_show,
        .next  = cciss_seq_next,
@@ -396,12 +425,9 @@ cciss_proc_write(struct file *file, const char __user *buf,
        if (strncmp(ENGAGE_SCSI, buffer, sizeof ENGAGE_SCSI - 1) == 0) {
                struct seq_file *seq = file->private_data;
                ctlr_info_t *h = seq->private;
-               int rc;
 
-               rc = cciss_engage_scsi(h->ctlr);
-               if (rc != 0)
-                       err = -rc;
-               else
+               err = cciss_engage_scsi(h->ctlr);
+               if (err == 0)
                        err = length;
        } else
 #endif /* CONFIG_CISS_SCSI_TAPE */
@@ -414,7 +440,7 @@ out:
        return err;
 }
 
-static struct file_operations cciss_proc_fops = {
+static const struct file_operations cciss_proc_fops = {
        .owner   = THIS_MODULE,
        .open    = cciss_seq_open,
        .read    = seq_read,
@@ -437,6 +463,331 @@ static void __devinit cciss_procinit(int i)
 }
 #endif                         /* CONFIG_PROC_FS */
 
+#define MAX_PRODUCT_NAME_LEN 19
+
+#define to_hba(n) container_of(n, struct ctlr_info, dev)
+#define to_drv(n) container_of(n, drive_info_struct, dev)
+
+static ssize_t host_store_rescan(struct device *dev,
+                                struct device_attribute *attr,
+                                const char *buf, size_t count)
+{
+       struct ctlr_info *h = to_hba(dev);
+
+       add_to_scan_list(h);
+       wake_up_process(cciss_scan_thread);
+       wait_for_completion_interruptible(&h->scan_wait);
+
+       return count;
+}
+static DEVICE_ATTR(rescan, S_IWUSR, NULL, host_store_rescan);
+
+static ssize_t dev_show_unique_id(struct device *dev,
+                                struct device_attribute *attr,
+                                char *buf)
+{
+       drive_info_struct *drv = to_drv(dev);
+       struct ctlr_info *h = to_hba(drv->dev.parent);
+       __u8 sn[16];
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+       if (h->busy_configuring)
+               ret = -EBUSY;
+       else
+               memcpy(sn, drv->serial_no, sizeof(sn));
+       spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+
+       if (ret)
+               return ret;
+       else
+               return snprintf(buf, 16 * 2 + 2,
+                               "%02X%02X%02X%02X%02X%02X%02X%02X"
+                               "%02X%02X%02X%02X%02X%02X%02X%02X\n",
+                               sn[0], sn[1], sn[2], sn[3],
+                               sn[4], sn[5], sn[6], sn[7],
+                               sn[8], sn[9], sn[10], sn[11],
+                               sn[12], sn[13], sn[14], sn[15]);
+}
+static DEVICE_ATTR(unique_id, S_IRUGO, dev_show_unique_id, NULL);
+
+static ssize_t dev_show_vendor(struct device *dev,
+                              struct device_attribute *attr,
+                              char *buf)
+{
+       drive_info_struct *drv = to_drv(dev);
+       struct ctlr_info *h = to_hba(drv->dev.parent);
+       char vendor[VENDOR_LEN + 1];
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+       if (h->busy_configuring)
+               ret = -EBUSY;
+       else
+               memcpy(vendor, drv->vendor, VENDOR_LEN + 1);
+       spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+
+       if (ret)
+               return ret;
+       else
+               return snprintf(buf, sizeof(vendor) + 1, "%s\n", drv->vendor);
+}
+static DEVICE_ATTR(vendor, S_IRUGO, dev_show_vendor, NULL);
+
+static ssize_t dev_show_model(struct device *dev,
+                             struct device_attribute *attr,
+                             char *buf)
+{
+       drive_info_struct *drv = to_drv(dev);
+       struct ctlr_info *h = to_hba(drv->dev.parent);
+       char model[MODEL_LEN + 1];
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+       if (h->busy_configuring)
+               ret = -EBUSY;
+       else
+               memcpy(model, drv->model, MODEL_LEN + 1);
+       spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+
+       if (ret)
+               return ret;
+       else
+               return snprintf(buf, sizeof(model) + 1, "%s\n", drv->model);
+}
+static DEVICE_ATTR(model, S_IRUGO, dev_show_model, NULL);
+
+static ssize_t dev_show_rev(struct device *dev,
+                           struct device_attribute *attr,
+                           char *buf)
+{
+       drive_info_struct *drv = to_drv(dev);
+       struct ctlr_info *h = to_hba(drv->dev.parent);
+       char rev[REV_LEN + 1];
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+       if (h->busy_configuring)
+               ret = -EBUSY;
+       else
+               memcpy(rev, drv->rev, REV_LEN + 1);
+       spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+
+       if (ret)
+               return ret;
+       else
+               return snprintf(buf, sizeof(rev) + 1, "%s\n", drv->rev);
+}
+static DEVICE_ATTR(rev, S_IRUGO, dev_show_rev, NULL);
+
+static ssize_t cciss_show_lunid(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       drive_info_struct *drv = to_drv(dev);
+       struct ctlr_info *h = to_hba(drv->dev.parent);
+       unsigned long flags;
+       unsigned char lunid[8];
+
+       spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+       if (h->busy_configuring) {
+               spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+               return -EBUSY;
+       }
+       if (!drv->heads) {
+               spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+               return -ENOTTY;
+       }
+       memcpy(lunid, drv->LunID, sizeof(lunid));
+       spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+       return snprintf(buf, 20, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
+               lunid[0], lunid[1], lunid[2], lunid[3],
+               lunid[4], lunid[5], lunid[6], lunid[7]);
+}
+static DEVICE_ATTR(lunid, S_IRUGO, cciss_show_lunid, NULL);
+
+static ssize_t cciss_show_raid_level(struct device *dev,
+                                    struct device_attribute *attr, char *buf)
+{
+       drive_info_struct *drv = to_drv(dev);
+       struct ctlr_info *h = to_hba(drv->dev.parent);
+       int raid;
+       unsigned long flags;
+
+       spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+       if (h->busy_configuring) {
+               spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+               return -EBUSY;
+       }
+       raid = drv->raid_level;
+       spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+       if (raid < 0 || raid > RAID_UNKNOWN)
+               raid = RAID_UNKNOWN;
+
+       return snprintf(buf, strlen(raid_label[raid]) + 7, "RAID %s\n",
+                       raid_label[raid]);
+}
+static DEVICE_ATTR(raid_level, S_IRUGO, cciss_show_raid_level, NULL);
+
+static ssize_t cciss_show_usage_count(struct device *dev,
+                                     struct device_attribute *attr, char *buf)
+{
+       drive_info_struct *drv = to_drv(dev);
+       struct ctlr_info *h = to_hba(drv->dev.parent);
+       unsigned long flags;
+       int count;
+
+       spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+       if (h->busy_configuring) {
+               spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+               return -EBUSY;
+       }
+       count = drv->usage_count;
+       spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+       return snprintf(buf, 20, "%d\n", count);
+}
+static DEVICE_ATTR(usage_count, S_IRUGO, cciss_show_usage_count, NULL);
+
+static struct attribute *cciss_host_attrs[] = {
+       &dev_attr_rescan.attr,
+       NULL
+};
+
+static struct attribute_group cciss_host_attr_group = {
+       .attrs = cciss_host_attrs,
+};
+
+static const struct attribute_group *cciss_host_attr_groups[] = {
+       &cciss_host_attr_group,
+       NULL
+};
+
+static struct device_type cciss_host_type = {
+       .name           = "cciss_host",
+       .groups         = cciss_host_attr_groups,
+       .release        = cciss_hba_release,
+};
+
+static struct attribute *cciss_dev_attrs[] = {
+       &dev_attr_unique_id.attr,
+       &dev_attr_model.attr,
+       &dev_attr_vendor.attr,
+       &dev_attr_rev.attr,
+       &dev_attr_lunid.attr,
+       &dev_attr_raid_level.attr,
+       &dev_attr_usage_count.attr,
+       NULL
+};
+
+static struct attribute_group cciss_dev_attr_group = {
+       .attrs = cciss_dev_attrs,
+};
+
+static const struct attribute_group *cciss_dev_attr_groups[] = {
+       &cciss_dev_attr_group,
+       NULL
+};
+
+static struct device_type cciss_dev_type = {
+       .name           = "cciss_device",
+       .groups         = cciss_dev_attr_groups,
+       .release        = cciss_device_release,
+};
+
+static struct bus_type cciss_bus_type = {
+       .name           = "cciss",
+};
+
+/*
+ * cciss_hba_release is called when the reference count
+ * of h->dev goes to zero.
+ */
+static void cciss_hba_release(struct device *dev)
+{
+       /*
+        * nothing to do, but need this to avoid a warning
+        * about not having a release handler from lib/kref.c.
+        */
+}
+
+/*
+ * Initialize sysfs entry for each controller.  This sets up and registers
+ * the 'cciss#' directory for each individual controller under
+ * /sys/bus/pci/devices/<dev>/.
+ */
+static int cciss_create_hba_sysfs_entry(struct ctlr_info *h)
+{
+       device_initialize(&h->dev);
+       h->dev.type = &cciss_host_type;
+       h->dev.bus = &cciss_bus_type;
+       dev_set_name(&h->dev, "%s", h->devname);
+       h->dev.parent = &h->pdev->dev;
+
+       return device_add(&h->dev);
+}
+
+/*
+ * Remove sysfs entries for an hba.
+ */
+static void cciss_destroy_hba_sysfs_entry(struct ctlr_info *h)
+{
+       device_del(&h->dev);
+       put_device(&h->dev); /* final put. */
+}
+
+/* cciss_device_release is called when the reference count
+ * of h->drv[x]dev goes to zero.
+ */
+static void cciss_device_release(struct device *dev)
+{
+       drive_info_struct *drv = to_drv(dev);
+       kfree(drv);
+}
+
+/*
+ * Initialize sysfs for each logical drive.  This sets up and registers
+ * the 'c#d#' directory for each individual logical drive under
+ * /sys/bus/pci/devices/<dev/ccis#/. We also create a link from
+ * /sys/block/cciss!c#d# to this entry.
+ */
+static long cciss_create_ld_sysfs_entry(struct ctlr_info *h,
+                                      int drv_index)
+{
+       struct device *dev;
+
+       if (h->drv[drv_index]->device_initialized)
+               return 0;
+
+       dev = &h->drv[drv_index]->dev;
+       device_initialize(dev);
+       dev->type = &cciss_dev_type;
+       dev->bus = &cciss_bus_type;
+       dev_set_name(dev, "c%dd%d", h->ctlr, drv_index);
+       dev->parent = &h->dev;
+       h->drv[drv_index]->device_initialized = 1;
+       return device_add(dev);
+}
+
+/*
+ * Remove sysfs entries for a logical drive.
+ */
+static void cciss_destroy_ld_sysfs_entry(struct ctlr_info *h, int drv_index,
+       int ctlr_exiting)
+{
+       struct device *dev = &h->drv[drv_index]->dev;
+
+       /* special case for c*d0, we only destroy it on controller exit */
+       if (drv_index == 0 && !ctlr_exiting)
+               return;
+
+       device_del(dev);
+       put_device(dev); /* the "final" put. */
+       h->drv[drv_index] = NULL;
+}
+
 /*
  * For operations that cannot sleep, a command block is allocated at init,
  * and managed by cmd_alloc() and cmd_free() using a simple bitmap to track
@@ -551,7 +902,7 @@ static int cciss_open(struct block_device *bdev, fmode_t mode)
        printk(KERN_DEBUG "cciss_open %s\n", bdev->bd_disk->disk_name);
 #endif                         /* CCISS_DEBUG */
 
-       if (host->busy_initializing || drv->busy_configuring)
+       if (drv->busy_configuring)
                return -EBUSY;
        /*
         * Root is allowed to open raw volume zero even if it's not configured
@@ -567,7 +918,8 @@ static int cciss_open(struct block_device *bdev, fmode_t mode)
                        if (MINOR(bdev->bd_dev) & 0x0f) {
                                return -ENXIO;
                                /* if it is, make sure we have a LUN ID */
-                       } else if (drv->LunID == 0) {
+                       } else if (memcmp(drv->LunID, CTLR_LUNID,
+                               sizeof(drv->LunID))) {
                                return -ENXIO;
                        }
                }
@@ -932,12 +1284,13 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
        case CCISS_DEREGDISK:
        case CCISS_REGNEWD:
        case CCISS_REVALIDVOLS:
-               return rebuild_lun_table(host, 0);
+               return rebuild_lun_table(host, 0, 1);
 
        case CCISS_GETLUNINFO:{
                        LogvolInfo_struct luninfo;
 
-                       luninfo.LunID = drv->LunID;
+                       memcpy(&luninfo.LunID, drv->LunID,
+                               sizeof(luninfo.LunID));
                        luninfo.num_opens = drv->usage_count;
                        luninfo.num_parts = 0;
                        if (copy_to_user(argp, &luninfo,
@@ -991,26 +1344,27 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
                                kfree(buff);
                                return -ENOMEM;
                        }
-                       // Fill in the command type
+                       /* Fill in the command type */
                        c->cmd_type = CMD_IOCTL_PEND;
-                       // Fill in Command Header
-                       c->Header.ReplyQueue = 0;       // unused in simple mode
-                       if (iocommand.buf_size > 0)     // buffer to fill
+                       /* Fill in Command Header */
+                       c->Header.ReplyQueue = 0;   /* unused in simple mode */
+                       if (iocommand.buf_size > 0) /* buffer to fill */
                        {
                                c->Header.SGList = 1;
                                c->Header.SGTotal = 1;
-                       } else  // no buffers to fill
+                       } else /* no buffers to fill */
                        {
                                c->Header.SGList = 0;
                                c->Header.SGTotal = 0;
                        }
                        c->Header.LUN = iocommand.LUN_info;
-                       c->Header.Tag.lower = c->busaddr;       // use the kernel address the cmd block for tag
+                       /* use the kernel address the cmd block for tag */
+                       c->Header.Tag.lower = c->busaddr;
 
-                       // Fill in Request block
+                       /* Fill in Request block */
                        c->Request = iocommand.Request;
 
-                       // Fill in the scatter gather information
+                       /* Fill in the scatter gather information */
                        if (iocommand.buf_size > 0) {
                                temp64.val = pci_map_single(host->pdev, buff,
                                        iocommand.buf_size,
@@ -1018,7 +1372,7 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
                                c->SG[0].Addr.lower = temp64.val32.lower;
                                c->SG[0].Addr.upper = temp64.val32.upper;
                                c->SG[0].Len = iocommand.buf_size;
-                               c->SG[0].Ext = 0;       // we are not chaining
+                               c->SG[0].Ext = 0;  /* we are not chaining */
                        }
                        c->waiting = &wait;
 
@@ -1275,7 +1629,10 @@ static void cciss_check_queues(ctlr_info_t *h)
                /* make sure the disk has been added and the drive is real
                 * because this can be called from the middle of init_one.
                 */
-               if (!(h->drv[curr_queue].queue) || !(h->drv[curr_queue].heads))
+               if (!h->drv[curr_queue])
+                       continue;
+               if (!(h->drv[curr_queue]->queue) ||
+                       !(h->drv[curr_queue]->heads))
                        continue;
                blk_start_queue(h->gendisk[curr_queue]->queue);
 
@@ -1299,10 +1656,11 @@ static void cciss_softirq_done(struct request *rq)
 {
        CommandList_struct *cmd = rq->completion_data;
        ctlr_info_t *h = hba[cmd->ctlr];
-       unsigned int nr_bytes;
+       SGDescriptor_struct *curr_sg = cmd->SG;
        unsigned long flags;
        u64bit temp64;
        int i, ddir;
+       int sg_index = 0;
 
        if (cmd->Request.Type.Direction == XFER_READ)
                ddir = PCI_DMA_FROMDEVICE;
@@ -1312,24 +1670,33 @@ static void cciss_softirq_done(struct request *rq)
        /* command did not need to be retried */
        /* unmap the DMA mapping for all the scatter gather elements */
        for (i = 0; i < cmd->Header.SGList; i++) {
-               temp64.val32.lower = cmd->SG[i].Addr.lower;
-               temp64.val32.upper = cmd->SG[i].Addr.upper;
-               pci_unmap_page(h->pdev, temp64.val, cmd->SG[i].Len, ddir);
+               if (curr_sg[sg_index].Ext == CCISS_SG_CHAIN) {
+                       temp64.val32.lower = cmd->SG[i].Addr.lower;
+                       temp64.val32.upper = cmd->SG[i].Addr.upper;
+                       pci_dma_sync_single_for_cpu(h->pdev, temp64.val,
+                                               cmd->SG[i].Len, ddir);
+                       pci_unmap_single(h->pdev, temp64.val,
+                                               cmd->SG[i].Len, ddir);
+                       /* Point to the next block */
+                       curr_sg = h->cmd_sg_list[cmd->cmdindex]->sgchain;
+                       sg_index = 0;
+               }
+               temp64.val32.lower = curr_sg[sg_index].Addr.lower;
+               temp64.val32.upper = curr_sg[sg_index].Addr.upper;
+               pci_unmap_page(h->pdev, temp64.val, curr_sg[sg_index].Len,
+                               ddir);
+               ++sg_index;
        }
 
 #ifdef CCISS_DEBUG
        printk("Done with %p\n", rq);
 #endif                         /* CCISS_DEBUG */
 
-       /*
-        * Store the full size and set the residual count for pc requests
-        */
-       nr_bytes = blk_rq_bytes(rq);
+       /* set the residual count for pc requests */
        if (blk_pc_request(rq))
-               rq->data_len = cmd->err_info->ResidualCnt;
+               rq->resid_len = cmd->err_info->ResidualCnt;
 
-       if (blk_end_request(rq, (rq->errors == 0) ? 0 : -EIO, nr_bytes))
-               BUG();
+       blk_end_request_all(rq, (rq->errors == 0) ? 0 : -EIO);
 
        spin_lock_irqsave(&h->lock, flags);
        cmd_free(h, cmd, 1);
@@ -1337,17 +1704,60 @@ static void cciss_softirq_done(struct request *rq)
        spin_unlock_irqrestore(&h->lock, flags);
 }
 
+static inline void log_unit_to_scsi3addr(ctlr_info_t *h,
+       unsigned char scsi3addr[], uint32_t log_unit)
+{
+       memcpy(scsi3addr, h->drv[log_unit]->LunID,
+               sizeof(h->drv[log_unit]->LunID));
+}
+
+/* This function gets the SCSI vendor, model, and revision of a logical drive
+ * via the inquiry page 0.  Model, vendor, and rev are set to empty strings if
+ * they cannot be read.
+ */
+static void cciss_get_device_descr(int ctlr, int logvol,
+                                  char *vendor, char *model, char *rev)
+{
+       int rc;
+       InquiryData_struct *inq_buf;
+       unsigned char scsi3addr[8];
+
+       *vendor = '\0';
+       *model = '\0';
+       *rev = '\0';
+
+       inq_buf = kzalloc(sizeof(InquiryData_struct), GFP_KERNEL);
+       if (!inq_buf)
+               return;
+
+       log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
+       rc = sendcmd_withirq(CISS_INQUIRY, ctlr, inq_buf, sizeof(*inq_buf), 0,
+                       scsi3addr, TYPE_CMD);
+       if (rc == IO_OK) {
+               memcpy(vendor, &inq_buf->data_byte[8], VENDOR_LEN);
+               vendor[VENDOR_LEN] = '\0';
+               memcpy(model, &inq_buf->data_byte[16], MODEL_LEN);
+               model[MODEL_LEN] = '\0';
+               memcpy(rev, &inq_buf->data_byte[32], REV_LEN);
+               rev[REV_LEN] = '\0';
+       }
+
+       kfree(inq_buf);
+       return;
+}
+
 /* This function gets the serial number of a logical drive via
  * inquiry page 0x83.  Serial no. is 16 bytes.  If the serial
  * number cannot be had, for whatever reason, 16 bytes of 0xff
  * are returned instead.
  */
-static void cciss_get_serial_no(int ctlr, int logvol, int withirq,
+static void cciss_get_serial_no(int ctlr, int logvol,
                                unsigned char *serial_no, int buflen)
 {
 #define PAGE_83_INQ_BYTES 64
        int rc;
        unsigned char *buf;
+       unsigned char scsi3addr[8];
 
        if (buflen > 16)
                buflen = 16;
@@ -1356,53 +1766,64 @@ static void cciss_get_serial_no(int ctlr, int logvol, int withirq,
        if (!buf)
                return;
        memset(serial_no, 0, buflen);
-       if (withirq)
-               rc = sendcmd_withirq(CISS_INQUIRY, ctlr, buf,
-                       PAGE_83_INQ_BYTES, 1, logvol, 0x83, TYPE_CMD);
-       else
-               rc = sendcmd(CISS_INQUIRY, ctlr, buf,
-                       PAGE_83_INQ_BYTES, 1, logvol, 0x83, NULL, TYPE_CMD);
+       log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
+       rc = sendcmd_withirq(CISS_INQUIRY, ctlr, buf,
+               PAGE_83_INQ_BYTES, 0x83, scsi3addr, TYPE_CMD);
        if (rc == IO_OK)
                memcpy(serial_no, &buf[8], buflen);
        kfree(buf);
        return;
 }
 
-static void cciss_add_disk(ctlr_info_t *h, struct gendisk *disk,
+/*
+ * cciss_add_disk sets up the block device queue for a logical drive
+ */
+static int cciss_add_disk(ctlr_info_t *h, struct gendisk *disk,
                                int drv_index)
 {
        disk->queue = blk_init_queue(do_cciss_request, &h->lock);
+       if (!disk->queue)
+               goto init_queue_failure;
        sprintf(disk->disk_name, "cciss/c%dd%d", h->ctlr, drv_index);
        disk->major = h->major;
        disk->first_minor = drv_index << NWD_SHIFT;
        disk->fops = &cciss_fops;
-       disk->private_data = &h->drv[drv_index];
-       disk->driverfs_dev = &h->pdev->dev;
+       if (cciss_create_ld_sysfs_entry(h, drv_index))
+               goto cleanup_queue;
+       disk->private_data = h->drv[drv_index];
+       disk->driverfs_dev = &h->drv[drv_index]->dev;
 
        /* Set up queue information */
        blk_queue_bounce_limit(disk->queue, h->pdev->dma_mask);
 
        /* This is a hardware imposed limit. */
-       blk_queue_max_hw_segments(disk->queue, MAXSGENTRIES);
+       blk_queue_max_hw_segments(disk->queue, h->maxsgentries);
 
        /* This is a limit in the driver and could be eliminated. */
-       blk_queue_max_phys_segments(disk->queue, MAXSGENTRIES);
+       blk_queue_max_phys_segments(disk->queue, h->maxsgentries);
 
-       blk_queue_max_sectors(disk->queue, h->cciss_max_sectors);
+       blk_queue_max_hw_sectors(disk->queue, h->cciss_max_sectors);
 
        blk_queue_softirq_done(disk->queue, cciss_softirq_done);
 
        disk->queue->queuedata = h;
 
-       blk_queue_hardsect_size(disk->queue,
-                               h->drv[drv_index].block_size);
+       blk_queue_logical_block_size(disk->queue,
+                                    h->drv[drv_index]->block_size);
 
        /* Make sure all queue data is written out before */
-       /* setting h->drv[drv_index].queue, as setting this */
+       /* setting h->drv[drv_index]->queue, as setting this */
        /* allows the interrupt handler to start the queue */
        wmb();
-       h->drv[drv_index].queue = disk->queue;
+       h->drv[drv_index]->queue = disk->queue;
        add_disk(disk);
+       return 0;
+
+cleanup_queue:
+       blk_cleanup_queue(disk->queue);
+       disk->queue = NULL;
+init_queue_failure:
+       return -1;
 }
 
 /* This function will check the usage_count of the drive to be updated/added.
@@ -1415,7 +1836,8 @@ static void cciss_add_disk(ctlr_info_t *h, struct gendisk *disk,
  * is also the controller node.  Any changes to disk 0 will show up on
  * the next reboot.
  */
-static void cciss_update_drive_info(int ctlr, int drv_index, int first_time)
+static void cciss_update_drive_info(int ctlr, int drv_index, int first_time,
+       int via_ioctl)
 {
        ctlr_info_t *h = hba[ctlr];
        struct gendisk *disk;
@@ -1425,35 +1847,25 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time)
        unsigned long flags = 0;
        int ret = 0;
        drive_info_struct *drvinfo;
-       int was_only_controller_node;
 
        /* Get information about the disk and modify the driver structure */
        inq_buff = kmalloc(sizeof(InquiryData_struct), GFP_KERNEL);
-       drvinfo = kmalloc(sizeof(*drvinfo), GFP_KERNEL);
+       drvinfo = kzalloc(sizeof(*drvinfo), GFP_KERNEL);
        if (inq_buff == NULL || drvinfo == NULL)
                goto mem_msg;
 
-       /* See if we're trying to update the "controller node"
-        * this will happen the when the first logical drive gets
-        * created by ACU.
-        */
-       was_only_controller_node = (drv_index == 0 &&
-                               h->drv[0].raid_level == -1);
-
        /* testing to see if 16-byte CDBs are already being used */
        if (h->cciss_read == CCISS_READ_16) {
-               cciss_read_capacity_16(h->ctlr, drv_index, 1,
+               cciss_read_capacity_16(h->ctlr, drv_index,
                        &total_size, &block_size);
 
        } else {
-               cciss_read_capacity(ctlr, drv_index, 1,
-                                   &total_size, &block_size);
-
+               cciss_read_capacity(ctlr, drv_index, &total_size, &block_size);
                /* if read_capacity returns all F's this volume is >2TB */
                /* in size so we switch to 16-byte CDB's for all */
                /* read/write ops */
                if (total_size == 0xFFFFFFFFULL) {
-                       cciss_read_capacity_16(ctlr, drv_index, 1,
+                       cciss_read_capacity_16(ctlr, drv_index,
                        &total_size, &block_size);
                        h->cciss_read = CCISS_READ_16;
                        h->cciss_write = CCISS_WRITE_16;
@@ -1463,23 +1875,28 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time)
                }
        }
 
-       cciss_geometry_inquiry(ctlr, drv_index, 1, total_size, block_size,
+       cciss_geometry_inquiry(ctlr, drv_index, total_size, block_size,
                               inq_buff, drvinfo);
        drvinfo->block_size = block_size;
        drvinfo->nr_blocks = total_size + 1;
 
-       cciss_get_serial_no(ctlr, drv_index, 1, drvinfo->serial_no,
+       cciss_get_device_descr(ctlr, drv_index, drvinfo->vendor,
+                               drvinfo->model, drvinfo->rev);
+       cciss_get_serial_no(ctlr, drv_index, drvinfo->serial_no,
                        sizeof(drvinfo->serial_no));
+       /* Save the lunid in case we deregister the disk, below. */
+       memcpy(drvinfo->LunID, h->drv[drv_index]->LunID,
+               sizeof(drvinfo->LunID));
 
        /* Is it the same disk we already know, and nothing's changed? */
-       if (h->drv[drv_index].raid_level != -1 &&
+       if (h->drv[drv_index]->raid_level != -1 &&
                ((memcmp(drvinfo->serial_no,
-                               h->drv[drv_index].serial_no, 16) == 0) &&
-               drvinfo->block_size == h->drv[drv_index].block_size &&
-               drvinfo->nr_blocks == h->drv[drv_index].nr_blocks &&
-               drvinfo->heads == h->drv[drv_index].heads &&
-               drvinfo->sectors == h->drv[drv_index].sectors &&
-               drvinfo->cylinders == h->drv[drv_index].cylinders))
+                               h->drv[drv_index]->serial_no, 16) == 0) &&
+               drvinfo->block_size == h->drv[drv_index]->block_size &&
+               drvinfo->nr_blocks == h->drv[drv_index]->nr_blocks &&
+               drvinfo->heads == h->drv[drv_index]->heads &&
+               drvinfo->sectors == h->drv[drv_index]->sectors &&
+               drvinfo->cylinders == h->drv[drv_index]->cylinders))
                        /* The disk is unchanged, nothing to update */
                        goto freeret;
 
@@ -1489,18 +1906,17 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time)
         * If the disk already exists then deregister it before proceeding
         * (unless it's the first disk (for the controller node).
         */
-       if (h->drv[drv_index].raid_level != -1 && drv_index != 0) {
+       if (h->drv[drv_index]->raid_level != -1 && drv_index != 0) {
                printk(KERN_WARNING "disk %d has changed.\n", drv_index);
                spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
-               h->drv[drv_index].busy_configuring = 1;
+               h->drv[drv_index]->busy_configuring = 1;
                spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
 
-               /* deregister_disk sets h->drv[drv_index].queue = NULL
+               /* deregister_disk sets h->drv[drv_index]->queue = NULL
                 * which keeps the interrupt handler from starting
                 * the queue.
                 */
-               ret = deregister_disk(h, drv_index, 0);
-               h->drv[drv_index].busy_configuring = 0;
+               ret = deregister_disk(h, drv_index, 0, via_ioctl);
        }
 
        /* If the disk is in use return */
@@ -1508,19 +1924,31 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time)
                goto freeret;
 
        /* Save the new information from cciss_geometry_inquiry
-        * and serial number inquiry.
+        * and serial number inquiry.  If the disk was deregistered
+        * above, then h->drv[drv_index] will be NULL.
         */
-       h->drv[drv_index].block_size = drvinfo->block_size;
-       h->drv[drv_index].nr_blocks = drvinfo->nr_blocks;
-       h->drv[drv_index].heads = drvinfo->heads;
-       h->drv[drv_index].sectors = drvinfo->sectors;
-       h->drv[drv_index].cylinders = drvinfo->cylinders;
-       h->drv[drv_index].raid_level = drvinfo->raid_level;
-       memcpy(h->drv[drv_index].serial_no, drvinfo->serial_no, 16);
+       if (h->drv[drv_index] == NULL) {
+               drvinfo->device_initialized = 0;
+               h->drv[drv_index] = drvinfo;
+               drvinfo = NULL; /* so it won't be freed below. */
+       } else {
+               /* special case for cxd0 */
+               h->drv[drv_index]->block_size = drvinfo->block_size;
+               h->drv[drv_index]->nr_blocks = drvinfo->nr_blocks;
+               h->drv[drv_index]->heads = drvinfo->heads;
+               h->drv[drv_index]->sectors = drvinfo->sectors;
+               h->drv[drv_index]->cylinders = drvinfo->cylinders;
+               h->drv[drv_index]->raid_level = drvinfo->raid_level;
+               memcpy(h->drv[drv_index]->serial_no, drvinfo->serial_no, 16);
+               memcpy(h->drv[drv_index]->vendor, drvinfo->vendor,
+                       VENDOR_LEN + 1);
+               memcpy(h->drv[drv_index]->model, drvinfo->model, MODEL_LEN + 1);
+               memcpy(h->drv[drv_index]->rev, drvinfo->rev, REV_LEN + 1);
+       }
 
        ++h->num_luns;
        disk = h->gendisk[drv_index];
-       set_capacity(disk, h->drv[drv_index].nr_blocks);
+       set_capacity(disk, h->drv[drv_index]->nr_blocks);
 
        /* If it's not disk 0 (drv_index != 0)
         * or if it was disk 0, but there was previously
@@ -1528,8 +1956,15 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time)
         * (raid_leve == -1) then we want to update the
         * logical drive's information.
         */
-       if (drv_index || first_time)
-               cciss_add_disk(h, disk, drv_index);
+       if (drv_index || first_time) {
+               if (cciss_add_disk(h, disk, drv_index) != 0) {
+                       cciss_free_gendisk(h, drv_index);
+                       cciss_free_drive_info(h, drv_index);
+                       printk(KERN_WARNING "cciss:%d could not update "
+                               "disk %d\n", h->ctlr, drv_index);
+                       --h->num_luns;
+               }
+       }
 
 freeret:
        kfree(inq_buff);
@@ -1541,28 +1976,70 @@ mem_msg:
 }
 
 /* This function will find the first index of the controllers drive array
- * that has a -1 for the raid_level and will return that index.  This is
- * where new drives will be added.  If the index to be returned is greater
- * than the highest_lun index for the controller then highest_lun is set
- * to this new index.  If there are no available indexes then -1 is returned.
- * "controller_node" is used to know if this is a real logical drive, or just
- * the controller node, which determines if this counts towards highest_lun.
+ * that has a null drv pointer and allocate the drive info struct and
+ * will return that index   This is where new drives will be added.
+ * If the index to be returned is greater than the highest_lun index for
+ * the controller then highest_lun is set * to this new index.
+ * If there are no available indexes or if tha allocation fails, then -1
+ * is returned.  * "controller_node" is used to know if this is a real
+ * logical drive, or just the controller node, which determines if this
+ * counts towards highest_lun.
  */
-static int cciss_find_free_drive_index(int ctlr, int controller_node)
+static int cciss_alloc_drive_info(ctlr_info_t *h, int controller_node)
 {
        int i;
+       drive_info_struct *drv;
 
+       /* Search for an empty slot for our drive info */
        for (i = 0; i < CISS_MAX_LUN; i++) {
-               if (hba[ctlr]->drv[i].raid_level == -1) {
-                       if (i > hba[ctlr]->highest_lun)
-                               if (!controller_node)
-                                       hba[ctlr]->highest_lun = i;
+
+               /* if not cxd0 case, and it's occupied, skip it. */
+               if (h->drv[i] && i != 0)
+                       continue;
+               /*
+                * If it's cxd0 case, and drv is alloc'ed already, and a
+                * disk is configured there, skip it.
+                */
+               if (i == 0 && h->drv[i] && h->drv[i]->raid_level != -1)
+                       continue;
+
+               /*
+                * We've found an empty slot.  Update highest_lun
+                * provided this isn't just the fake cxd0 controller node.
+                */
+               if (i > h->highest_lun && !controller_node)
+                       h->highest_lun = i;
+
+               /* If adding a real disk at cxd0, and it's already alloc'ed */
+               if (i == 0 && h->drv[i] != NULL)
                        return i;
-               }
+
+               /*
+                * Found an empty slot, not already alloc'ed.  Allocate it.
+                * Mark it with raid_level == -1, so we know it's new later on.
+                */
+               drv = kzalloc(sizeof(*drv), GFP_KERNEL);
+               if (!drv)
+                       return -1;
+               drv->raid_level = -1; /* so we know it's new */
+               h->drv[i] = drv;
+               return i;
        }
        return -1;
 }
 
+static void cciss_free_drive_info(ctlr_info_t *h, int drv_index)
+{
+       kfree(h->drv[drv_index]);
+       h->drv[drv_index] = NULL;
+}
+
+static void cciss_free_gendisk(ctlr_info_t *h, int drv_index)
+{
+       put_disk(h->gendisk[drv_index]);
+       h->gendisk[drv_index] = NULL;
+}
+
 /* cciss_add_gendisk finds a free hba[]->drv structure
  * and allocates a gendisk if needed, and sets the lunid
  * in the drvinfo structure.   It returns the index into
@@ -1572,13 +2049,15 @@ static int cciss_find_free_drive_index(int ctlr, int controller_node)
  * a means to talk to the controller in case no logical
  * drives have yet been configured.
  */
-static int cciss_add_gendisk(ctlr_info_t *h, __u32 lunid, int controller_node)
+static int cciss_add_gendisk(ctlr_info_t *h, unsigned char lunid[],
+       int controller_node)
 {
        int drv_index;
 
-       drv_index = cciss_find_free_drive_index(h->ctlr, controller_node);
+       drv_index = cciss_alloc_drive_info(h, controller_node);
        if (drv_index == -1)
                return -1;
+
        /*Check if the gendisk needs to be allocated */
        if (!h->gendisk[drv_index]) {
                h->gendisk[drv_index] =
@@ -1587,17 +2066,25 @@ static int cciss_add_gendisk(ctlr_info_t *h, __u32 lunid, int controller_node)
                        printk(KERN_ERR "cciss%d: could not "
                                "allocate a new disk %d\n",
                                h->ctlr, drv_index);
-                       return -1;
+                       goto err_free_drive_info;
                }
        }
-       h->drv[drv_index].LunID = lunid;
-
+       memcpy(h->drv[drv_index]->LunID, lunid,
+               sizeof(h->drv[drv_index]->LunID));
+       if (cciss_create_ld_sysfs_entry(h, drv_index))
+               goto err_free_disk;
        /* Don't need to mark this busy because nobody */
        /* else knows about this disk yet to contend */
        /* for access to it. */
-       h->drv[drv_index].busy_configuring = 0;
+       h->drv[drv_index]->busy_configuring = 0;
        wmb();
        return drv_index;
+
+err_free_disk:
+       cciss_free_gendisk(h, drv_index);
+err_free_drive_info:
+       cciss_free_drive_info(h, drv_index);
+       return -1;
 }
 
 /* This is for the special case of a controller which
@@ -1613,21 +2100,25 @@ static void cciss_add_controller_node(ctlr_info_t *h)
        if (h->gendisk[0] != NULL) /* already did this? Then bail. */
                return;
 
-       drv_index = cciss_add_gendisk(h, 0, 1);
-       if (drv_index == -1) {
-               printk(KERN_WARNING "cciss%d: could not "
-                       "add disk 0.\n", h->ctlr);
-               return;
-       }
-       h->drv[drv_index].block_size = 512;
-       h->drv[drv_index].nr_blocks = 0;
-       h->drv[drv_index].heads = 0;
-       h->drv[drv_index].sectors = 0;
-       h->drv[drv_index].cylinders = 0;
-       h->drv[drv_index].raid_level = -1;
-       memset(h->drv[drv_index].serial_no, 0, 16);
+       drv_index = cciss_add_gendisk(h, CTLR_LUNID, 1);
+       if (drv_index == -1)
+               goto error;
+       h->drv[drv_index]->block_size = 512;
+       h->drv[drv_index]->nr_blocks = 0;
+       h->drv[drv_index]->heads = 0;
+       h->drv[drv_index]->sectors = 0;
+       h->drv[drv_index]->cylinders = 0;
+       h->drv[drv_index]->raid_level = -1;
+       memset(h->drv[drv_index]->serial_no, 0, 16);
        disk = h->gendisk[drv_index];
-       cciss_add_disk(h, disk, drv_index);
+       if (cciss_add_disk(h, disk, drv_index) == 0)
+               return;
+       cciss_free_gendisk(h, drv_index);
+       cciss_free_drive_info(h, drv_index);
+error:
+       printk(KERN_WARNING "cciss%d: could not "
+               "add disk 0.\n", h->ctlr);
+       return;
 }
 
 /* This function will add and remove logical drives from the Logical
@@ -1638,7 +2129,8 @@ static void cciss_add_controller_node(ctlr_info_t *h)
  * INPUT
  * h           = The controller to perform the operations on
  */
-static int rebuild_lun_table(ctlr_info_t *h, int first_time)
+static int rebuild_lun_table(ctlr_info_t *h, int first_time,
+       int via_ioctl)
 {
        int ctlr = h->ctlr;
        int num_luns;
@@ -1648,7 +2140,7 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time)
        int i;
        int drv_found;
        int drv_index = 0;
-       __u32 lunid = 0;
+       unsigned char lunid[8] = CTLR_LUNID;
        unsigned long flags;
 
        if (!capable(CAP_SYS_RAWIO))
@@ -1668,8 +2160,8 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time)
                goto mem_msg;
 
        return_code = sendcmd_withirq(CISS_REPORT_LOG, ctlr, ld_buff,
-                                     sizeof(ReportLunData_struct), 0,
-                                     0, 0, TYPE_CMD);
+                                     sizeof(ReportLunData_struct),
+                                     0, CTLR_LUNID, TYPE_CMD);
 
        if (return_code == IO_OK)
                listlength = be32_to_cpu(*(__be32 *) ld_buff->LUNListLength);
@@ -1701,13 +2193,13 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time)
                drv_found = 0;
 
                /* skip holes in the array from already deleted drives */
-               if (h->drv[i].raid_level == -1)
+               if (h->drv[i] == NULL)
                        continue;
 
                for (j = 0; j < num_luns; j++) {
-                       memcpy(&lunid, &ld_buff->LUN[j][0], 4);
-                       lunid = le32_to_cpu(lunid);
-                       if (h->drv[i].LunID == lunid) {
+                       memcpy(lunid, &ld_buff->LUN[j][0], sizeof(lunid));
+                       if (memcmp(h->drv[i]->LunID, lunid,
+                               sizeof(lunid)) == 0) {
                                drv_found = 1;
                                break;
                        }
@@ -1715,10 +2207,11 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time)
                if (!drv_found) {
                        /* Deregister it from the OS, it's gone. */
                        spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
-                       h->drv[i].busy_configuring = 1;
+                       h->drv[i]->busy_configuring = 1;
                        spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
-                       return_code = deregister_disk(h, i, 1);
-                       h->drv[i].busy_configuring = 0;
+                       return_code = deregister_disk(h, i, 1, via_ioctl);
+                       if (h->drv[i] != NULL)
+                               h->drv[i]->busy_configuring = 0;
                }
        }
 
@@ -1732,17 +2225,16 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time)
 
                drv_found = 0;
 
-               memcpy(&lunid, &ld_buff->LUN[i][0], 4);
-               lunid = le32_to_cpu(lunid);
-
+               memcpy(lunid, &ld_buff->LUN[i][0], sizeof(lunid));
                /* Find if the LUN is already in the drive array
                 * of the driver.  If so then update its info
                 * if not in use.  If it does not exist then find
                 * the first free index and add it.
                 */
                for (j = 0; j <= h->highest_lun; j++) {
-                       if (h->drv[j].raid_level != -1 &&
-                               h->drv[j].LunID == lunid) {
+                       if (h->drv[j] != NULL &&
+                               memcmp(h->drv[j]->LunID, lunid,
+                                       sizeof(h->drv[j]->LunID)) == 0) {
                                drv_index = j;
                                drv_found = 1;
                                break;
@@ -1755,7 +2247,8 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time)
                        if (drv_index == -1)
                                goto freeret;
                }
-               cciss_update_drive_info(ctlr, drv_index, first_time);
+               cciss_update_drive_info(ctlr, drv_index, first_time,
+                       via_ioctl);
        }               /* end for */
 
 freeret:
@@ -1772,6 +2265,25 @@ mem_msg:
        goto freeret;
 }
 
+static void cciss_clear_drive_info(drive_info_struct *drive_info)
+{
+       /* zero out the disk size info */
+       drive_info->nr_blocks = 0;
+       drive_info->block_size = 0;
+       drive_info->heads = 0;
+       drive_info->sectors = 0;
+       drive_info->cylinders = 0;
+       drive_info->raid_level = -1;
+       memset(drive_info->serial_no, 0, sizeof(drive_info->serial_no));
+       memset(drive_info->model, 0, sizeof(drive_info->model));
+       memset(drive_info->rev, 0, sizeof(drive_info->rev));
+       memset(drive_info->vendor, 0, sizeof(drive_info->vendor));
+       /*
+        * don't clear the LUNID though, we need to remember which
+        * one this one is.
+        */
+}
+
 /* This function will deregister the disk and it's queue from the
  * kernel.  It must be called with the controller lock held and the
  * drv structures busy_configuring flag set.  It's parameters are:
@@ -1786,43 +2298,48 @@ mem_msg:
  *             the disk in preparation for re-adding it.  In this case
  *             the highest_lun should be left unchanged and the LunID
  *             should not be cleared.
+ * via_ioctl
+ *    This indicates whether we've reached this path via ioctl.
+ *    This affects the maximum usage count allowed for c0d0 to be messed with.
+ *    If this path is reached via ioctl(), then the max_usage_count will
+ *    be 1, as the process calling ioctl() has got to have the device open.
+ *    If we get here via sysfs, then the max usage count will be zero.
 */
 static int deregister_disk(ctlr_info_t *h, int drv_index,
-                          int clear_all)
+                          int clear_all, int via_ioctl)
 {
        int i;
        struct gendisk *disk;
        drive_info_struct *drv;
+       int recalculate_highest_lun;
 
        if (!capable(CAP_SYS_RAWIO))
                return -EPERM;
 
-       drv = &h->drv[drv_index];
+       drv = h->drv[drv_index];
        disk = h->gendisk[drv_index];
 
        /* make sure logical volume is NOT is use */
        if (clear_all || (h->gendisk[0] == disk)) {
-               if (drv->usage_count > 1)
+               if (drv->usage_count > via_ioctl)
                        return -EBUSY;
        } else if (drv->usage_count > 0)
                return -EBUSY;
 
+       recalculate_highest_lun = (drv == h->drv[h->highest_lun]);
+
        /* invalidate the devices and deregister the disk.  If it is disk
         * zero do not deregister it but just zero out it's values.  This
         * allows us to delete disk zero but keep the controller registered.
         */
        if (h->gendisk[0] != disk) {
                struct request_queue *q = disk->queue;
-               if (disk->flags & GENHD_FL_UP)
+               if (disk->flags & GENHD_FL_UP) {
+                       cciss_destroy_ld_sysfs_entry(h, drv_index, 0);
                        del_gendisk(disk);
-               if (q) {
-                       blk_cleanup_queue(q);
-                       /* Set drv->queue to NULL so that we do not try
-                        * to call blk_start_queue on this queue in the
-                        * interrupt handler
-                        */
-                       drv->queue = NULL;
                }
+               if (q)
+                       blk_cleanup_queue(q);
                /* If clear_all is set then we are deleting the logical
                 * drive, not just refreshing its info.  For drives
                 * other than disk 0 we will call put_disk.  We do not
@@ -1845,43 +2362,27 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
                }
        } else {
                set_capacity(disk, 0);
+               cciss_clear_drive_info(drv);
        }
 
        --h->num_luns;
-       /* zero out the disk size info */
-       drv->nr_blocks = 0;
-       drv->block_size = 0;
-       drv->heads = 0;
-       drv->sectors = 0;
-       drv->cylinders = 0;
-       drv->raid_level = -1;   /* This can be used as a flag variable to
-                                * indicate that this element of the drive
-                                * array is free.
-                                */
-
-       if (clear_all) {
-               /* check to see if it was the last disk */
-               if (drv == h->drv + h->highest_lun) {
-                       /* if so, find the new hightest lun */
-                       int i, newhighest = -1;
-                       for (i = 0; i <= h->highest_lun; i++) {
-                               /* if the disk has size > 0, it is available */
-                               if (h->drv[i].heads)
-                                       newhighest = i;
-                       }
-                       h->highest_lun = newhighest;
-               }
 
-               drv->LunID = 0;
+       /* if it was the last disk, find the new hightest lun */
+       if (clear_all && recalculate_highest_lun) {
+               int i, newhighest = -1;
+               for (i = 0; i <= h->highest_lun; i++) {
+                       /* if the disk has size > 0, it is available */
+                       if (h->drv[i] && h->drv[i]->heads)
+                               newhighest = i;
+               }
+               h->highest_lun = newhighest;
        }
        return 0;
 }
 
-static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff, size_t size, unsigned int use_unit_num,     /* 0: address the controller,
-                                                                                                                          1: address logical volume log_unit,
-                                                                                                                          2: periph device address is scsi3addr */
-                   unsigned int log_unit, __u8 page_code,
-                   unsigned char *scsi3addr, int cmd_type)
+static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff,
+               size_t size, __u8 page_code, unsigned char *scsi3addr,
+               int cmd_type)
 {
        ctlr_info_t *h = hba[ctlr];
        u64bit buff_dma_handle;
@@ -1897,27 +2398,12 @@ static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff, size_
                c->Header.SGTotal = 0;
        }
        c->Header.Tag.lower = c->busaddr;
+       memcpy(c->Header.LUN.LunAddrBytes, scsi3addr, 8);
 
        c->Request.Type.Type = cmd_type;
        if (cmd_type == TYPE_CMD) {
                switch (cmd) {
                case CISS_INQUIRY:
-                       /* If the logical unit number is 0 then, this is going
-                          to controller so It's a physical command
-                          mode = 0 target = 0.  So we have nothing to write.
-                          otherwise, if use_unit_num == 1,
-                          mode = 1(volume set addressing) target = LUNID
-                          otherwise, if use_unit_num == 2,
-                          mode = 0(periph dev addr) target = scsi3addr */
-                       if (use_unit_num == 1) {
-                               c->Header.LUN.LogDev.VolId =
-                                   h->drv[log_unit].LunID;
-                               c->Header.LUN.LogDev.Mode = 1;
-                       } else if (use_unit_num == 2) {
-                               memcpy(c->Header.LUN.LunAddrBytes, scsi3addr,
-                                      8);
-                               c->Header.LUN.LogDev.Mode = 0;
-                       }
                        /* are we trying to read a vital product page */
                        if (page_code != 0) {
                                c->Request.CDB[1] = 0x01;
@@ -1940,15 +2426,13 @@ static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff, size_
                        c->Request.Type.Direction = XFER_READ;
                        c->Request.Timeout = 0;
                        c->Request.CDB[0] = cmd;
-                       c->Request.CDB[6] = (size >> 24) & 0xFF;        //MSB
+                       c->Request.CDB[6] = (size >> 24) & 0xFF; /* MSB */
                        c->Request.CDB[7] = (size >> 16) & 0xFF;
                        c->Request.CDB[8] = (size >> 8) & 0xFF;
                        c->Request.CDB[9] = size & 0xFF;
                        break;
 
                case CCISS_READ_CAPACITY:
-                       c->Header.LUN.LogDev.VolId = h->drv[log_unit].LunID;
-                       c->Header.LUN.LogDev.Mode = 1;
                        c->Request.CDBLen = 10;
                        c->Request.Type.Attribute = ATTR_SIMPLE;
                        c->Request.Type.Direction = XFER_READ;
@@ -1956,8 +2440,6 @@ static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff, size_
                        c->Request.CDB[0] = cmd;
                        break;
                case CCISS_READ_CAPACITY_16:
-                       c->Header.LUN.LogDev.VolId = h->drv[log_unit].LunID;
-                       c->Header.LUN.LogDev.Mode = 1;
                        c->Request.CDBLen = 16;
                        c->Request.Type.Attribute = ATTR_SIMPLE;
                        c->Request.Type.Direction = XFER_READ;
@@ -1979,6 +2461,12 @@ static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff, size_
                        c->Request.CDB[0] = BMIC_WRITE;
                        c->Request.CDB[6] = BMIC_CACHE_FLUSH;
                        break;
+               case TEST_UNIT_READY:
+                       c->Request.CDBLen = 6;
+                       c->Request.Type.Attribute = ATTR_SIMPLE;
+                       c->Request.Type.Direction = XFER_NONE;
+                       c->Request.Timeout = 0;
+                       break;
                default:
                        printk(KERN_WARNING
                               "cciss%d:  Unknown Command 0x%c\n", ctlr, cmd);
@@ -1997,13 +2485,13 @@ static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff, size_
                        memcpy(&c->Request.CDB[4], buff, 8);
                        break;
                case 1: /* RESET message */
-                       c->Request.CDBLen = 12;
+                       c->Request.CDBLen = 16;
                        c->Request.Type.Attribute = ATTR_SIMPLE;
-                       c->Request.Type.Direction = XFER_WRITE;
+                       c->Request.Type.Direction = XFER_NONE;
                        c->Request.Timeout = 0;
                        memset(&c->Request.CDB[0], 0, sizeof(c->Request.CDB));
                        c->Request.CDB[0] = cmd;        /* reset */
-                       c->Request.CDB[1] = 0x04;       /* reset a LUN */
+                       c->Request.CDB[1] = 0x03;       /* reset a target */
                        break;
                case 3: /* No-Op message */
                        c->Request.CDBLen = 1;
@@ -2035,143 +2523,179 @@ static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff, size_
        return status;
 }
 
-static int sendcmd_withirq(__u8 cmd,
-                          int ctlr,
-                          void *buff,
-                          size_t size,
-                          unsigned int use_unit_num,
-                          unsigned int log_unit, __u8 page_code, int cmd_type)
+static int check_target_status(ctlr_info_t *h, CommandList_struct *c)
 {
-       ctlr_info_t *h = hba[ctlr];
-       CommandList_struct *c;
-       u64bit buff_dma_handle;
-       unsigned long flags;
-       int return_status;
-       DECLARE_COMPLETION_ONSTACK(wait);
+       switch (c->err_info->ScsiStatus) {
+       case SAM_STAT_GOOD:
+               return IO_OK;
+       case SAM_STAT_CHECK_CONDITION:
+               switch (0xf & c->err_info->SenseInfo[2]) {
+               case 0: return IO_OK; /* no sense */
+               case 1: return IO_OK; /* recovered error */
+               default:
+                       if (check_for_unit_attention(h, c))
+                               return IO_NEEDS_RETRY;
+                       printk(KERN_WARNING "cciss%d: cmd 0x%02x "
+                               "check condition, sense key = 0x%02x\n",
+                               h->ctlr, c->Request.CDB[0],
+                               c->err_info->SenseInfo[2]);
+               }
+               break;
+       default:
+               printk(KERN_WARNING "cciss%d: cmd 0x%02x"
+                       "scsi status = 0x%02x\n", h->ctlr,
+                       c->Request.CDB[0], c->err_info->ScsiStatus);
+               break;
+       }
+       return IO_ERROR;
+}
 
-       if ((c = cmd_alloc(h, 0)) == NULL)
-               return -ENOMEM;
-       return_status = fill_cmd(c, cmd, ctlr, buff, size, use_unit_num,
-                                log_unit, page_code, NULL, cmd_type);
-       if (return_status != IO_OK) {
-               cmd_free(h, c, 0);
-               return return_status;
+static int process_sendcmd_error(ctlr_info_t *h, CommandList_struct *c)
+{
+       int return_status = IO_OK;
+
+       if (c->err_info->CommandStatus == CMD_SUCCESS)
+               return IO_OK;
+
+       switch (c->err_info->CommandStatus) {
+       case CMD_TARGET_STATUS:
+               return_status = check_target_status(h, c);
+               break;
+       case CMD_DATA_UNDERRUN:
+       case CMD_DATA_OVERRUN:
+               /* expected for inquiry and report lun commands */
+               break;
+       case CMD_INVALID:
+               printk(KERN_WARNING "cciss: cmd 0x%02x is "
+                      "reported invalid\n", c->Request.CDB[0]);
+               return_status = IO_ERROR;
+               break;
+       case CMD_PROTOCOL_ERR:
+               printk(KERN_WARNING "cciss: cmd 0x%02x has "
+                      "protocol error \n", c->Request.CDB[0]);
+               return_status = IO_ERROR;
+               break;
+       case CMD_HARDWARE_ERR:
+               printk(KERN_WARNING "cciss: cmd 0x%02x had "
+                      " hardware error\n", c->Request.CDB[0]);
+               return_status = IO_ERROR;
+               break;
+       case CMD_CONNECTION_LOST:
+               printk(KERN_WARNING "cciss: cmd 0x%02x had "
+                      "connection lost\n", c->Request.CDB[0]);
+               return_status = IO_ERROR;
+               break;
+       case CMD_ABORTED:
+               printk(KERN_WARNING "cciss: cmd 0x%02x was "
+                      "aborted\n", c->Request.CDB[0]);
+               return_status = IO_ERROR;
+               break;
+       case CMD_ABORT_FAILED:
+               printk(KERN_WARNING "cciss: cmd 0x%02x reports "
+                      "abort failed\n", c->Request.CDB[0]);
+               return_status = IO_ERROR;
+               break;
+       case CMD_UNSOLICITED_ABORT:
+               printk(KERN_WARNING
+                      "cciss%d: unsolicited abort 0x%02x\n", h->ctlr,
+                       c->Request.CDB[0]);
+               return_status = IO_NEEDS_RETRY;
+               break;
+       default:
+               printk(KERN_WARNING "cciss: cmd 0x%02x returned "
+                      "unknown status %x\n", c->Request.CDB[0],
+                      c->err_info->CommandStatus);
+               return_status = IO_ERROR;
        }
-      resend_cmd2:
-       c->waiting = &wait;
+       return return_status;
+}
 
+static int sendcmd_withirq_core(ctlr_info_t *h, CommandList_struct *c,
+       int attempt_retry)
+{
+       DECLARE_COMPLETION_ONSTACK(wait);
+       u64bit buff_dma_handle;
+       unsigned long flags;
+       int return_status = IO_OK;
+
+resend_cmd2:
+       c->waiting = &wait;
        /* Put the request on the tail of the queue and send it */
-       spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
+       spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
        addQ(&h->reqQ, c);
        h->Qdepth++;
        start_io(h);
-       spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
+       spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
 
        wait_for_completion(&wait);
 
-       if (c->err_info->CommandStatus != 0) {  /* an error has occurred */
-               switch (c->err_info->CommandStatus) {
-               case CMD_TARGET_STATUS:
-                       printk(KERN_WARNING "cciss: cmd %p has "
-                              " completed with errors\n", c);
-                       if (c->err_info->ScsiStatus) {
-                               printk(KERN_WARNING "cciss: cmd %p "
-                                      "has SCSI Status = %x\n",
-                                      c, c->err_info->ScsiStatus);
-                       }
+       if (c->err_info->CommandStatus == 0 || !attempt_retry)
+               goto command_done;
 
-                       break;
-               case CMD_DATA_UNDERRUN:
-               case CMD_DATA_OVERRUN:
-                       /* expected for inquire and report lun commands */
-                       break;
-               case CMD_INVALID:
-                       printk(KERN_WARNING "cciss: Cmd %p is "
-                              "reported invalid\n", c);
-                       return_status = IO_ERROR;
-                       break;
-               case CMD_PROTOCOL_ERR:
-                       printk(KERN_WARNING "cciss: cmd %p has "
-                              "protocol error \n", c);
-                       return_status = IO_ERROR;
-                       break;
-               case CMD_HARDWARE_ERR:
-                       printk(KERN_WARNING "cciss: cmd %p had "
-                              " hardware error\n", c);
-                       return_status = IO_ERROR;
-                       break;
-               case CMD_CONNECTION_LOST:
-                       printk(KERN_WARNING "cciss: cmd %p had "
-                              "connection lost\n", c);
-                       return_status = IO_ERROR;
-                       break;
-               case CMD_ABORTED:
-                       printk(KERN_WARNING "cciss: cmd %p was "
-                              "aborted\n", c);
-                       return_status = IO_ERROR;
-                       break;
-               case CMD_ABORT_FAILED:
-                       printk(KERN_WARNING "cciss: cmd %p reports "
-                              "abort failed\n", c);
-                       return_status = IO_ERROR;
-                       break;
-               case CMD_UNSOLICITED_ABORT:
-                       printk(KERN_WARNING
-                              "cciss%d: unsolicited abort %p\n", ctlr, c);
-                       if (c->retry_count < MAX_CMD_RETRIES) {
-                               printk(KERN_WARNING
-                                      "cciss%d: retrying %p\n", ctlr, c);
-                               c->retry_count++;
-                               /* erase the old error information */
-                               memset(c->err_info, 0,
-                                      sizeof(ErrorInfo_struct));
-                               return_status = IO_OK;
-                               INIT_COMPLETION(wait);
-                               goto resend_cmd2;
-                       }
-                       return_status = IO_ERROR;
-                       break;
-               default:
-                       printk(KERN_WARNING "cciss: cmd %p returned "
-                              "unknown status %x\n", c,
-                              c->err_info->CommandStatus);
-                       return_status = IO_ERROR;
-               }
+       return_status = process_sendcmd_error(h, c);
+
+       if (return_status == IO_NEEDS_RETRY &&
+               c->retry_count < MAX_CMD_RETRIES) {
+               printk(KERN_WARNING "cciss%d: retrying 0x%02x\n", h->ctlr,
+                       c->Request.CDB[0]);
+               c->retry_count++;
+               /* erase the old error information */
+               memset(c->err_info, 0, sizeof(ErrorInfo_struct));
+               return_status = IO_OK;
+               INIT_COMPLETION(wait);
+               goto resend_cmd2;
        }
+
+command_done:
        /* unlock the buffers from DMA */
        buff_dma_handle.val32.lower = c->SG[0].Addr.lower;
        buff_dma_handle.val32.upper = c->SG[0].Addr.upper;
        pci_unmap_single(h->pdev, (dma_addr_t) buff_dma_handle.val,
                         c->SG[0].Len, PCI_DMA_BIDIRECTIONAL);
+       return return_status;
+}
+
+static int sendcmd_withirq(__u8 cmd, int ctlr, void *buff, size_t size,
+                          __u8 page_code, unsigned char scsi3addr[],
+                       int cmd_type)
+{
+       ctlr_info_t *h = hba[ctlr];
+       CommandList_struct *c;
+       int return_status;
+
+       c = cmd_alloc(h, 0);
+       if (!c)
+               return -ENOMEM;
+       return_status = fill_cmd(c, cmd, ctlr, buff, size, page_code,
+               scsi3addr, cmd_type);
+       if (return_status == IO_OK)
+               return_status = sendcmd_withirq_core(h, c, 1);
+
        cmd_free(h, c, 0);
        return return_status;
 }
 
 static void cciss_geometry_inquiry(int ctlr, int logvol,
-                                  int withirq, sector_t total_size,
+                                  sector_t total_size,
                                   unsigned int block_size,
                                   InquiryData_struct *inq_buff,
                                   drive_info_struct *drv)
 {
        int return_code;
        unsigned long t;
+       unsigned char scsi3addr[8];
 
        memset(inq_buff, 0, sizeof(InquiryData_struct));
-       if (withirq)
-               return_code = sendcmd_withirq(CISS_INQUIRY, ctlr,
-                                             inq_buff, sizeof(*inq_buff), 1,
-                                             logvol, 0xC1, TYPE_CMD);
-       else
-               return_code = sendcmd(CISS_INQUIRY, ctlr, inq_buff,
-                                     sizeof(*inq_buff), 1, logvol, 0xC1, NULL,
-                                     TYPE_CMD);
+       log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
+       return_code = sendcmd_withirq(CISS_INQUIRY, ctlr, inq_buff,
+                       sizeof(*inq_buff), 0xC1, scsi3addr, TYPE_CMD);
        if (return_code == IO_OK) {
                if (inq_buff->data_byte[8] == 0xFF) {
                        printk(KERN_WARNING
                               "cciss: reading geometry failed, volume "
                               "does not support reading geometry\n");
                        drv->heads = 255;
-                       drv->sectors = 32;      // Sectors per track
+                       drv->sectors = 32;      /* Sectors per track */
                        drv->cylinders = total_size + 1;
                        drv->raid_level = RAID_UNKNOWN;
                } else {
@@ -2194,16 +2718,15 @@ static void cciss_geometry_inquiry(int ctlr, int logvol,
        } else {                /* Get geometry failed */
                printk(KERN_WARNING "cciss: reading geometry failed\n");
        }
-       printk(KERN_INFO "      heads=%d, sectors=%d, cylinders=%d\n\n",
-              drv->heads, drv->sectors, drv->cylinders);
 }
 
 static void
-cciss_read_capacity(int ctlr, int logvol, int withirq, sector_t *total_size,
+cciss_read_capacity(int ctlr, int logvol, sector_t *total_size,
                    unsigned int *block_size)
 {
        ReadCapdata_struct *buf;
        int return_code;
+       unsigned char scsi3addr[8];
 
        buf = kzalloc(sizeof(ReadCapdata_struct), GFP_KERNEL);
        if (!buf) {
@@ -2211,14 +2734,9 @@ cciss_read_capacity(int ctlr, int logvol, int withirq, sector_t *total_size,
                return;
        }
 
-       if (withirq)
-               return_code = sendcmd_withirq(CCISS_READ_CAPACITY,
-                               ctlr, buf, sizeof(ReadCapdata_struct),
-                                       1, logvol, 0, TYPE_CMD);
-       else
-               return_code = sendcmd(CCISS_READ_CAPACITY,
-                               ctlr, buf, sizeof(ReadCapdata_struct),
-                                       1, logvol, 0, NULL, TYPE_CMD);
+       log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
+       return_code = sendcmd_withirq(CCISS_READ_CAPACITY, ctlr, buf,
+               sizeof(ReadCapdata_struct), 0, scsi3addr, TYPE_CMD);
        if (return_code == IO_OK) {
                *total_size = be32_to_cpu(*(__be32 *) buf->total_size);
                *block_size = be32_to_cpu(*(__be32 *) buf->block_size);
@@ -2227,17 +2745,15 @@ cciss_read_capacity(int ctlr, int logvol, int withirq, sector_t *total_size,
                *total_size = 0;
                *block_size = BLOCK_SIZE;
        }
-       if (*total_size != 0)
-               printk(KERN_INFO "      blocks= %llu block_size= %d\n",
-               (unsigned long long)*total_size+1, *block_size);
        kfree(buf);
 }
 
-static void
-cciss_read_capacity_16(int ctlr, int logvol, int withirq, sector_t *total_size,                                unsigned int *block_size)
+static void cciss_read_capacity_16(int ctlr, int logvol,
+       sector_t *total_size, unsigned int *block_size)
 {
        ReadCapdata_struct_16 *buf;
        int return_code;
+       unsigned char scsi3addr[8];
 
        buf = kzalloc(sizeof(ReadCapdata_struct_16), GFP_KERNEL);
        if (!buf) {
@@ -2245,16 +2761,10 @@ cciss_read_capacity_16(int ctlr, int logvol, int withirq, sector_t *total_size,
                return;
        }
 
-       if (withirq) {
-               return_code = sendcmd_withirq(CCISS_READ_CAPACITY_16,
-                       ctlr, buf, sizeof(ReadCapdata_struct_16),
-                               1, logvol, 0, TYPE_CMD);
-       }
-       else {
-               return_code = sendcmd(CCISS_READ_CAPACITY_16,
-                       ctlr, buf, sizeof(ReadCapdata_struct_16),
-                               1, logvol, 0, NULL, TYPE_CMD);
-       }
+       log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
+       return_code = sendcmd_withirq(CCISS_READ_CAPACITY_16,
+               ctlr, buf, sizeof(ReadCapdata_struct_16),
+                       0, scsi3addr, TYPE_CMD);
        if (return_code == IO_OK) {
                *total_size = be64_to_cpu(*(__be64 *) buf->total_size);
                *block_size = be32_to_cpu(*(__be32 *) buf->block_size);
@@ -2279,7 +2789,8 @@ static int cciss_revalidate(struct gendisk *disk)
        InquiryData_struct *inq_buff = NULL;
 
        for (logvol = 0; logvol < CISS_MAX_LUN; logvol++) {
-               if (h->drv[logvol].LunID == drv->LunID) {
+               if (memcmp(h->drv[logvol]->LunID, drv->LunID,
+                       sizeof(drv->LunID)) == 0) {
                        FOUND = 1;
                        break;
                }
@@ -2294,16 +2805,16 @@ static int cciss_revalidate(struct gendisk *disk)
                return 1;
        }
        if (h->cciss_read == CCISS_READ_10) {
-               cciss_read_capacity(h->ctlr, logvol, 1,
+               cciss_read_capacity(h->ctlr, logvol,
                                        &total_size, &block_size);
        } else {
-               cciss_read_capacity_16(h->ctlr, logvol, 1,
+               cciss_read_capacity_16(h->ctlr, logvol,
                                        &total_size, &block_size);
        }
-       cciss_geometry_inquiry(h->ctlr, logvol, 1, total_size, block_size,
+       cciss_geometry_inquiry(h->ctlr, logvol, total_size, block_size,
                               inq_buff, drv);
 
-       blk_queue_hardsect_size(drv->queue, drv->block_size);
+       blk_queue_logical_block_size(drv->queue, drv->block_size);
        set_capacity(disk, drv->nr_blocks);
 
        kfree(inq_buff);
@@ -2311,233 +2822,6 @@ static int cciss_revalidate(struct gendisk *disk)
 }
 
 /*
- *   Wait polling for a command to complete.
- *   The memory mapped FIFO is polled for the completion.
- *   Used only at init time, interrupts from the HBA are disabled.
- */
-static unsigned long pollcomplete(int ctlr)
-{
-       unsigned long done;
-       int i;
-
-       /* Wait (up to 20 seconds) for a command to complete */
-
-       for (i = 20 * HZ; i > 0; i--) {
-               done = hba[ctlr]->access.command_completed(hba[ctlr]);
-               if (done == FIFO_EMPTY)
-                       schedule_timeout_uninterruptible(1);
-               else
-                       return done;
-       }
-       /* Invalid address to tell caller we ran out of time */
-       return 1;
-}
-
-static int add_sendcmd_reject(__u8 cmd, int ctlr, unsigned long complete)
-{
-       /* We get in here if sendcmd() is polling for completions
-          and gets some command back that it wasn't expecting --
-          something other than that which it just sent down.
-          Ordinarily, that shouldn't happen, but it can happen when
-          the scsi tape stuff gets into error handling mode, and
-          starts using sendcmd() to try to abort commands and
-          reset tape drives.  In that case, sendcmd may pick up
-          completions of commands that were sent to logical drives
-          through the block i/o system, or cciss ioctls completing, etc.
-          In that case, we need to save those completions for later
-          processing by the interrupt handler.
-        */
-
-#ifdef CONFIG_CISS_SCSI_TAPE
-       struct sendcmd_reject_list *srl = &hba[ctlr]->scsi_rejects;
-
-       /* If it's not the scsi tape stuff doing error handling, (abort */
-       /* or reset) then we don't expect anything weird. */
-       if (cmd != CCISS_RESET_MSG && cmd != CCISS_ABORT_MSG) {
-#endif
-               printk(KERN_WARNING "cciss cciss%d: SendCmd "
-                      "Invalid command list address returned! (%lx)\n",
-                      ctlr, complete);
-               /* not much we can do. */
-#ifdef CONFIG_CISS_SCSI_TAPE
-               return 1;
-       }
-
-       /* We've sent down an abort or reset, but something else
-          has completed */
-       if (srl->ncompletions >= (hba[ctlr]->nr_cmds + 2)) {
-               /* Uh oh.  No room to save it for later... */
-               printk(KERN_WARNING "cciss%d: Sendcmd: Invalid command addr, "
-                      "reject list overflow, command lost!\n", ctlr);
-               return 1;
-       }
-       /* Save it for later */
-       srl->complete[srl->ncompletions] = complete;
-       srl->ncompletions++;
-#endif
-       return 0;
-}
-
-/*
- * Send a command to the controller, and wait for it to complete.
- * Only used at init time.
- */
-static int sendcmd(__u8 cmd, int ctlr, void *buff, size_t size, unsigned int use_unit_num,     /* 0: address the controller,
-                                                                                                  1: address logical volume log_unit,
-                                                                                                  2: periph device address is scsi3addr */
-                  unsigned int log_unit,
-                  __u8 page_code, unsigned char *scsi3addr, int cmd_type)
-{
-       CommandList_struct *c;
-       int i;
-       unsigned long complete;
-       ctlr_info_t *info_p = hba[ctlr];
-       u64bit buff_dma_handle;
-       int status, done = 0;
-
-       if ((c = cmd_alloc(info_p, 1)) == NULL) {
-               printk(KERN_WARNING "cciss: unable to get memory");
-               return IO_ERROR;
-       }
-       status = fill_cmd(c, cmd, ctlr, buff, size, use_unit_num,
-                         log_unit, page_code, scsi3addr, cmd_type);
-       if (status != IO_OK) {
-               cmd_free(info_p, c, 1);
-               return status;
-       }
-      resend_cmd1:
-       /*
-        * Disable interrupt
-        */
-#ifdef CCISS_DEBUG
-       printk(KERN_DEBUG "cciss: turning intr off\n");
-#endif                         /* CCISS_DEBUG */
-       info_p->access.set_intr_mask(info_p, CCISS_INTR_OFF);
-
-       /* Make sure there is room in the command FIFO */
-       /* Actually it should be completely empty at this time */
-       /* unless we are in here doing error handling for the scsi */
-       /* tape side of the driver. */
-       for (i = 200000; i > 0; i--) {
-               /* if fifo isn't full go */
-               if (!(info_p->access.fifo_full(info_p))) {
-
-                       break;
-               }
-               udelay(10);
-               printk(KERN_WARNING "cciss cciss%d: SendCmd FIFO full,"
-                      " waiting!\n", ctlr);
-       }
-       /*
-        * Send the cmd
-        */
-       info_p->access.submit_command(info_p, c);
-       done = 0;
-       do {
-               complete = pollcomplete(ctlr);
-
-#ifdef CCISS_DEBUG
-               printk(KERN_DEBUG "cciss: command completed\n");
-#endif                         /* CCISS_DEBUG */
-
-               if (complete == 1) {
-                       printk(KERN_WARNING
-                              "cciss cciss%d: SendCmd Timeout out, "
-                              "No command list address returned!\n", ctlr);
-                       status = IO_ERROR;
-                       done = 1;
-                       break;
-               }
-
-               /* This will need to change for direct lookup completions */
-               if ((complete & CISS_ERROR_BIT)
-                   && (complete & ~CISS_ERROR_BIT) == c->busaddr) {
-                       /* if data overrun or underun on Report command
-                          ignore it
-                        */
-                       if (((c->Request.CDB[0] == CISS_REPORT_LOG) ||
-                            (c->Request.CDB[0] == CISS_REPORT_PHYS) ||
-                            (c->Request.CDB[0] == CISS_INQUIRY)) &&
-                           ((c->err_info->CommandStatus ==
-                             CMD_DATA_OVERRUN) ||
-                            (c->err_info->CommandStatus == CMD_DATA_UNDERRUN)
-                           )) {
-                               complete = c->busaddr;
-                       } else {
-                               if (c->err_info->CommandStatus ==
-                                   CMD_UNSOLICITED_ABORT) {
-                                       printk(KERN_WARNING "cciss%d: "
-                                              "unsolicited abort %p\n",
-                                              ctlr, c);
-                                       if (c->retry_count < MAX_CMD_RETRIES) {
-                                               printk(KERN_WARNING
-                                                      "cciss%d: retrying %p\n",
-                                                      ctlr, c);
-                                               c->retry_count++;
-                                               /* erase the old error */
-                                               /* information */
-                                               memset(c->err_info, 0,
-                                                      sizeof
-                                                      (ErrorInfo_struct));
-                                               goto resend_cmd1;
-                                       } else {
-                                               printk(KERN_WARNING
-                                                      "cciss%d: retried %p too "
-                                                      "many times\n", ctlr, c);
-                                               status = IO_ERROR;
-                                               goto cleanup1;
-                                       }
-                               } else if (c->err_info->CommandStatus ==
-                                          CMD_UNABORTABLE) {
-                                       printk(KERN_WARNING
-                                              "cciss%d: command could not be aborted.\n",
-                                              ctlr);
-                                       status = IO_ERROR;
-                                       goto cleanup1;
-                               }
-                               printk(KERN_WARNING "ciss ciss%d: sendcmd"
-                                      " Error %x \n", ctlr,
-                                      c->err_info->CommandStatus);
-                               printk(KERN_WARNING "ciss ciss%d: sendcmd"
-                                      " offensive info\n"
-                                      "  size %x\n   num %x   value %x\n",
-                                      ctlr,
-                                      c->err_info->MoreErrInfo.Invalid_Cmd.
-                                      offense_size,
-                                      c->err_info->MoreErrInfo.Invalid_Cmd.
-                                      offense_num,
-                                      c->err_info->MoreErrInfo.Invalid_Cmd.
-                                      offense_value);
-                               status = IO_ERROR;
-                               goto cleanup1;
-                       }
-               }
-               /* This will need changing for direct lookup completions */
-               if (complete != c->busaddr) {
-                       if (add_sendcmd_reject(cmd, ctlr, complete) != 0) {
-                               BUG();  /* we are pretty much hosed if we get here. */
-                       }
-                       continue;
-               } else
-                       done = 1;
-       } while (!done);
-
-      cleanup1:
-       /* unlock the data buffer from DMA */
-       buff_dma_handle.val32.lower = c->SG[0].Addr.lower;
-       buff_dma_handle.val32.upper = c->SG[0].Addr.upper;
-       pci_unmap_single(info_p->pdev, (dma_addr_t) buff_dma_handle.val,
-                        c->SG[0].Len, PCI_DMA_BIDIRECTIONAL);
-#ifdef CONFIG_CISS_SCSI_TAPE
-       /* if we saved some commands for later, process them now. */
-       if (info_p->scsi_rejects.ncompletions > 0)
-               do_cciss_intr(0, info_p);
-#endif
-       cmd_free(info_p, c, 1);
-       return status;
-}
-
-/*
  * Map (physical) PCI mem into (virtual) kernel space
  */
 static void __iomem *remap_pci_mem(ulong base, ulong size)
@@ -2691,7 +2975,7 @@ static inline void complete_command(ctlr_info_t *h, CommandList_struct *cmd,
                        printk(KERN_WARNING "cciss: cmd %p has"
                               " completed with data underrun "
                               "reported\n", cmd);
-                       cmd->rq->data_len = cmd->err_info->ResidualCnt;
+                       cmd->rq->resid_len = cmd->err_info->ResidualCnt;
                }
                break;
        case CMD_DATA_OVERRUN:
@@ -2795,9 +3079,13 @@ static void do_cciss_request(struct request_queue *q)
        int seg;
        struct request *creq;
        u64bit temp64;
-       struct scatterlist tmp_sg[MAXSGENTRIES];
+       struct scatterlist *tmp_sg;
+       SGDescriptor_struct *curr_sg;
        drive_info_struct *drv;
        int i, dir;
+       int nseg = 0;
+       int sg_index = 0;
+       int chained = 0;
 
        /* We call start_io here in case there is a command waiting on the
         * queue that has not been sent.
@@ -2806,17 +3094,18 @@ static void do_cciss_request(struct request_queue *q)
                goto startio;
 
       queue:
-       creq = elv_next_request(q);
+       creq = blk_peek_request(q);
        if (!creq)
                goto startio;
 
-       BUG_ON(creq->nr_phys_segments > MAXSGENTRIES);
+       BUG_ON(creq->nr_phys_segments > h->maxsgentries);
 
        if ((c = cmd_alloc(h, 1)) == NULL)
                goto full;
 
-       blkdev_dequeue_request(creq);
+       blk_start_request(creq);
 
+       tmp_sg = h->scatter_list[c->cmdindex];
        spin_unlock_irq(q->queue_lock);
 
        c->cmd_type = CMD_RWREQ;
@@ -2824,29 +3113,28 @@ static void do_cciss_request(struct request_queue *q)
 
        /* fill in the request */
        drv = creq->rq_disk->private_data;
-       c->Header.ReplyQueue = 0;       // unused in simple mode
+       c->Header.ReplyQueue = 0;       /* unused in simple mode */
        /* got command from pool, so use the command block index instead */
        /* for direct lookups. */
        /* The first 2 bits are reserved for controller error reporting. */
        c->Header.Tag.lower = (c->cmdindex << 3);
        c->Header.Tag.lower |= 0x04;    /* flag for direct lookup. */
-       c->Header.LUN.LogDev.VolId = drv->LunID;
-       c->Header.LUN.LogDev.Mode = 1;
-       c->Request.CDBLen = 10; // 12 byte commands not in FW yet;
-       c->Request.Type.Type = TYPE_CMD;        // It is a command.
+       memcpy(&c->Header.LUN, drv->LunID, sizeof(drv->LunID));
+       c->Request.CDBLen = 10; /* 12 byte commands not in FW yet; */
+       c->Request.Type.Type = TYPE_CMD;        /* It is a command. */
        c->Request.Type.Attribute = ATTR_SIMPLE;
        c->Request.Type.Direction =
            (rq_data_dir(creq) == READ) ? XFER_READ : XFER_WRITE;
-       c->Request.Timeout = 0; // Don't time out
+       c->Request.Timeout = 0; /* Don't time out */
        c->Request.CDB[0] =
            (rq_data_dir(creq) == READ) ? h->cciss_read : h->cciss_write;
-       start_blk = creq->sector;
+       start_blk = blk_rq_pos(creq);
 #ifdef CCISS_DEBUG
-       printk(KERN_DEBUG "ciss: sector =%d nr_sectors=%d\n", (int)creq->sector,
-              (int)creq->nr_sectors);
+       printk(KERN_DEBUG "ciss: sector =%d nr_sectors=%d\n",
+              (int)blk_rq_pos(creq), (int)blk_rq_sectors(creq));
 #endif                         /* CCISS_DEBUG */
 
-       sg_init_table(tmp_sg, MAXSGENTRIES);
+       sg_init_table(tmp_sg, h->maxsgentries);
        seg = blk_rq_map_sg(q, creq, tmp_sg);
 
        /* get the DMA records for the setup */
@@ -2855,42 +3143,87 @@ static void do_cciss_request(struct request_queue *q)
        else
                dir = PCI_DMA_TODEVICE;
 
+       curr_sg = c->SG;
+       sg_index = 0;
+       chained = 0;
+
        for (i = 0; i < seg; i++) {
-               c->SG[i].Len = tmp_sg[i].length;
+               if (((sg_index+1) == (h->max_cmd_sgentries)) &&
+                       !chained && ((seg - i) > 1)) {
+                       nseg = seg - i;
+                       curr_sg[sg_index].Len = (nseg) *
+                                       sizeof(SGDescriptor_struct);
+                       curr_sg[sg_index].Ext = CCISS_SG_CHAIN;
+
+                       /* Point to next chain block. */
+                       curr_sg = h->cmd_sg_list[c->cmdindex]->sgchain;
+                       sg_index = 0;
+                       chained = 1;
+               }
+               curr_sg[sg_index].Len = tmp_sg[i].length;
                temp64.val = (__u64) pci_map_page(h->pdev, sg_page(&tmp_sg[i]),
-                                                 tmp_sg[i].offset,
-                                                 tmp_sg[i].length, dir);
-               c->SG[i].Addr.lower = temp64.val32.lower;
-               c->SG[i].Addr.upper = temp64.val32.upper;
-               c->SG[i].Ext = 0;       // we are not chaining
+                                               tmp_sg[i].offset,
+                                               tmp_sg[i].length, dir);
+               curr_sg[sg_index].Addr.lower = temp64.val32.lower;
+               curr_sg[sg_index].Addr.upper = temp64.val32.upper;
+               curr_sg[sg_index].Ext = 0;  /* we are not chaining */
+
+               ++sg_index;
+       }
+
+       if (chained) {
+               int len;
+               curr_sg = c->SG;
+               sg_index = h->max_cmd_sgentries - 1;
+               len = curr_sg[sg_index].Len;
+               /* Setup pointer to next chain block.
+                * Fill out last element in current chain
+                * block with address of next chain block.
+                */
+               temp64.val = pci_map_single(h->pdev,
+                                       h->cmd_sg_list[c->cmdindex]->sgchain,
+                                       len, dir);
+
+               h->cmd_sg_list[c->cmdindex]->sg_chain_dma = temp64.val;
+               curr_sg[sg_index].Addr.lower = temp64.val32.lower;
+               curr_sg[sg_index].Addr.upper = temp64.val32.upper;
+
+               pci_dma_sync_single_for_device(h->pdev,
+                               h->cmd_sg_list[c->cmdindex]->sg_chain_dma,
+                               len, dir);
        }
+
        /* track how many SG entries we are using */
        if (seg > h->maxSG)
                h->maxSG = seg;
 
 #ifdef CCISS_DEBUG
-       printk(KERN_DEBUG "cciss: Submitting %lu sectors in %d segments\n",
-              creq->nr_sectors, seg);
+       printk(KERN_DEBUG "cciss: Submitting %ld sectors in %d segments "
+                       "chained[%d]\n",
+                       blk_rq_sectors(creq), seg, chained);
 #endif                         /* CCISS_DEBUG */
 
-       c->Header.SGList = c->Header.SGTotal = seg;
+       c->Header.SGList = c->Header.SGTotal = seg + chained;
+       if (seg > h->max_cmd_sgentries)
+               c->Header.SGList = h->max_cmd_sgentries;
+
        if (likely(blk_fs_request(creq))) {
                if(h->cciss_read == CCISS_READ_10) {
                        c->Request.CDB[1] = 0;
-                       c->Request.CDB[2] = (start_blk >> 24) & 0xff;   //MSB
+                       c->Request.CDB[2] = (start_blk >> 24) & 0xff; /* MSB */
                        c->Request.CDB[3] = (start_blk >> 16) & 0xff;
                        c->Request.CDB[4] = (start_blk >> 8) & 0xff;
                        c->Request.CDB[5] = start_blk & 0xff;
-                       c->Request.CDB[6] = 0;  // (sect >> 24) & 0xff; MSB
-                       c->Request.CDB[7] = (creq->nr_sectors >> 8) & 0xff;
-                       c->Request.CDB[8] = creq->nr_sectors & 0xff;
+                       c->Request.CDB[6] = 0; /* (sect >> 24) & 0xff; MSB */
+                       c->Request.CDB[7] = (blk_rq_sectors(creq) >> 8) & 0xff;
+                       c->Request.CDB[8] = blk_rq_sectors(creq) & 0xff;
                        c->Request.CDB[9] = c->Request.CDB[11] = c->Request.CDB[12] = 0;
                } else {
                        u32 upper32 = upper_32_bits(start_blk);
 
                        c->Request.CDBLen = 16;
                        c->Request.CDB[1]= 0;
-                       c->Request.CDB[2]= (upper32 >> 24) & 0xff;      //MSB
+                       c->Request.CDB[2]= (upper32 >> 24) & 0xff; /* MSB */
                        c->Request.CDB[3]= (upper32 >> 16) & 0xff;
                        c->Request.CDB[4]= (upper32 >>  8) & 0xff;
                        c->Request.CDB[5]= upper32 & 0xff;
@@ -2898,10 +3231,10 @@ static void do_cciss_request(struct request_queue *q)
                        c->Request.CDB[7]= (start_blk >> 16) & 0xff;
                        c->Request.CDB[8]= (start_blk >>  8) & 0xff;
                        c->Request.CDB[9]= start_blk & 0xff;
-                       c->Request.CDB[10]= (creq->nr_sectors >>  24) & 0xff;
-                       c->Request.CDB[11]= (creq->nr_sectors >>  16) & 0xff;
-                       c->Request.CDB[12]= (creq->nr_sectors >>  8) & 0xff;
-                       c->Request.CDB[13]= creq->nr_sectors & 0xff;
+                       c->Request.CDB[10]= (blk_rq_sectors(creq) >> 24) & 0xff;
+                       c->Request.CDB[11]= (blk_rq_sectors(creq) >> 16) & 0xff;
+                       c->Request.CDB[12]= (blk_rq_sectors(creq) >>  8) & 0xff;
+                       c->Request.CDB[13]= blk_rq_sectors(creq) & 0xff;
                        c->Request.CDB[14] = c->Request.CDB[15] = 0;
                }
        } else if (blk_pc_request(creq)) {
@@ -2931,44 +3264,18 @@ startio:
 
 static inline unsigned long get_next_completion(ctlr_info_t *h)
 {
-#ifdef CONFIG_CISS_SCSI_TAPE
-       /* Any rejects from sendcmd() lying around? Process them first */
-       if (h->scsi_rejects.ncompletions == 0)
-               return h->access.command_completed(h);
-       else {
-               struct sendcmd_reject_list *srl;
-               int n;
-               srl = &h->scsi_rejects;
-               n = --srl->ncompletions;
-               /* printk("cciss%d: processing saved reject\n", h->ctlr); */
-               printk("p");
-               return srl->complete[n];
-       }
-#else
        return h->access.command_completed(h);
-#endif
 }
 
 static inline int interrupt_pending(ctlr_info_t *h)
 {
-#ifdef CONFIG_CISS_SCSI_TAPE
-       return (h->access.intr_pending(h)
-               || (h->scsi_rejects.ncompletions > 0));
-#else
        return h->access.intr_pending(h);
-#endif
 }
 
 static inline long interrupt_not_for_us(ctlr_info_t *h)
 {
-#ifdef CONFIG_CISS_SCSI_TAPE
-       return (((h->access.intr_pending(h) == 0) ||
-                (h->interrupts_enabled == 0))
-               && (h->scsi_rejects.ncompletions == 0));
-#else
        return (((h->access.intr_pending(h) == 0) ||
                 (h->interrupts_enabled == 0)));
-#endif
 }
 
 static irqreturn_t do_cciss_intr(int irq, void *dev_id)
@@ -3035,20 +3342,124 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+/**
+ * add_to_scan_list() - add controller to rescan queue
+ * @h:               Pointer to the controller.
+ *
+ * Adds the controller to the rescan queue if not already on the queue.
+ *
+ * returns 1 if added to the queue, 0 if skipped (could be on the
+ * queue already, or the controller could be initializing or shutting
+ * down).
+ **/
+static int add_to_scan_list(struct ctlr_info *h)
+{
+       struct ctlr_info *test_h;
+       int found = 0;
+       int ret = 0;
+
+       if (h->busy_initializing)
+               return 0;
+
+       if (!mutex_trylock(&h->busy_shutting_down))
+               return 0;
+
+       mutex_lock(&scan_mutex);
+       list_for_each_entry(test_h, &scan_q, scan_list) {
+               if (test_h == h) {
+                       found = 1;
+                       break;
+               }
+       }
+       if (!found && !h->busy_scanning) {
+               INIT_COMPLETION(h->scan_wait);
+               list_add_tail(&h->scan_list, &scan_q);
+               ret = 1;
+       }
+       mutex_unlock(&scan_mutex);
+       mutex_unlock(&h->busy_shutting_down);
+
+       return ret;
+}
+
+/**
+ * remove_from_scan_list() - remove controller from rescan queue
+ * @h:                    Pointer to the controller.
+ *
+ * Removes the controller from the rescan queue if present. Blocks if
+ * the controller is currently conducting a rescan.  The controller
+ * can be in one of three states:
+ * 1. Doesn't need a scan
+ * 2. On the scan list, but not scanning yet (we remove it)
+ * 3. Busy scanning (and not on the list). In this case we want to wait for
+ *    the scan to complete to make sure the scanning thread for this
+ *    controller is completely idle.
+ **/
+static void remove_from_scan_list(struct ctlr_info *h)
+{
+       struct ctlr_info *test_h, *tmp_h;
+
+       mutex_lock(&scan_mutex);
+       list_for_each_entry_safe(test_h, tmp_h, &scan_q, scan_list) {
+               if (test_h == h) { /* state 2. */
+                       list_del(&h->scan_list);
+                       complete_all(&h->scan_wait);
+                       mutex_unlock(&scan_mutex);
+                       return;
+               }
+       }
+       if (h->busy_scanning) { /* state 3. */
+               mutex_unlock(&scan_mutex);
+               wait_for_completion(&h->scan_wait);
+       } else { /* state 1, nothing to do. */
+               mutex_unlock(&scan_mutex);
+       }
+}
+
+/**
+ * scan_thread() - kernel thread used to rescan controllers
+ * @data:       Ignored.
+ *
+ * A kernel thread used scan for drive topology changes on
+ * controllers. The thread processes only one controller at a time
+ * using a queue.  Controllers are added to the queue using
+ * add_to_scan_list() and removed from the queue either after done
+ * processing or using remove_from_scan_list().
+ *
+ * returns 0.
+ **/
 static int scan_thread(void *data)
 {
-       ctlr_info_t *h = data;
-       int rc;
-       DECLARE_COMPLETION_ONSTACK(wait);
-       h->rescan_wait = &wait;
+       struct ctlr_info *h;
 
-       for (;;) {
-               rc = wait_for_completion_interruptible(&wait);
+       while (1) {
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule();
                if (kthread_should_stop())
                        break;
-               if (!rc)
-                       rebuild_lun_table(h, 0);
+
+               while (1) {
+                       mutex_lock(&scan_mutex);
+                       if (list_empty(&scan_q)) {
+                               mutex_unlock(&scan_mutex);
+                               break;
+                       }
+
+                       h = list_entry(scan_q.next,
+                                      struct ctlr_info,
+                                      scan_list);
+                       list_del(&h->scan_list);
+                       h->busy_scanning = 1;
+                       mutex_unlock(&scan_mutex);
+
+                       rebuild_lun_table(h, 0, 0);
+                       complete_all(&h->scan_wait);
+                       mutex_lock(&scan_mutex);
+                       h->busy_scanning = 0;
+                       mutex_unlock(&scan_mutex);
+               }
        }
+
        return 0;
 }
 
@@ -3071,8 +3482,22 @@ static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c)
        case REPORT_LUNS_CHANGED:
                printk(KERN_WARNING "cciss%d: report LUN data "
                        "changed\n", h->ctlr);
-               if (h->rescan_wait)
-                       complete(h->rescan_wait);
+       /*
+        * Here, we could call add_to_scan_list and wake up the scan thread,
+        * except that it's quite likely that we will get more than one
+        * REPORT_LUNS_CHANGED condition in quick succession, which means
+        * that those which occur after the first one will likely happen
+        * *during* the scan_thread's rescan.  And the rescan code is not
+        * robust enough to restart in the middle, undoing what it has already
+        * done, and it's not clear that it's even possible to do this, since
+        * part of what it does is notify the block layer, which starts
+        * doing it's own i/o to read partition tables and so on, and the
+        * driver doesn't have visibility to know what might need undoing.
+        * In any event, if possible, it is horribly complicated to get right
+        * so we just don't do it for now.
+        *
+        * Note: this REPORT_LUNS_CHANGED condition only occurs on the MSA2012.
+        */
                return 1;
        break;
        case POWER_OR_RESET:
@@ -3225,7 +3650,27 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
        __u64 cfg_offset;
        __u32 cfg_base_addr;
        __u64 cfg_base_addr_index;
-       int i, err;
+       int i, prod_index, err;
+
+       subsystem_vendor_id = pdev->subsystem_vendor;
+       subsystem_device_id = pdev->subsystem_device;
+       board_id = (((__u32) (subsystem_device_id << 16) & 0xffff0000) |
+                   subsystem_vendor_id);
+
+       for (i = 0; i < ARRAY_SIZE(products); i++) {
+               /* Stand aside for hpsa driver on request */
+               if (cciss_allow_hpsa && products[i].board_id == HPSA_BOUNDARY)
+                       return -ENODEV;
+               if (board_id == products[i].board_id)
+                       break;
+       }
+       prod_index = i;
+       if (prod_index == ARRAY_SIZE(products)) {
+               dev_warn(&pdev->dev,
+                       "unrecognized board ID: 0x%08lx, ignoring.\n",
+                       (unsigned long) board_id);
+               return -ENODEV;
+       }
 
        /* check to see if controller has been disabled */
        /* BEFORE trying to enable it */
@@ -3249,11 +3694,6 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
                return err;
        }
 
-       subsystem_vendor_id = pdev->subsystem_vendor;
-       subsystem_device_id = pdev->subsystem_device;
-       board_id = (((__u32) (subsystem_device_id << 16) & 0xffff0000) |
-                   subsystem_vendor_id);
-
 #ifdef CCISS_DEBUG
        printk("command = %x\n", command);
        printk("irq = %x\n", pdev->irq);
@@ -3292,7 +3732,7 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
                if (scratchpad == CCISS_FIRMWARE_READY)
                        break;
                set_current_state(TASK_INTERRUPTIBLE);
-               schedule_timeout(HZ / 10);      /* wait 100ms */
+               schedule_timeout(msecs_to_jiffies(100));        /* wait 100ms */
        }
        if (scratchpad != CCISS_FIRMWARE_READY) {
                printk(KERN_WARNING "cciss: Board not ready.  Timed out.\n");
@@ -3339,14 +3779,26 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
         * leave a little room for ioctl calls.
         */
        c->max_commands = readl(&(c->cfgtable->CmdsOutMax));
-       for (i = 0; i < ARRAY_SIZE(products); i++) {
-               if (board_id == products[i].board_id) {
-                       c->product_name = products[i].product_name;
-                       c->access = *(products[i].access);
-                       c->nr_cmds = c->max_commands - 4;
-                       break;
-               }
+       c->maxsgentries = readl(&(c->cfgtable->MaxSGElements));
+
+       /*
+        * Limit native command to 32 s/g elements to save dma'able memory.
+        * Howvever spec says if 0, use 31
+        */
+
+       c->max_cmd_sgentries = 31;
+       if (c->maxsgentries > 512) {
+               c->max_cmd_sgentries = 32;
+               c->chainsize = c->maxsgentries - c->max_cmd_sgentries + 1;
+               c->maxsgentries -= 1;   /* account for chain pointer */
+       } else {
+               c->maxsgentries = 31;   /* Default to traditional value */
+               c->chainsize = 0;       /* traditional */
        }
+
+       c->product_name = products[prod_index].product_name;
+       c->access = *(products[prod_index].access);
+       c->nr_cmds = c->max_commands - 4;
        if ((readb(&c->cfgtable->Signature[0]) != 'C') ||
            (readb(&c->cfgtable->Signature[1]) != 'I') ||
            (readb(&c->cfgtable->Signature[2]) != 'S') ||
@@ -3355,27 +3807,6 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
                err = -ENODEV;
                goto err_out_free_res;
        }
-       /* We didn't find the controller in our list. We know the
-        * signature is valid. If it's an HP device let's try to
-        * bind to the device and fire it up. Otherwise we bail.
-        */
-       if (i == ARRAY_SIZE(products)) {
-               if (subsystem_vendor_id == PCI_VENDOR_ID_HP) {
-                       c->product_name = products[i-1].product_name;
-                       c->access = *(products[i-1].access);
-                       c->nr_cmds = c->max_commands - 4;
-                       printk(KERN_WARNING "cciss: This is an unknown "
-                               "Smart Array controller.\n"
-                               "cciss: Please update to the latest driver "
-                               "available from www.hp.com.\n");
-               } else {
-                       printk(KERN_WARNING "cciss: Sorry, I don't know how"
-                               " to access the Smart Array controller %08lx\n"
-                                       , (unsigned long)board_id);
-                       err = -ENODEV;
-                       goto err_out_free_res;
-               }
-       }
 #ifdef CONFIG_X86
        {
                /* Need to enable prefetch in the SCSI core for 6400 in x86 */
@@ -3418,7 +3849,7 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
                        break;
                /* delay and try again */
                set_current_state(TASK_INTERRUPTIBLE);
-               schedule_timeout(10);
+               schedule_timeout(msecs_to_jiffies(1));
        }
 
 #ifdef CCISS_DEBUG
@@ -3472,15 +3903,16 @@ Enomem:
        return -1;
 }
 
-static void free_hba(int i)
+static void free_hba(int n)
 {
-       ctlr_info_t *p = hba[i];
-       int n;
+       ctlr_info_t *h = hba[n];
+       int i;
 
-       hba[i] = NULL;
-       for (n = 0; n < CISS_MAX_LUN; n++)
-               put_disk(p->gendisk[n]);
-       kfree(p);
+       hba[n] = NULL;
+       for (i = 0; i < h->highest_lun + 1; i++)
+               if (h->gendisk[i] != NULL)
+                       put_disk(h->gendisk[i]);
+       kfree(h);
 }
 
 /* Send a message CDB to the firmware. */
@@ -3690,9 +4122,10 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
 {
        int i;
        int j = 0;
+       int k = 0;
        int rc;
        int dac, return_code;
-       InquiryData_struct *inq_buff = NULL;
+       InquiryData_struct *inq_buff;
 
        if (reset_devices) {
                /* Reset the controller with a PCI power-cycle */
@@ -3721,14 +4154,20 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
        hba[i]->busy_initializing = 1;
        INIT_HLIST_HEAD(&hba[i]->cmpQ);
        INIT_HLIST_HEAD(&hba[i]->reqQ);
+       mutex_init(&hba[i]->busy_shutting_down);
 
        if (cciss_pci_init(hba[i], pdev) != 0)
-               goto clean1;
+               goto clean_no_release_regions;
 
        sprintf(hba[i]->devname, "cciss%d", i);
        hba[i]->ctlr = i;
        hba[i]->pdev = pdev;
 
+       init_completion(&hba[i]->scan_wait);
+
+       if (cciss_create_hba_sysfs_entry(hba[i]))
+               goto clean0;
+
        /* configure PCI DMA stuff */
        if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64)))
                dac = 1;
@@ -3787,15 +4226,53 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
                printk(KERN_ERR "cciss: out of memory");
                goto clean4;
        }
-#ifdef CONFIG_CISS_SCSI_TAPE
-       hba[i]->scsi_rejects.complete =
-           kmalloc(sizeof(hba[i]->scsi_rejects.complete[0]) *
-                   (hba[i]->nr_cmds + 5), GFP_KERNEL);
-       if (hba[i]->scsi_rejects.complete == NULL) {
-               printk(KERN_ERR "cciss: out of memory");
+
+       /* Need space for temp scatter list */
+       hba[i]->scatter_list = kmalloc(hba[i]->max_commands *
+                                               sizeof(struct scatterlist *),
+                                               GFP_KERNEL);
+       for (k = 0; k < hba[i]->nr_cmds; k++) {
+               hba[i]->scatter_list[k] = kmalloc(sizeof(struct scatterlist) *
+                                                       hba[i]->maxsgentries,
+                                                       GFP_KERNEL);
+               if (hba[i]->scatter_list[k] == NULL) {
+                       printk(KERN_ERR "cciss%d: could not allocate "
+                               "s/g lists\n", i);
+                       goto clean4;
+               }
+       }
+       hba[i]->cmd_sg_list = kmalloc(sizeof(struct Cmd_sg_list *) *
+                                               hba[i]->nr_cmds,
+                                               GFP_KERNEL);
+       if (!hba[i]->cmd_sg_list) {
+               printk(KERN_ERR "cciss%d: Cannot get memory for "
+                       "s/g chaining.\n", i);
                goto clean4;
        }
-#endif
+       /* Build up chain blocks for each command */
+       if (hba[i]->chainsize > 0) {
+               for (j = 0; j < hba[i]->nr_cmds; j++) {
+                       hba[i]->cmd_sg_list[j] =
+                                       kmalloc(sizeof(struct Cmd_sg_list),
+                                                       GFP_KERNEL);
+                       if (!hba[i]->cmd_sg_list[j]) {
+                               printk(KERN_ERR "cciss%d: Cannot get memory "
+                                       "for chain block.\n", i);
+                               goto clean4;
+                       }
+                       /* Need a block of chainsized s/g elements. */
+                       hba[i]->cmd_sg_list[j]->sgchain =
+                                       kmalloc((hba[i]->chainsize *
+                                               sizeof(SGDescriptor_struct)),
+                                               GFP_KERNEL);
+                       if (!hba[i]->cmd_sg_list[j]->sgchain) {
+                               printk(KERN_ERR "cciss%d: Cannot get memory "
+                                       "for s/g chains\n", i);
+                               goto clean4;
+                       }
+               }
+       }
+
        spin_lock_init(&hba[i]->lock);
 
        /* Initialize the pdev driver private data.
@@ -3810,8 +4287,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
        hba[i]->num_luns = 0;
        hba[i]->highest_lun = -1;
        for (j = 0; j < CISS_MAX_LUN; j++) {
-               hba[i]->drv[j].raid_level = -1;
-               hba[i]->drv[j].queue = NULL;
+               hba[i]->drv[j] = NULL;
                hba[i]->gendisk[j] = NULL;
        }
 
@@ -3828,7 +4304,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
        }
 
        return_code = sendcmd_withirq(CISS_INQUIRY, i, inq_buff,
-               sizeof(InquiryData_struct), 0, 0 , 0, TYPE_CMD);
+               sizeof(InquiryData_struct), 0, CTLR_LUNID, TYPE_CMD);
        if (return_code == IO_OK) {
                hba[i]->firm_ver[0] = inq_buff->data_byte[32];
                hba[i]->firm_ver[1] = inq_buff->data_byte[33];
@@ -3838,27 +4314,32 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
                printk(KERN_WARNING "cciss: unable to determine firmware"
                        " version of controller\n");
        }
+       kfree(inq_buff);
 
        cciss_procinit(i);
 
-       hba[i]->cciss_max_sectors = 2048;
+       hba[i]->cciss_max_sectors = 8192;
 
+       rebuild_lun_table(hba[i], 1, 0);
        hba[i]->busy_initializing = 0;
-
-       rebuild_lun_table(hba[i], 1);
-       hba[i]->cciss_scan_thread = kthread_run(scan_thread, hba[i],
-                               "cciss_scan%02d", i);
-       if (IS_ERR(hba[i]->cciss_scan_thread))
-               return PTR_ERR(hba[i]->cciss_scan_thread);
-
        return 1;
 
 clean4:
-       kfree(inq_buff);
-#ifdef CONFIG_CISS_SCSI_TAPE
-       kfree(hba[i]->scsi_rejects.complete);
-#endif
        kfree(hba[i]->cmd_pool_bits);
+       /* Free up sg elements */
+       for (k = 0; k < hba[i]->nr_cmds; k++)
+               kfree(hba[i]->scatter_list[k]);
+       kfree(hba[i]->scatter_list);
+       /* Only free up extra s/g lists if controller supports them */
+       if (hba[i]->chainsize > 0) {
+               for (j = 0; j < hba[i]->nr_cmds; j++) {
+                       if (hba[i]->cmd_sg_list[j]) {
+                               kfree(hba[i]->cmd_sg_list[j]->sgchain);
+                               kfree(hba[i]->cmd_sg_list[j]);
+                       }
+               }
+               kfree(hba[i]->cmd_sg_list);
+       }
        if (hba[i]->cmd_pool)
                pci_free_consistent(hba[i]->pdev,
                                    hba[i]->nr_cmds * sizeof(CommandList_struct),
@@ -3872,18 +4353,16 @@ clean4:
 clean2:
        unregister_blkdev(hba[i]->major, hba[i]->devname);
 clean1:
+       cciss_destroy_hba_sysfs_entry(hba[i]);
+clean0:
+       pci_release_regions(pdev);
+clean_no_release_regions:
        hba[i]->busy_initializing = 0;
-       /* cleanup any queues that may have been initialized */
-       for (j=0; j <= hba[i]->highest_lun; j++){
-               drive_info_struct *drv = &(hba[i]->drv[j]);
-               if (drv->queue)
-                       blk_cleanup_queue(drv->queue);
-       }
+
        /*
         * Deliberately omit pci_disable_device(): it does something nasty to
         * Smart Array controllers that pci_enable_device does not undo
         */
-       pci_release_regions(pdev);
        pci_set_drvdata(pdev, NULL);
        free_hba(i);
        return -1;
@@ -3891,30 +4370,28 @@ clean1:
 
 static void cciss_shutdown(struct pci_dev *pdev)
 {
-       ctlr_info_t *tmp_ptr;
-       int i;
-       char flush_buf[4];
+       ctlr_info_t *h;
+       char *flush_buf;
        int return_code;
 
-       tmp_ptr = pci_get_drvdata(pdev);
-       if (tmp_ptr == NULL)
-               return;
-       i = tmp_ptr->ctlr;
-       if (hba[i] == NULL)
+       h = pci_get_drvdata(pdev);
+       flush_buf = kzalloc(4, GFP_KERNEL);
+       if (!flush_buf) {
+               printk(KERN_WARNING
+                       "cciss:%d cache not flushed, out of memory.\n",
+                       h->ctlr);
                return;
-
-       /* Turn board interrupts off  and send the flush cache command */
-       /* sendcmd will turn off interrupt, and send the flush...
-        * To write all data in the battery backed cache to disks */
-       memset(flush_buf, 0, 4);
-       return_code = sendcmd(CCISS_CACHE_FLUSH, i, flush_buf, 4, 0, 0, 0, NULL,
-                             TYPE_CMD);
-       if (return_code == IO_OK) {
-               printk(KERN_INFO "Completed flushing cache on controller %d\n", i);
-       } else {
-               printk(KERN_WARNING "Error flushing cache on controller %d\n", i);
        }
-       free_irq(hba[i]->intr[2], hba[i]);
+       /* write all data in the battery backed cache to disk */
+       memset(flush_buf, 0, 4);
+       return_code = sendcmd_withirq(CCISS_CACHE_FLUSH, h->ctlr, flush_buf,
+               4, 0, CTLR_LUNID, TYPE_CMD);
+       kfree(flush_buf);
+       if (return_code != IO_OK)
+               printk(KERN_WARNING "cciss%d: Error flushing cache\n",
+                       h->ctlr);
+       h->access.set_intr_mask(h, CCISS_INTR_OFF);
+       free_irq(h->intr[2], h);
 }
 
 static void __devexit cciss_remove_one(struct pci_dev *pdev)
@@ -3935,8 +4412,9 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev)
                return;
        }
 
-       kthread_stop(hba[i]->cciss_scan_thread);
+       mutex_lock(&hba[i]->busy_shutting_down);
 
+       remove_from_scan_list(hba[i]);
        remove_proc_entry(hba[i]->devname, proc_cciss);
        unregister_blkdev(hba[i]->major, hba[i]->devname);
 
@@ -3946,8 +4424,10 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev)
                if (disk) {
                        struct request_queue *q = disk->queue;
 
-                       if (disk->flags & GENHD_FL_UP)
+                       if (disk->flags & GENHD_FL_UP) {
+                               cciss_destroy_ld_sysfs_entry(hba[i], j, 1);
                                del_gendisk(disk);
+                       }
                        if (q)
                                blk_cleanup_queue(q);
                }
@@ -3973,15 +4453,28 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev)
        pci_free_consistent(hba[i]->pdev, hba[i]->nr_cmds * sizeof(ErrorInfo_struct),
                            hba[i]->errinfo_pool, hba[i]->errinfo_pool_dhandle);
        kfree(hba[i]->cmd_pool_bits);
-#ifdef CONFIG_CISS_SCSI_TAPE
-       kfree(hba[i]->scsi_rejects.complete);
-#endif
+       /* Free up sg elements */
+       for (j = 0; j < hba[i]->nr_cmds; j++)
+               kfree(hba[i]->scatter_list[j]);
+       kfree(hba[i]->scatter_list);
+       /* Only free up extra s/g lists if controller supports them */
+       if (hba[i]->chainsize > 0) {
+               for (j = 0; j < hba[i]->nr_cmds; j++) {
+                       if (hba[i]->cmd_sg_list[j]) {
+                               kfree(hba[i]->cmd_sg_list[j]->sgchain);
+                               kfree(hba[i]->cmd_sg_list[j]);
+                       }
+               }
+               kfree(hba[i]->cmd_sg_list);
+       }
        /*
         * Deliberately omit pci_disable_device(): it does something nasty to
         * Smart Array controllers that pci_enable_device does not undo
         */
        pci_release_regions(pdev);
        pci_set_drvdata(pdev, NULL);
+       cciss_destroy_hba_sysfs_entry(hba[i]);
+       mutex_unlock(&hba[i]->busy_shutting_down);
        free_hba(i);
 }
 
@@ -3999,6 +4492,8 @@ static struct pci_driver cciss_pci_driver = {
  */
 static int __init cciss_init(void)
 {
+       int err;
+
        /*
         * The hardware requires that commands are aligned on a 64-bit
         * boundary. Given that we use pci_alloc_consistent() to allocate an
@@ -4008,8 +4503,30 @@ static int __init cciss_init(void)
 
        printk(KERN_INFO DRIVER_NAME "\n");
 
+       err = bus_register(&cciss_bus_type);
+       if (err)
+               return err;
+
+       /* Start the scan thread */
+       cciss_scan_thread = kthread_run(scan_thread, NULL, "cciss_scan");
+       if (IS_ERR(cciss_scan_thread)) {
+               err = PTR_ERR(cciss_scan_thread);
+               goto err_bus_unregister;
+       }
+
        /* Register for our PCI devices */
-       return pci_register_driver(&cciss_pci_driver);
+       err = pci_register_driver(&cciss_pci_driver);
+       if (err)
+               goto err_thread_stop;
+
+       return err;
+
+err_thread_stop:
+       kthread_stop(cciss_scan_thread);
+err_bus_unregister:
+       bus_unregister(&cciss_bus_type);
+
+       return err;
 }
 
 static void __exit cciss_cleanup(void)
@@ -4025,7 +4542,9 @@ static void __exit cciss_cleanup(void)
                        cciss_remove_one(hba[i]->pdev);
                }
        }
+       kthread_stop(cciss_scan_thread);
        remove_proc_entry("driver/cciss", NULL);
+       bus_unregister(&cciss_bus_type);
 }
 
 static void fail_all_cmds(unsigned long ctlr)
@@ -4054,7 +4573,8 @@ static void fail_all_cmds(unsigned long ctlr)
        while (!hlist_empty(&h->cmpQ)) {
                c = hlist_entry(h->cmpQ.first, CommandList_struct, list);
                removeQ(c);
-               c->err_info->CommandStatus = CMD_HARDWARE_ERR;
+               if (c->cmd_type != CMD_MSG_STALE)
+                       c->err_info->CommandStatus = CMD_HARDWARE_ERR;
                if (c->cmd_type == CMD_RWREQ) {
                        complete_command(h, c, 0);
                } else if (c->cmd_type == CMD_IOCTL_PEND)