Merge branches 'cxgb3' and 'mlx4' into for-linus
[safe/jmp/linux-2.6] / drivers / block / cciss.c
index 781b745..4d4d5e0 100644 (file)
@@ -51,6 +51,7 @@
 #include <scsi/scsi_ioctl.h>
 #include <linux/cdrom.h>
 #include <linux/scatterlist.h>
+#include <linux/kthread.h>
 
 #define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin))
 #define DRIVER_NAME "HP CISS Driver (v 3.6.20)"
@@ -96,6 +97,8 @@ static const struct pci_device_id cciss_pci_device_id[] = {
        {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSE,     0x103C, 0x3245},
        {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSE,     0x103C, 0x3247},
        {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,}
@@ -133,6 +136,8 @@ static struct board_type products[] = {
        {0x3245103C, "Smart Array P410i", &SA5_access},
        {0x3247103C, "Smart Array P411", &SA5_access},
        {0x3249103C, "Smart Array P812", &SA5_access},
+       {0x324A103C, "Smart Array P712m", &SA5_access},
+       {0x324B103C, "Smart Array P711m", &SA5_access},
        {0xFFFF103C, "Unknown Smart Array", &SA5_access},
 };
 
@@ -152,15 +157,15 @@ static ctlr_info_t *hba[MAX_CTLR];
 
 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 inode *inode, struct file *filep);
-static int cciss_release(struct inode *inode, struct file *filep);
-static int cciss_ioctl(struct inode *inode, struct file *filep,
+static int cciss_open(struct block_device *bdev, fmode_t mode);
+static int cciss_release(struct gendisk *disk, fmode_t mode);
+static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
                       unsigned int cmd, unsigned long arg);
 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 deregister_disk(struct gendisk *disk, drive_info_struct *drv,
+static int deregister_disk(ctlr_info_t *h, int drv_index,
                           int clear_all);
 
 static void cciss_read_capacity(int ctlr, int logvol, int withirq,
@@ -182,6 +187,8 @@ static int sendcmd_withirq(__u8 cmd, int ctlr, void *buff, size_t size,
                           __u8 page_code, int cmd_type);
 
 static void fail_all_cmds(unsigned long ctlr);
+static int scan_thread(void *data);
+static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c);
 
 #ifdef CONFIG_PROC_FS
 static void cciss_procinit(int i);
@@ -192,17 +199,18 @@ static void cciss_procinit(int i)
 #endif                         /* CONFIG_PROC_FS */
 
 #ifdef CONFIG_COMPAT
-static long cciss_compat_ioctl(struct file *f, unsigned cmd, unsigned long arg);
+static int cciss_compat_ioctl(struct block_device *, fmode_t,
+                             unsigned, unsigned long);
 #endif
 
 static struct block_device_operations cciss_fops = {
        .owner = THIS_MODULE,
-       .__open = cciss_open,
-       .__release = cciss_release,
-       .__ioctl = cciss_ioctl,
+       .open = cciss_open,
+       .release = cciss_release,
+       .locked_ioctl = cciss_ioctl,
        .getgeo = cciss_getgeo,
 #ifdef CONFIG_COMPAT
-       .__compat_ioctl = cciss_compat_ioctl,
+       .compat_ioctl = cciss_compat_ioctl,
 #endif
        .revalidate_disk = cciss_revalidate,
 };
@@ -210,31 +218,17 @@ static struct block_device_operations cciss_fops = {
 /*
  * Enqueuing and dequeuing functions for cmdlists.
  */
-static inline void addQ(CommandList_struct **Qptr, CommandList_struct *c)
+static inline void addQ(struct hlist_head *list, CommandList_struct *c)
 {
-       if (*Qptr == NULL) {
-               *Qptr = c;
-               c->next = c->prev = c;
-       } else {
-               c->prev = (*Qptr)->prev;
-               c->next = (*Qptr);
-               (*Qptr)->prev->next = c;
-               (*Qptr)->prev = c;
-       }
+       hlist_add_head(&c->list, list);
 }
 
-static inline CommandList_struct *removeQ(CommandList_struct **Qptr,
-                                         CommandList_struct *c)
+static inline void removeQ(CommandList_struct *c)
 {
-       if (c && c->next != c) {
-               if (*Qptr == c)
-                       *Qptr = c->next;
-               c->prev->next = c->next;
-               c->next->prev = c->prev;
-       } else {
-               *Qptr = NULL;
-       }
-       return c;
+       if (WARN_ON(hlist_unhashed(&c->list)))
+               return;
+
+       hlist_del_init(&c->list);
 }
 
 #include "cciss_scsi.c"                /* For SCSI tape support */
@@ -501,6 +495,7 @@ static CommandList_struct *cmd_alloc(ctlr_info_t *h, int get_from_pool)
                c->cmdindex = i;
        }
 
+       INIT_HLIST_NODE(&c->list);
        c->busaddr = (__u32) cmd_dma_handle;
        temp64.val = (__u64) err_dma_handle;
        c->ErrDesc.Addr.lower = temp64.val32.lower;
@@ -547,13 +542,13 @@ static inline drive_info_struct *get_drv(struct gendisk *disk)
 /*
  * Open.  Make sure the device is really there.
  */
-static int cciss_open(struct inode *inode, struct file *filep)
+static int cciss_open(struct block_device *bdev, fmode_t mode)
 {
-       ctlr_info_t *host = get_host(inode->i_bdev->bd_disk);
-       drive_info_struct *drv = get_drv(inode->i_bdev->bd_disk);
+       ctlr_info_t *host = get_host(bdev->bd_disk);
+       drive_info_struct *drv = get_drv(bdev->bd_disk);
 
 #ifdef CCISS_DEBUG
-       printk(KERN_DEBUG "cciss_open %s\n", inode->i_bdev->bd_disk->disk_name);
+       printk(KERN_DEBUG "cciss_open %s\n", bdev->bd_disk->disk_name);
 #endif                         /* CCISS_DEBUG */
 
        if (host->busy_initializing || drv->busy_configuring)
@@ -567,9 +562,9 @@ static int cciss_open(struct inode *inode, struct file *filep)
         * for "raw controller".
         */
        if (drv->heads == 0) {
-               if (iminor(inode) != 0) {       /* not node 0? */
+               if (MINOR(bdev->bd_dev) != 0) { /* not node 0? */
                        /* if not node 0 make sure it is a partition = 0 */
-                       if (iminor(inode) & 0x0f) {
+                       if (MINOR(bdev->bd_dev) & 0x0f) {
                                return -ENXIO;
                                /* if it is, make sure we have a LUN ID */
                        } else if (drv->LunID == 0) {
@@ -587,14 +582,13 @@ static int cciss_open(struct inode *inode, struct file *filep)
 /*
  * Close.  Sync first.
  */
-static int cciss_release(struct inode *inode, struct file *filep)
+static int cciss_release(struct gendisk *disk, fmode_t mode)
 {
-       ctlr_info_t *host = get_host(inode->i_bdev->bd_disk);
-       drive_info_struct *drv = get_drv(inode->i_bdev->bd_disk);
+       ctlr_info_t *host = get_host(disk);
+       drive_info_struct *drv = get_drv(disk);
 
 #ifdef CCISS_DEBUG
-       printk(KERN_DEBUG "cciss_release %s\n",
-              inode->i_bdev->bd_disk->disk_name);
+       printk(KERN_DEBUG "cciss_release %s\n", disk->disk_name);
 #endif                         /* CCISS_DEBUG */
 
        drv->usage_count--;
@@ -604,21 +598,23 @@ static int cciss_release(struct inode *inode, struct file *filep)
 
 #ifdef CONFIG_COMPAT
 
-static int do_ioctl(struct file *f, unsigned cmd, unsigned long arg)
+static int do_ioctl(struct block_device *bdev, fmode_t mode,
+                   unsigned cmd, unsigned long arg)
 {
        int ret;
        lock_kernel();
-       ret = cciss_ioctl(f->f_path.dentry->d_inode, f, cmd, arg);
+       ret = cciss_ioctl(bdev, mode, cmd, arg);
        unlock_kernel();
        return ret;
 }
 
-static int cciss_ioctl32_passthru(struct file *f, unsigned cmd,
-                                 unsigned long arg);
-static int cciss_ioctl32_big_passthru(struct file *f, unsigned cmd,
-                                     unsigned long arg);
+static int cciss_ioctl32_passthru(struct block_device *bdev, fmode_t mode,
+                                 unsigned cmd, unsigned long arg);
+static int cciss_ioctl32_big_passthru(struct block_device *bdev, fmode_t mode,
+                                     unsigned cmd, unsigned long arg);
 
-static long cciss_compat_ioctl(struct file *f, unsigned cmd, unsigned long arg)
+static int cciss_compat_ioctl(struct block_device *bdev, fmode_t mode,
+                             unsigned cmd, unsigned long arg)
 {
        switch (cmd) {
        case CCISS_GETPCIINFO:
@@ -636,20 +632,20 @@ static long cciss_compat_ioctl(struct file *f, unsigned cmd, unsigned long arg)
        case CCISS_REGNEWD:
        case CCISS_RESCANDISK:
        case CCISS_GETLUNINFO:
-               return do_ioctl(f, cmd, arg);
+               return do_ioctl(bdev, mode, cmd, arg);
 
        case CCISS_PASSTHRU32:
-               return cciss_ioctl32_passthru(f, cmd, arg);
+               return cciss_ioctl32_passthru(bdev, mode, cmd, arg);
        case CCISS_BIG_PASSTHRU32:
-               return cciss_ioctl32_big_passthru(f, cmd, arg);
+               return cciss_ioctl32_big_passthru(bdev, mode, cmd, arg);
 
        default:
                return -ENOIOCTLCMD;
        }
 }
 
-static int cciss_ioctl32_passthru(struct file *f, unsigned cmd,
-                                 unsigned long arg)
+static int cciss_ioctl32_passthru(struct block_device *bdev, fmode_t mode,
+                                 unsigned cmd, unsigned long arg)
 {
        IOCTL32_Command_struct __user *arg32 =
            (IOCTL32_Command_struct __user *) arg;
@@ -676,7 +672,7 @@ static int cciss_ioctl32_passthru(struct file *f, unsigned cmd,
        if (err)
                return -EFAULT;
 
-       err = do_ioctl(f, CCISS_PASSTHRU, (unsigned long)p);
+       err = do_ioctl(bdev, mode, CCISS_PASSTHRU, (unsigned long)p);
        if (err)
                return err;
        err |=
@@ -687,8 +683,8 @@ static int cciss_ioctl32_passthru(struct file *f, unsigned cmd,
        return err;
 }
 
-static int cciss_ioctl32_big_passthru(struct file *file, unsigned cmd,
-                                     unsigned long arg)
+static int cciss_ioctl32_big_passthru(struct block_device *bdev, fmode_t mode,
+                                     unsigned cmd, unsigned long arg)
 {
        BIG_IOCTL32_Command_struct __user *arg32 =
            (BIG_IOCTL32_Command_struct __user *) arg;
@@ -717,7 +713,7 @@ static int cciss_ioctl32_big_passthru(struct file *file, unsigned cmd,
        if (err)
                return -EFAULT;
 
-       err = do_ioctl(file, CCISS_BIG_PASSTHRU, (unsigned long)p);
+       err = do_ioctl(bdev, mode, CCISS_BIG_PASSTHRU, (unsigned long)p);
        if (err)
                return err;
        err |=
@@ -742,13 +738,18 @@ static int cciss_getgeo(struct block_device *bdev, struct hd_geometry *geo)
        return 0;
 }
 
+static void check_ioctl_unit_attention(ctlr_info_t *host, CommandList_struct *c)
+{
+       if (c->err_info->CommandStatus == CMD_TARGET_STATUS &&
+                       c->err_info->ScsiStatus != SAM_STAT_CHECK_CONDITION)
+               (void)check_for_unit_attention(host, c);
+}
 /*
  * ioctl
  */
-static int cciss_ioctl(struct inode *inode, struct file *filep,
+static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
                       unsigned int cmd, unsigned long arg)
 {
-       struct block_device *bdev = inode->i_bdev;
        struct gendisk *disk = bdev->bd_disk;
        ctlr_info_t *host = get_host(disk);
        drive_info_struct *drv = get_drv(disk);
@@ -1037,6 +1038,8 @@ static int cciss_ioctl(struct inode *inode, struct file *filep,
                                         iocommand.buf_size,
                                         PCI_DMA_BIDIRECTIONAL);
 
+                       check_ioctl_unit_attention(host, c);
+
                        /* Copy the error information out */
                        iocommand.error_info = *(c->err_info);
                        if (copy_to_user
@@ -1188,6 +1191,7 @@ static int cciss_ioctl(struct inode *inode, struct file *filep,
                                        (dma_addr_t) temp64.val, buff_size[i],
                                        PCI_DMA_BIDIRECTIONAL);
                        }
+                       check_ioctl_unit_attention(host, c);
                        /* Copy the error information out */
                        ioc->error_info = *(c->err_info);
                        if (copy_to_user(argp, ioc, sizeof(*ioc))) {
@@ -1232,8 +1236,7 @@ static int cciss_ioctl(struct inode *inode, struct file *filep,
        case SG_EMULATED_HOST:
        case SG_IO:
        case SCSI_IOCTL_SEND_COMMAND:
-               return scsi_cmd_ioctl(disk->queue, disk,
-                                     filep ? filep->f_mode : 0, cmd, argp);
+               return scsi_cmd_ioctl(disk->queue, disk, mode, cmd, argp);
 
        /* scsi_cmd_ioctl would normally handle these, below, but */
        /* they aren't a good fit for cciss, as CD-ROMs are */
@@ -1296,6 +1299,7 @@ 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;
        unsigned long flags;
        u64bit temp64;
        int i, ddir;
@@ -1317,7 +1321,14 @@ static void cciss_softirq_done(struct request *rq)
        printk("Done with %p\n", rq);
 #endif                         /* CCISS_DEBUG */
 
-       if (blk_end_request(rq, (rq->errors == 0) ? 0 : -EIO, blk_rq_bytes(rq)))
+       /*
+        * Store the full size and set the residual count for pc requests
+        */
+       nr_bytes = blk_rq_bytes(rq);
+       if (blk_pc_request(rq))
+               rq->data_len = cmd->err_info->ResidualCnt;
+
+       if (blk_end_request(rq, (rq->errors == 0) ? 0 : -EIO, nr_bytes))
                BUG();
 
        spin_lock_irqsave(&h->lock, flags);
@@ -1366,6 +1377,7 @@ static void cciss_add_disk(ctlr_info_t *h, struct gendisk *disk,
        disk->first_minor = drv_index << NWD_SHIFT;
        disk->fops = &cciss_fops;
        disk->private_data = &h->drv[drv_index];
+       disk->driverfs_dev = &h->pdev->dev;
 
        /* Set up queue information */
        blk_queue_bounce_limit(disk->queue, h->pdev->dma_mask);
@@ -1487,8 +1499,7 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time)
                 * which keeps the interrupt handler from starting
                 * the queue.
                 */
-               ret = deregister_disk(h->gendisk[drv_index],
-                                     &h->drv[drv_index], 0);
+               ret = deregister_disk(h, drv_index, 0);
                h->drv[drv_index].busy_configuring = 0;
        }
 
@@ -1688,6 +1699,11 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time)
        for (i = 0; i <= h->highest_lun; i++) {
                int j;
                drv_found = 0;
+
+               /* skip holes in the array from already deleted drives */
+               if (h->drv[i].raid_level == -1)
+                       continue;
+
                for (j = 0; j < num_luns; j++) {
                        memcpy(&lunid, &ld_buff->LUN[j][0], 4);
                        lunid = le32_to_cpu(lunid);
@@ -1701,8 +1717,7 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time)
                        spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
                        h->drv[i].busy_configuring = 1;
                        spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
-                       return_code = deregister_disk(h->gendisk[i],
-                               &h->drv[i], 1);
+                       return_code = deregister_disk(h, i, 1);
                        h->drv[i].busy_configuring = 0;
                }
        }
@@ -1772,15 +1787,19 @@ mem_msg:
  *             the highest_lun should be left unchanged and the LunID
  *             should not be cleared.
 */
-static int deregister_disk(struct gendisk *disk, drive_info_struct *drv,
+static int deregister_disk(ctlr_info_t *h, int drv_index,
                           int clear_all)
 {
        int i;
-       ctlr_info_t *h = get_host(disk);
+       struct gendisk *disk;
+       drive_info_struct *drv;
 
        if (!capable(CAP_SYS_RAWIO))
                return -EPERM;
 
+       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)
@@ -2538,7 +2557,8 @@ static void start_io(ctlr_info_t *h)
 {
        CommandList_struct *c;
 
-       while ((c = h->reqQ) != NULL) {
+       while (!hlist_empty(&h->reqQ)) {
+               c = hlist_entry(h->reqQ.first, CommandList_struct, list);
                /* can't do anything if fifo is full */
                if ((h->access.fifo_full(h))) {
                        printk(KERN_WARNING "cciss: fifo full\n");
@@ -2546,14 +2566,14 @@ static void start_io(ctlr_info_t *h)
                }
 
                /* Get the first entry from the Request Q */
-               removeQ(&(h->reqQ), c);
+               removeQ(c);
                h->Qdepth--;
 
                /* Tell the controller execute command */
                h->access.submit_command(h, c);
 
                /* Put job onto the completed Q */
-               addQ(&(h->cmpQ), c);
+               addQ(&h->cmpQ, c);
        }
 }
 
@@ -2566,7 +2586,7 @@ static inline void resend_cciss_cmd(ctlr_info_t *h, CommandList_struct *c)
        memset(c->err_info, 0, sizeof(ErrorInfo_struct));
 
        /* add it to software queue and then send it to the controller */
-       addQ(&(h->reqQ), c);
+       addQ(&h->reqQ, c);
        h->Qdepth++;
        if (h->Qdepth > h->maxQsinceinit)
                h->maxQsinceinit = h->Qdepth;
@@ -2585,12 +2605,14 @@ static inline unsigned int make_status_bytes(unsigned int scsi_status_byte,
                ((driver_byte & 0xff) << 24);
 }
 
-static inline int evaluate_target_status(CommandList_struct *cmd)
+static inline int evaluate_target_status(ctlr_info_t *h,
+                       CommandList_struct *cmd, int *retry_cmd)
 {
        unsigned char sense_key;
        unsigned char status_byte, msg_byte, host_byte, driver_byte;
        int error_value;
 
+       *retry_cmd = 0;
        /* If we get in here, it means we got "target status", that is, scsi status */
        status_byte = cmd->err_info->ScsiStatus;
        driver_byte = DRIVER_OK;
@@ -2618,6 +2640,11 @@ static inline int evaluate_target_status(CommandList_struct *cmd)
        if (((sense_key == 0x0) || (sense_key == 0x1)) && !blk_pc_request(cmd->rq))
                error_value = 0;
 
+       if (check_for_unit_attention(h, cmd)) {
+               *retry_cmd = !blk_pc_request(cmd->rq);
+               return 0;
+       }
+
        if (!blk_pc_request(cmd->rq)) { /* Not SG_IO or similar? */
                if (error_value != 0)
                        printk(KERN_WARNING "cciss: cmd %p has CHECK CONDITION"
@@ -2657,7 +2684,7 @@ static inline void complete_command(ctlr_info_t *h, CommandList_struct *cmd,
 
        switch (cmd->err_info->CommandStatus) {
        case CMD_TARGET_STATUS:
-               rq->errors = evaluate_target_status(cmd);
+               rq->errors = evaluate_target_status(h, cmd, &retry_cmd);
                break;
        case CMD_DATA_UNDERRUN:
                if (blk_fs_request(cmd->rq)) {
@@ -2842,7 +2869,7 @@ static void do_cciss_request(struct request_queue *q)
                h->maxSG = seg;
 
 #ifdef CCISS_DEBUG
-       printk(KERN_DEBUG "cciss: Submitting %d sectors in %d segments\n",
+       printk(KERN_DEBUG "cciss: Submitting %lu sectors in %d segments\n",
               creq->nr_sectors, seg);
 #endif                         /* CCISS_DEBUG */
 
@@ -2887,7 +2914,7 @@ static void do_cciss_request(struct request_queue *q)
 
        spin_lock_irq(q->queue_lock);
 
-       addQ(&(h->reqQ), c);
+       addQ(&h->reqQ, c);
        h->Qdepth++;
        if (h->Qdepth > h->maxQsinceinit)
                h->maxQsinceinit = h->Qdepth;
@@ -2975,16 +3002,12 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id)
                                a = c->busaddr;
 
                        } else {
+                               struct hlist_node *tmp;
+
                                a &= ~3;
-                               if ((c = h->cmpQ) == NULL) {
-                                       printk(KERN_WARNING
-                                              "cciss: Completion of %08x ignored\n",
-                                              a1);
-                                       continue;
-                               }
-                               while (c->busaddr != a) {
-                                       c = c->next;
-                                       if (c == h->cmpQ)
+                               c = NULL;
+                               hlist_for_each_entry(c, tmp, &h->cmpQ, list) {
+                                       if (c->busaddr == a)
                                                break;
                                }
                        }
@@ -2992,8 +3015,8 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id)
                         * If we've found the command, take it off the
                         * completion Q and free it
                         */
-                       if (c->busaddr == a) {
-                               removeQ(&h->cmpQ, c);
+                       if (c && c->busaddr == a) {
+                               removeQ(c);
                                if (c->cmd_type == CMD_RWREQ) {
                                        complete_command(h, c, 0);
                                } else if (c->cmd_type == CMD_IOCTL_PEND) {
@@ -3012,6 +3035,63 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+static int scan_thread(void *data)
+{
+       ctlr_info_t *h = data;
+       int rc;
+       DECLARE_COMPLETION_ONSTACK(wait);
+       h->rescan_wait = &wait;
+
+       for (;;) {
+               rc = wait_for_completion_interruptible(&wait);
+               if (kthread_should_stop())
+                       break;
+               if (!rc)
+                       rebuild_lun_table(h, 0);
+       }
+       return 0;
+}
+
+static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c)
+{
+       if (c->err_info->SenseInfo[2] != UNIT_ATTENTION)
+               return 0;
+
+       switch (c->err_info->SenseInfo[12]) {
+       case STATE_CHANGED:
+               printk(KERN_WARNING "cciss%d: a state change "
+                       "detected, command retried\n", h->ctlr);
+               return 1;
+       break;
+       case LUN_FAILED:
+               printk(KERN_WARNING "cciss%d: LUN failure "
+                       "detected, action required\n", h->ctlr);
+               return 1;
+       break;
+       case REPORT_LUNS_CHANGED:
+               printk(KERN_WARNING "cciss%d: report LUN data "
+                       "changed\n", h->ctlr);
+               if (h->rescan_wait)
+                       complete(h->rescan_wait);
+               return 1;
+       break;
+       case POWER_OR_RESET:
+               printk(KERN_WARNING "cciss%d: a power on "
+                       "or device reset detected\n", h->ctlr);
+               return 1;
+       break;
+       case UNIT_ATTENTION_CLEARED:
+               printk(KERN_WARNING "cciss%d: unit attention "
+                   "cleared by another initiator\n", h->ctlr);
+               return 1;
+       break;
+       default:
+               printk(KERN_WARNING "cciss%d: unknown "
+                       "unit attention detected\n", h->ctlr);
+                               return 1;
+       }
+}
+
 /*
  *  We cannot read the structure directly, for portability we must use
  *   the io functions.
@@ -3185,14 +3265,23 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
  */
        cciss_interrupt_mode(c, pdev, board_id);
 
-       /*
-        * Memory base addr is first addr , the second points to the config
-        *   table
-        */
+       /* find the memory BAR */
+       for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
+               if (pci_resource_flags(pdev, i) & IORESOURCE_MEM)
+                       break;
+       }
+       if (i == DEVICE_COUNT_RESOURCE) {
+               printk(KERN_WARNING "cciss: No memory BAR found\n");
+               err = -ENODEV;
+               goto err_out_free_res;
+       }
+
+       c->paddr = pci_resource_start(pdev, i); /* addressing mode bits
+                                                * already removed
+                                                */
 
-       c->paddr = pci_resource_start(pdev, 0); /* addressing mode bits already removed */
 #ifdef CCISS_DEBUG
-       printk("address 0 = %x\n", c->paddr);
+       printk("address 0 = %lx\n", c->paddr);
 #endif                         /* CCISS_DEBUG */
        c->vaddr = remap_pci_mem(c->paddr, 0x250);
 
@@ -3219,7 +3308,8 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
 #endif                         /* CCISS_DEBUG */
        cfg_base_addr_index = find_PCI_BAR_index(pdev, cfg_base_addr);
 #ifdef CCISS_DEBUG
-       printk("cfg base address index = %x\n", cfg_base_addr_index);
+       printk("cfg base address index = %llx\n",
+               (unsigned long long)cfg_base_addr_index);
 #endif                         /* CCISS_DEBUG */
        if (cfg_base_addr_index == -1) {
                printk(KERN_WARNING "cciss: Cannot find cfg_base_addr_index\n");
@@ -3229,7 +3319,7 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
 
        cfg_offset = readl(c->vaddr + SA5_CTMEM_OFFSET);
 #ifdef CCISS_DEBUG
-       printk("cfg offset = %x\n", cfg_offset);
+       printk("cfg offset = %llx\n", (unsigned long long)cfg_offset);
 #endif                         /* CCISS_DEBUG */
        c->cfgtable = remap_pci_mem(pci_resource_start(pdev,
                                                       cfg_base_addr_index) +
@@ -3393,6 +3483,203 @@ static void free_hba(int i)
        kfree(p);
 }
 
+/* Send a message CDB to the firmware. */
+static __devinit int cciss_message(struct pci_dev *pdev, unsigned char opcode, unsigned char type)
+{
+       typedef struct {
+               CommandListHeader_struct CommandHeader;
+               RequestBlock_struct Request;
+               ErrDescriptor_struct ErrorDescriptor;
+       } Command;
+       static const size_t cmd_sz = sizeof(Command) + sizeof(ErrorInfo_struct);
+       Command *cmd;
+       dma_addr_t paddr64;
+       uint32_t paddr32, tag;
+       void __iomem *vaddr;
+       int i, err;
+
+       vaddr = ioremap_nocache(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
+       if (vaddr == NULL)
+               return -ENOMEM;
+
+       /* The Inbound Post Queue only accepts 32-bit physical addresses for the
+          CCISS commands, so they must be allocated from the lower 4GiB of
+          memory. */
+       err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+       if (err) {
+               iounmap(vaddr);
+               return -ENOMEM;
+       }
+
+       cmd = pci_alloc_consistent(pdev, cmd_sz, &paddr64);
+       if (cmd == NULL) {
+               iounmap(vaddr);
+               return -ENOMEM;
+       }
+
+       /* This must fit, because of the 32-bit consistent DMA mask.  Also,
+          although there's no guarantee, we assume that the address is at
+          least 4-byte aligned (most likely, it's page-aligned). */
+       paddr32 = paddr64;
+
+       cmd->CommandHeader.ReplyQueue = 0;
+       cmd->CommandHeader.SGList = 0;
+       cmd->CommandHeader.SGTotal = 0;
+       cmd->CommandHeader.Tag.lower = paddr32;
+       cmd->CommandHeader.Tag.upper = 0;
+       memset(&cmd->CommandHeader.LUN.LunAddrBytes, 0, 8);
+
+       cmd->Request.CDBLen = 16;
+       cmd->Request.Type.Type = TYPE_MSG;
+       cmd->Request.Type.Attribute = ATTR_HEADOFQUEUE;
+       cmd->Request.Type.Direction = XFER_NONE;
+       cmd->Request.Timeout = 0; /* Don't time out */
+       cmd->Request.CDB[0] = opcode;
+       cmd->Request.CDB[1] = type;
+       memset(&cmd->Request.CDB[2], 0, 14); /* the rest of the CDB is reserved */
+
+       cmd->ErrorDescriptor.Addr.lower = paddr32 + sizeof(Command);
+       cmd->ErrorDescriptor.Addr.upper = 0;
+       cmd->ErrorDescriptor.Len = sizeof(ErrorInfo_struct);
+
+       writel(paddr32, vaddr + SA5_REQUEST_PORT_OFFSET);
+
+       for (i = 0; i < 10; i++) {
+               tag = readl(vaddr + SA5_REPLY_PORT_OFFSET);
+               if ((tag & ~3) == paddr32)
+                       break;
+               schedule_timeout_uninterruptible(HZ);
+       }
+
+       iounmap(vaddr);
+
+       /* we leak the DMA buffer here ... no choice since the controller could
+          still complete the command. */
+       if (i == 10) {
+               printk(KERN_ERR "cciss: controller message %02x:%02x timed out\n",
+                       opcode, type);
+               return -ETIMEDOUT;
+       }
+
+       pci_free_consistent(pdev, cmd_sz, cmd, paddr64);
+
+       if (tag & 2) {
+               printk(KERN_ERR "cciss: controller message %02x:%02x failed\n",
+                       opcode, type);
+               return -EIO;
+       }
+
+       printk(KERN_INFO "cciss: controller message %02x:%02x succeeded\n",
+               opcode, type);
+       return 0;
+}
+
+#define cciss_soft_reset_controller(p) cciss_message(p, 1, 0)
+#define cciss_noop(p) cciss_message(p, 3, 0)
+
+static __devinit int cciss_reset_msi(struct pci_dev *pdev)
+{
+/* the #defines are stolen from drivers/pci/msi.h. */
+#define msi_control_reg(base)          (base + PCI_MSI_FLAGS)
+#define PCI_MSIX_FLAGS_ENABLE          (1 << 15)
+
+       int pos;
+       u16 control = 0;
+
+       pos = pci_find_capability(pdev, PCI_CAP_ID_MSI);
+       if (pos) {
+               pci_read_config_word(pdev, msi_control_reg(pos), &control);
+               if (control & PCI_MSI_FLAGS_ENABLE) {
+                       printk(KERN_INFO "cciss: resetting MSI\n");
+                       pci_write_config_word(pdev, msi_control_reg(pos), control & ~PCI_MSI_FLAGS_ENABLE);
+               }
+       }
+
+       pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX);
+       if (pos) {
+               pci_read_config_word(pdev, msi_control_reg(pos), &control);
+               if (control & PCI_MSIX_FLAGS_ENABLE) {
+                       printk(KERN_INFO "cciss: resetting MSI-X\n");
+                       pci_write_config_word(pdev, msi_control_reg(pos), control & ~PCI_MSIX_FLAGS_ENABLE);
+               }
+       }
+
+       return 0;
+}
+
+/* This does a hard reset of the controller using PCI power management
+ * states. */
+static __devinit int cciss_hard_reset_controller(struct pci_dev *pdev)
+{
+       u16 pmcsr, saved_config_space[32];
+       int i, pos;
+
+       printk(KERN_INFO "cciss: using PCI PM to reset controller\n");
+
+       /* This is very nearly the same thing as
+
+          pci_save_state(pci_dev);
+          pci_set_power_state(pci_dev, PCI_D3hot);
+          pci_set_power_state(pci_dev, PCI_D0);
+          pci_restore_state(pci_dev);
+
+          but we can't use these nice canned kernel routines on
+          kexec, because they also check the MSI/MSI-X state in PCI
+          configuration space and do the wrong thing when it is
+          set/cleared.  Also, the pci_save/restore_state functions
+          violate the ordering requirements for restoring the
+          configuration space from the CCISS document (see the
+          comment below).  So we roll our own .... */
+
+       for (i = 0; i < 32; i++)
+               pci_read_config_word(pdev, 2*i, &saved_config_space[i]);
+
+       pos = pci_find_capability(pdev, PCI_CAP_ID_PM);
+       if (pos == 0) {
+               printk(KERN_ERR "cciss_reset_controller: PCI PM not supported\n");
+               return -ENODEV;
+       }
+
+       /* Quoting from the Open CISS Specification: "The Power
+        * Management Control/Status Register (CSR) controls the power
+        * state of the device.  The normal operating state is D0,
+        * CSR=00h.  The software off state is D3, CSR=03h.  To reset
+        * the controller, place the interface device in D3 then to
+        * D0, this causes a secondary PCI reset which will reset the
+        * controller." */
+
+       /* enter the D3hot power management state */
+       pci_read_config_word(pdev, pos + PCI_PM_CTRL, &pmcsr);
+       pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
+       pmcsr |= PCI_D3hot;
+       pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr);
+
+       schedule_timeout_uninterruptible(HZ >> 1);
+
+       /* enter the D0 power management state */
+       pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
+       pmcsr |= PCI_D0;
+       pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr);
+
+       schedule_timeout_uninterruptible(HZ >> 1);
+
+       /* Restore the PCI configuration space.  The Open CISS
+        * Specification says, "Restore the PCI Configuration
+        * Registers, offsets 00h through 60h. It is important to
+        * restore the command register, 16-bits at offset 04h,
+        * last. Do not restore the configuration status register,
+        * 16-bits at offset 06h."  Note that the offset is 2*i. */
+       for (i = 0; i < 32; i++) {
+               if (i == 2 || i == 3)
+                       continue;
+               pci_write_config_word(pdev, 2*i, saved_config_space[i]);
+       }
+       wmb();
+       pci_write_config_word(pdev, 4, saved_config_space[2]);
+
+       return 0;
+}
+
 /*
  *  This is it.  Find all the controllers and register them.  I really hate
  *  stealing all these major device numbers.
@@ -3404,13 +3691,36 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
        int i;
        int j = 0;
        int rc;
-       int dac;
+       int dac, return_code;
+       InquiryData_struct *inq_buff = NULL;
+
+       if (reset_devices) {
+               /* Reset the controller with a PCI power-cycle */
+               if (cciss_hard_reset_controller(pdev) || cciss_reset_msi(pdev))
+                       return -ENODEV;
+
+               /* Now try to get the controller to respond to a no-op. Some
+                  devices (notably the HP Smart Array 5i Controller) need
+                  up to 30 seconds to respond. */
+               for (i=0; i<30; i++) {
+                       if (cciss_noop(pdev) == 0)
+                               break;
+
+                       schedule_timeout_uninterruptible(HZ);
+               }
+               if (i == 30) {
+                       printk(KERN_ERR "cciss: controller seems dead\n");
+                       return -EBUSY;
+               }
+       }
 
        i = alloc_cciss_hba();
        if (i < 0)
                return -1;
 
        hba[i]->busy_initializing = 1;
+       INIT_HLIST_HEAD(&hba[i]->cmpQ);
+       INIT_HLIST_HEAD(&hba[i]->reqQ);
 
        if (cciss_pci_init(hba[i], pdev) != 0)
                goto clean1;
@@ -3420,9 +3730,9 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
        hba[i]->pdev = pdev;
 
        /* configure PCI DMA stuff */
-       if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK))
+       if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64)))
                dac = 1;
-       else if (!pci_set_dma_mask(pdev, DMA_32BIT_MASK))
+       else if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32)))
                dac = 0;
        else {
                printk(KERN_ERR "cciss: no suitable DMA available\n");
@@ -3510,6 +3820,25 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
        /* Turn the interrupts on so we can service requests */
        hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_ON);
 
+       /* Get the firmware version */
+       inq_buff = kzalloc(sizeof(InquiryData_struct), GFP_KERNEL);
+       if (inq_buff == NULL) {
+               printk(KERN_ERR "cciss: out of memory\n");
+               goto clean4;
+       }
+
+       return_code = sendcmd_withirq(CISS_INQUIRY, i, inq_buff,
+               sizeof(InquiryData_struct), 0, 0 , 0, 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];
+               hba[i]->firm_ver[2] = inq_buff->data_byte[34];
+               hba[i]->firm_ver[3] = inq_buff->data_byte[35];
+       } else {         /* send command failed */
+               printk(KERN_WARNING "cciss: unable to determine firmware"
+                       " version of controller\n");
+       }
+
        cciss_procinit(i);
 
        hba[i]->cciss_max_sectors = 2048;
@@ -3517,9 +3846,15 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
        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
@@ -3591,6 +3926,7 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev)
                printk(KERN_ERR "cciss: Unable to remove device \n");
                return;
        }
+
        tmp_ptr = pci_get_drvdata(pdev);
        i = tmp_ptr->ctlr;
        if (hba[i] == NULL) {
@@ -3599,6 +3935,8 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev)
                return;
        }
 
+       kthread_stop(hba[i]->cciss_scan_thread);
+
        remove_proc_entry(hba[i]->devname, proc_cciss);
        unregister_blkdev(hba[i]->major, hba[i]->devname);
 
@@ -3661,6 +3999,13 @@ static struct pci_driver cciss_pci_driver = {
  */
 static int __init cciss_init(void)
 {
+       /*
+        * The hardware requires that commands are aligned on a 64-bit
+        * boundary. Given that we use pci_alloc_consistent() to allocate an
+        * array of them, the size must be a multiple of 8 bytes.
+        */
+       BUILD_BUG_ON(sizeof(CommandList_struct) % 8);
+
        printk(KERN_INFO DRIVER_NAME "\n");
 
        /* Register for our PCI devices */
@@ -3698,15 +4043,17 @@ static void fail_all_cmds(unsigned long ctlr)
        pci_disable_device(h->pdev);    /* Make sure it is really dead. */
 
        /* move everything off the request queue onto the completed queue */
-       while ((c = h->reqQ) != NULL) {
-               removeQ(&(h->reqQ), c);
+       while (!hlist_empty(&h->reqQ)) {
+               c = hlist_entry(h->reqQ.first, CommandList_struct, list);
+               removeQ(c);
                h->Qdepth--;
-               addQ(&(h->cmpQ), c);
+               addQ(&h->cmpQ, c);
        }
 
        /* Now, fail everything on the completed queue with a HW error */
-       while ((c = h->cmpQ) != NULL) {
-               removeQ(&h->cmpQ, c);
+       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_RWREQ) {
                        complete_command(h, c, 0);