[PATCH] CCISS: use ARRAY_SIZE without intermediates
[safe/jmp/linux-2.6] / drivers / block / cciss.c
index 49f0541..94e82a2 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *    Disk Array driver for HP SA 5xxx and 6xxx Controllers
- *    Copyright 2000, 2005 Hewlett-Packard Development Company, L.P.
+ *    Copyright 2000, 2006 Hewlett-Packard Development Company, L.P.
  *
  *    This program is free software; you can redistribute it and/or modify
  *    it under the terms of the GNU General Public License as published by
@@ -38,6 +38,7 @@
 #include <linux/hdreg.h>
 #include <linux/spinlock.h>
 #include <linux/compat.h>
+#include <linux/blktrace_api.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
 
 #include <linux/completion.h>
 
 #define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin))
-#define DRIVER_NAME "HP CISS Driver (v 2.6.8)"
-#define DRIVER_VERSION CCISS_DRIVER_VERSION(2,6,8)
+#define DRIVER_NAME "HP CISS Driver (v 2.6.10)"
+#define DRIVER_VERSION CCISS_DRIVER_VERSION(2,6,10)
 
 /* Embedded module documentation macros - see modules.h */
 MODULE_AUTHOR("Hewlett-Packard Company");
-MODULE_DESCRIPTION("Driver for HP Controller SA5xxx SA6xxx version 2.6.8");
+MODULE_DESCRIPTION("Driver for HP Controller SA5xxx SA6xxx version 2.6.10");
 MODULE_SUPPORTED_DEVICE("HP SA5i SA5i+ SA532 SA5300 SA5312 SA641 SA642 SA6400"
                        " SA6i P600 P800 P400 P400i E200 E200i");
 MODULE_LICENSE("GPL");
@@ -103,8 +104,6 @@ static const struct pci_device_id cciss_pci_device_id[] = {
 };
 MODULE_DEVICE_TABLE(pci, cciss_pci_device_id);
 
-#define NR_PRODUCTS (sizeof(products)/sizeof(struct board_type))
-
 /*  board_id = Subsystem Device ID & Vendor ID
  *  product = Marketing Name for the board
  *  access = Address of the struct of function pointers 
@@ -148,10 +147,12 @@ static struct board_type products[] = {
 static ctlr_info_t *hba[MAX_CTLR];
 
 static void do_cciss_request(request_queue_t *q);
+static irqreturn_t do_cciss_intr(int irq, void *dev_id, struct pt_regs *regs);
 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, 
                unsigned int cmd, unsigned long arg);
+static int cciss_getgeo(struct block_device *bdev, struct hd_geometry *geo);
 
 static int revalidate_allvol(ctlr_info_t *host);
 static int cciss_revalidate(struct gendisk *disk);
@@ -165,7 +166,7 @@ static void cciss_geometry_inquiry(int ctlr, int logvol,
                        unsigned int block_size, InquiryData_struct *inq_buff,
                        drive_info_struct *drv);
 static void cciss_getgeometry(int cntl_num);
-
+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,
@@ -193,6 +194,7 @@ static struct block_device_operations cciss_fops  = {
        .open           = cciss_open, 
        .release        = cciss_release,
         .ioctl         = cciss_ioctl,
+        .getgeo                = cciss_getgeo,
 #ifdef CONFIG_COMPAT
        .compat_ioctl   = cciss_compat_ioctl,
 #endif
@@ -281,7 +283,7 @@ static int cciss_proc_get_info(char *buffer, char **start, off_t offset,
                 h->product_name,
                 (unsigned long)h->board_id,
                h->firm_ver[0], h->firm_ver[1], h->firm_ver[2], h->firm_ver[3],
-                (unsigned int)h->intr,
+                (unsigned int)h->intr[SIMPLE_MODE_INT],
                 h->num_luns, 
                h->Qdepth, h->commands_outstanding,
                h->maxQsinceinit, h->max_outstanding, h->maxSG);
@@ -483,9 +485,6 @@ static int cciss_open(struct inode *inode, struct file *filep)
        printk(KERN_DEBUG "cciss_open %s\n", inode->i_bdev->bd_disk->disk_name);
 #endif /* CCISS_DEBUG */ 
 
-       if (host->busy_initializing)
-               return -EBUSY;
-
        if (host->busy_initializing || drv->busy_configuring)
                return -EBUSY;
        /*
@@ -635,6 +634,20 @@ static int cciss_ioctl32_big_passthru(struct file *file, unsigned cmd, unsigned
        return err;
 }
 #endif
+
+static int cciss_getgeo(struct block_device *bdev, struct hd_geometry *geo)
+{
+       drive_info_struct *drv = get_drv(bdev->bd_disk);
+
+       if (!drv->cylinders)
+               return -ENXIO;
+
+       geo->heads = drv->heads;
+       geo->sectors = drv->sectors;
+       geo->cylinders = drv->cylinders;
+       return 0;
+}
+
 /*
  * ioctl 
  */
@@ -653,21 +666,6 @@ static int cciss_ioctl(struct inode *inode, struct file *filep,
 #endif /* CCISS_DEBUG */ 
        
        switch(cmd) {
-       case HDIO_GETGEO:
-       {
-                struct hd_geometry driver_geo;
-                if (drv->cylinders) {
-                        driver_geo.heads = drv->heads;
-                        driver_geo.sectors = drv->sectors;
-                        driver_geo.cylinders = drv->cylinders;
-                } else
-                       return -ENXIO;
-                driver_geo.start= get_start_sect(inode->i_bdev);
-                if (copy_to_user(argp, &driver_geo, sizeof(struct hd_geometry)))
-                        return  -EFAULT;
-                return(0);
-       }
-
        case CCISS_GETPCIINFO:
        {
                cciss_pci_info_struct pciinfo;
@@ -996,13 +994,11 @@ static int cciss_ioctl(struct inode *inode, struct file *filep,
                        status = -EINVAL;
                        goto cleanup1;
                }
-               buff = (unsigned char **) kmalloc(MAXSGENTRIES * 
-                               sizeof(char *), GFP_KERNEL);
+               buff = kzalloc(MAXSGENTRIES * sizeof(char *), GFP_KERNEL);
                if (!buff) {
                        status = -ENOMEM;
                        goto cleanup1;
                }
-               memset(buff, 0, MAXSGENTRIES);
                buff_size = (int *) kmalloc(MAXSGENTRIES * sizeof(int), 
                                        GFP_KERNEL);
                if (!buff_size) {
@@ -1019,10 +1015,11 @@ static int cciss_ioctl(struct inode *inode, struct file *filep,
                                status = -ENOMEM;
                                goto cleanup1;
                        }
-                       if (ioc->Request.Type.Direction == XFER_WRITE &&
-                               copy_from_user(buff[sg_used], data_ptr, sz)) {
+                       if (ioc->Request.Type.Direction == XFER_WRITE) {
+                               if (copy_from_user(buff[sg_used], data_ptr, sz)) {
                                        status = -ENOMEM;
-                                       goto cleanup1;                  
+                                       goto cleanup1;
+                               }
                        } else {
                                memset(buff[sg_used], 0, sz);
                        }
@@ -1099,14 +1096,11 @@ static int cciss_ioctl(struct inode *inode, struct file *filep,
 cleanup1:
                if (buff) {
                        for(i=0; i<sg_used; i++)
-                               if(buff[i] != NULL)
-                                       kfree(buff[i]);
+                               kfree(buff[i]);
                        kfree(buff);
                }
-               if (buff_size)
-                       kfree(buff_size);
-               if (ioc)
-                       kfree(ioc);
+               kfree(buff_size);
+               kfree(ioc);
                return(status);
        }
        default:
@@ -1143,8 +1137,14 @@ static int revalidate_allvol(ctlr_info_t *host)
 
        for(i=0; i< NWD; i++) {
                struct gendisk *disk = host->gendisk[i];
-               if (disk->flags & GENHD_FL_UP)
-                       del_gendisk(disk);
+               if (disk) {
+                       request_queue_t *q = disk->queue;
+
+                       if (disk->flags & GENHD_FL_UP)
+                               del_gendisk(disk);
+                       if (q)
+                               blk_cleanup_queue(q);
+               }
        }
 
         /*
@@ -1178,6 +1178,54 @@ static int revalidate_allvol(ctlr_info_t *host)
         return 0;
 }
 
+static inline void complete_buffers(struct bio *bio, int status)
+{
+       while (bio) {
+               struct bio *xbh = bio->bi_next;
+               int nr_sectors = bio_sectors(bio);
+
+               bio->bi_next = NULL;
+               blk_finished_io(len);
+               bio_endio(bio, nr_sectors << 9, status ? 0 : -EIO);
+               bio = xbh;
+       }
+
+}
+
+static void cciss_softirq_done(struct request *rq)
+{
+       CommandList_struct *cmd = rq->completion_data;
+       ctlr_info_t *h = hba[cmd->ctlr];
+       unsigned long flags;
+       u64bit temp64;
+       int i, ddir;
+
+       if (cmd->Request.Type.Direction == XFER_READ)
+               ddir = PCI_DMA_FROMDEVICE;
+       else
+               ddir = PCI_DMA_TODEVICE;
+
+       /* 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);
+       }
+
+       complete_buffers(rq->bio, rq->errors);
+
+#ifdef CCISS_DEBUG
+       printk("Done with %p\n", rq);
+#endif /* CCISS_DEBUG */
+
+       add_disk_randomness(rq->rq_disk);
+       spin_lock_irqsave(&h->lock, flags);
+       end_that_request_last(rq, rq->errors);
+       cmd_free(h, cmd,1);
+       spin_unlock_irqrestore(&h->lock, flags);
+}
+
 /* This function will check the usage_count of the drive to be updated/added.
  * If the usage_count is zero then the drive information will be updated and
  * the disk will be re-registered with the kernel.  If not then it will be
@@ -1246,6 +1294,8 @@ static void cciss_update_drive_info(int ctlr, int drv_index)
 
                blk_queue_max_sectors(disk->queue, 512);
 
+               blk_queue_softirq_done(disk->queue, cciss_softirq_done);
+
                disk->queue->queuedata = hba[ctlr];
 
                blk_queue_hardsect_size(disk->queue,
@@ -1458,10 +1508,14 @@ static int deregister_disk(struct gendisk *disk, drive_info_struct *drv,
         * allows us to delete disk zero but keep the controller registered.
        */
        if (h->gendisk[0] != disk){
-               if (disk->flags & GENHD_FL_UP){
-                       blk_cleanup_queue(disk->queue);
-               del_gendisk(disk);
-                       drv->queue = NULL;
+               if (disk) {
+                       request_queue_t *q = disk->queue;
+                       if (disk->flags & GENHD_FL_UP)
+                               del_gendisk(disk);
+                       if (q) {
+                               blk_cleanup_queue(q);
+                               drv->queue = NULL;
+                       }
                }
        }
 
@@ -1589,6 +1643,24 @@ static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff,
                }
        } else if (cmd_type == TYPE_MSG) {
                switch (cmd) {
+               case 0: /* ABORT message */
+                       c->Request.CDBLen = 12;
+                       c->Request.Type.Attribute = ATTR_SIMPLE;
+                       c->Request.Type.Direction = XFER_WRITE;
+                       c->Request.Timeout = 0;
+                       c->Request.CDB[0] = cmd; /* abort */
+                       c->Request.CDB[1] = 0;   /* abort a command */
+                       /* buff contains the tag of the command to abort */
+                       memcpy(&c->Request.CDB[4], buff, 8);
+                       break;
+               case 1: /* RESET message */
+                       c->Request.CDBLen = 12;
+                       c->Request.Type.Attribute = ATTR_SIMPLE;
+                       c->Request.Type.Direction = XFER_WRITE;
+                       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 */
                case 3: /* No-Op message */
                        c->Request.CDBLen = 1;
                        c->Request.Type.Attribute = ATTR_SIMPLE;
@@ -1730,8 +1802,10 @@ case CMD_HARDWARE_ERR:
                }
        }       
        /* 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,
-                       size, PCI_DMA_BIDIRECTIONAL);
+                       c->SG[0].Len, PCI_DMA_BIDIRECTIONAL);
        cmd_free(h, c, 0);
         return(return_status);
 
@@ -1873,6 +1947,52 @@ static unsigned long pollcomplete(int ctlr)
        /* 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 >= (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. 
@@ -1895,7 +2015,7 @@ static int sendcmd(
        unsigned long complete;
        ctlr_info_t *info_p= hba[ctlr];
        u64bit buff_dma_handle;
-       int status;
+       int status, done = 0;
 
        if ((c = cmd_alloc(info_p, 1)) == NULL) {
                printk(KERN_WARNING "cciss: unable to get memory");
@@ -1917,7 +2037,9 @@ resend_cmd1:
         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. */
+        /* 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 */
@@ -1934,13 +2056,25 @@ resend_cmd1:
          * Send the cmd
          */
         info_p->access.submit_command(info_p, c);
-        complete = pollcomplete(ctlr);
+       done = 0;
+       do {
+               complete = pollcomplete(ctlr);
 
 #ifdef CCISS_DEBUG
-       printk(KERN_DEBUG "cciss: command completed\n");
+               printk(KERN_DEBUG "cciss: command completed\n");
 #endif /* CCISS_DEBUG */
 
-       if (complete != 1) {
+               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)
                     {
@@ -1980,6 +2114,10 @@ resend_cmd1:
                                                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, 
@@ -1994,25 +2132,27 @@ resend_cmd1:
                                goto cleanup1;
                        }
                }
+               /* This will need changing for direct lookup completions */
                 if (complete != c->busaddr) {
-                        printk( KERN_WARNING "cciss cciss%d: SendCmd "
-                      "Invalid command list address returned! (%lx)\n",
-                                ctlr, complete);
-                       status = IO_ERROR;
-                       goto cleanup1;
-                }
-        } else {
-                printk( KERN_WARNING
-                        "cciss cciss%d: SendCmd Timeout out, "
-                        "No command list address returned!\n",
-                        ctlr);
-               status = IO_ERROR;
-        }
+                       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,
-                               size, PCI_DMA_BIDIRECTIONAL);
+                               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, NULL);
+#endif
        cmd_free(info_p, c, 1);
        return (status);
 } 
@@ -2044,7 +2184,7 @@ static void start_io( ctlr_info_t *h)
                        break;
                }
 
-               /* Get the frist entry from the Request Q */ 
+               /* Get the first entry from the Request Q */ 
                removeQ(&(h->reqQ), c);
                h->Qdepth--;
        
@@ -2055,20 +2195,6 @@ static void start_io( ctlr_info_t *h)
                addQ (&(h->cmpQ), c); 
        }
 }
-
-static inline void complete_buffers(struct bio *bio, int status)
-{
-       while (bio) {
-               struct bio *xbh = bio->bi_next; 
-               int nr_sectors = bio_sectors(bio);
-
-               bio->bi_next = NULL; 
-               blk_finished_io(len);
-               bio_endio(bio, nr_sectors << 9, status ? 0 : -EIO);
-               bio = xbh;
-       }
-
-} 
 /* Assumes that CCISS_LOCK(h->ctlr) is held. */
 /* Zeros out the error record and then resends the command back */
 /* to the controller */
@@ -2085,16 +2211,16 @@ static inline void resend_cciss_cmd( ctlr_info_t *h, CommandList_struct *c)
 
        start_io(h);
 }
+
 /* checks the status of the job and calls complete buffers to mark all 
- * buffers for the completed job. 
+ * buffers for the completed job. Note that this function does not need
+ * to hold the hba/queue lock.
  */ 
 static inline void complete_command( ctlr_info_t *h, CommandList_struct *cmd,
                int timeout)
 {
        int status = 1;
-       int i;
        int retry_cmd = 0;
-       u64bit temp64;
                
        if (timeout)
                status = 0; 
@@ -2202,24 +2328,11 @@ static inline void complete_command( ctlr_info_t *h, CommandList_struct *cmd,
                resend_cciss_cmd(h,cmd);
                return;
        }       
-       /* 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(hba[cmd->ctlr]->pdev,
-                       temp64.val, cmd->SG[i].Len,
-                       (cmd->Request.Type.Direction == XFER_READ) ?
-                               PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);
-       }
-       complete_buffers(cmd->rq->bio, status);
-
-#ifdef CCISS_DEBUG
-       printk("Done with %p\n", cmd->rq);
-#endif /* CCISS_DEBUG */ 
 
-       end_that_request_last(cmd->rq);
-       cmd_free(h,cmd,1);
+       cmd->rq->completion_data = cmd;
+       cmd->rq->errors = status;
+       blk_add_trace_rq(cmd->rq->q, cmd->rq, BLK_TA_COMPLETE);
+       blk_complete_request(cmd->rq);
 }
 
 /* 
@@ -2247,8 +2360,7 @@ queue:
        if (!creq)
                goto startio;
 
-       if (creq->nr_phys_segments > MAXSGENTRIES)
-                BUG();
+       BUG_ON(creq->nr_phys_segments > MAXSGENTRIES);
 
        if (( c = cmd_alloc(h, 1)) == NULL)
                goto full;
@@ -2337,6 +2449,48 @@ startio:
        start_io(h);
 }
 
+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, struct pt_regs *regs)
 {
        ctlr_info_t *h = dev_id;
@@ -2346,19 +2500,15 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id, struct pt_regs *regs)
        int j;
        int start_queue = h->next_to_run;
 
-       /* Is this interrupt for us? */
-       if (( h->access.intr_pending(h) == 0) || (h->interrupts_enabled == 0))
+       if (interrupt_not_for_us(h))
                return IRQ_NONE;
-
        /*
         * If there are completed commands in the completion queue,
         * we had better do something about it.
         */
        spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
-       while( h->access.intr_pending(h))
-       {
-               while((a = h->access.command_completed(h)) != FIFO_EMPTY) 
-               {
+       while (interrupt_pending(h)) {
+               while((a = get_next_completion(h)) != FIFO_EMPTY) {
                        a1 = a;
                        if ((a & 0x04)) {
                                a2 = (a >> 3);
@@ -2486,16 +2636,6 @@ static void print_cfg_table( CfgTable_struct *tb)
 }
 #endif /* CCISS_DEBUG */ 
 
-static void release_io_mem(ctlr_info_t *c)
-{
-       /* if IO mem was not protected do nothing */
-       if( c->io_mem_addr == 0)
-               return;
-       release_region(c->io_mem_addr, c->io_mem_length);
-       c->io_mem_addr = 0;
-       c->io_mem_length = 0;
-}
-
 static int find_PCI_BAR_index(struct pci_dev *pdev,
                                unsigned long pci_bar_addr)
 {
@@ -2531,6 +2671,60 @@ static int find_PCI_BAR_index(struct pci_dev *pdev,
        return -1;
 }
 
+/* If MSI/MSI-X is supported by the kernel we will try to enable it on
+ * controllers that are capable. If not, we use IO-APIC mode.
+ */
+
+static void __devinit cciss_interrupt_mode(ctlr_info_t *c, struct pci_dev *pdev, __u32 board_id)
+{
+#ifdef CONFIG_PCI_MSI
+        int err;
+        struct msix_entry cciss_msix_entries[4] = {{0,0}, {0,1},
+                                                  {0,2}, {0,3}};
+
+       /* Some boards advertise MSI but don't really support it */
+       if ((board_id == 0x40700E11) ||
+               (board_id == 0x40800E11) ||
+               (board_id == 0x40820E11) ||
+               (board_id == 0x40830E11))
+               goto default_int_mode;
+
+        if (pci_find_capability(pdev, PCI_CAP_ID_MSIX)) {
+                err = pci_enable_msix(pdev, cciss_msix_entries, 4);
+                if (!err) {
+                        c->intr[0] = cciss_msix_entries[0].vector;
+                        c->intr[1] = cciss_msix_entries[1].vector;
+                        c->intr[2] = cciss_msix_entries[2].vector;
+                        c->intr[3] = cciss_msix_entries[3].vector;
+                        c->msix_vector = 1;
+                        return;
+                }
+                if (err > 0) {
+                        printk(KERN_WARNING "cciss: only %d MSI-X vectors "
+                                        "available\n", err);
+                } else {
+                        printk(KERN_WARNING "cciss: MSI-X init failed %d\n",
+                                               err);
+                }
+        }
+        if (pci_find_capability(pdev, PCI_CAP_ID_MSI)) {
+                if (!pci_enable_msi(pdev)) {
+                        c->intr[SIMPLE_MODE_INT] = pdev->irq;
+                        c->msi_vector = 1;
+                        return;
+                } else {
+                        printk(KERN_WARNING "cciss: MSI init failed\n");
+                       c->intr[SIMPLE_MODE_INT] = pdev->irq;
+                        return;
+                }
+        }
+default_int_mode:
+#endif /* CONFIG_PCI_MSI */
+       /* if we get here we're going to use the default interrupt mode */
+        c->intr[SIMPLE_MODE_INT] = pdev->irq;
+       return;
+}
+
 static int cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
 {
        ushort subsystem_vendor_id, subsystem_device_id, command;
@@ -2538,7 +2732,7 @@ static int 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;
+       int i, err;
 
        /* check to see if controller has been disabled */
        /* BEFORE trying to enable it */
@@ -2546,13 +2740,21 @@ static int cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
        if(!(command & 0x02))
        {
                printk(KERN_WARNING "cciss: controller appears to be disabled\n");
-               return(-1);
+               return -ENODEV;
        }
 
-       if (pci_enable_device(pdev))
+       err = pci_enable_device(pdev);
+       if (err)
        {
                printk(KERN_ERR "cciss: Unable to Enable PCI device\n");
-               return( -1);
+               return err;
+       }
+
+       err = pci_request_regions(pdev, "cciss");
+       if (err) {
+               printk(KERN_ERR "cciss: Cannot obtain PCI resources, "
+                       "aborting\n");
+               goto err_out_disable_pdev;
        }
 
        subsystem_vendor_id = pdev->subsystem_vendor;
@@ -2560,38 +2762,16 @@ static int cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
        board_id = (((__u32) (subsystem_device_id << 16) & 0xffff0000) |
                                        subsystem_vendor_id);
 
-       /* search for our IO range so we can protect it */
-       for(i=0; i<DEVICE_COUNT_RESOURCE; i++)
-       {
-               /* is this an IO range */ 
-               if( pci_resource_flags(pdev, i) & 0x01 ) {
-                       c->io_mem_addr = pci_resource_start(pdev, i);
-                       c->io_mem_length = pci_resource_end(pdev, i) -
-                               pci_resource_start(pdev, i) +1;
-#ifdef CCISS_DEBUG
-                       printk("IO value found base_addr[%d] %lx %lx\n", i,
-                               c->io_mem_addr, c->io_mem_length);
-#endif /* CCISS_DEBUG */
-                       /* register the IO range */ 
-                       if(!request_region( c->io_mem_addr,
-                                        c->io_mem_length, "cciss"))
-                       {
-                               printk(KERN_WARNING "cciss I/O memory range already in use addr=%lx length=%ld\n",
-                               c->io_mem_addr, c->io_mem_length);
-                               c->io_mem_addr= 0;
-                               c->io_mem_length = 0;
-                       } 
-                       break;
-               }
-       }
-
 #ifdef CCISS_DEBUG
        printk("command = %x\n", command);
        printk("irq = %x\n", pdev->irq);
        printk("board_id = %x\n", board_id);
 #endif /* CCISS_DEBUG */ 
 
-       c->intr = pdev->irq;
+/* If the kernel supports MSI/MSI-X we will try to enable that functionality,
+ * else we use the IO-APIC interrupt assigned to us by system ROM.
+ */
+       cciss_interrupt_mode(c, pdev, board_id);
 
        /*
         * Memory base addr is first addr , the second points to the config
@@ -2615,7 +2795,8 @@ static int cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
        }
        if (scratchpad != CCISS_FIRMWARE_READY) {
                printk(KERN_WARNING "cciss: Board not ready.  Timed out.\n");
-               return -1;
+               err = -ENODEV;
+               goto err_out_free_res;
        }
 
        /* get the address index number */
@@ -2631,8 +2812,8 @@ static int cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
 #endif /* CCISS_DEBUG */
        if (cfg_base_addr_index == -1) {
                printk(KERN_WARNING "cciss: Cannot find cfg_base_addr_index\n");
-               release_io_mem(c);
-               return -1;
+               err = -ENODEV;
+               goto err_out_free_res;
        }
 
        cfg_offset = readl(c->vaddr + SA5_CTMEM_OFFSET);
@@ -2645,21 +2826,22 @@ static int cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
        c->board_id = board_id;
 
 #ifdef CCISS_DEBUG
-       print_cfg_table(c->cfgtable); 
+       print_cfg_table(c->cfgtable);
 #endif /* CCISS_DEBUG */
 
-       for(i=0; i<NR_PRODUCTS; i++) {
+       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);
                        break;
                }
        }
-       if (i == NR_PRODUCTS) {
+       if (i == ARRAY_SIZE(products)) {
                printk(KERN_WARNING "cciss: Sorry, I don't know how"
                        " to access the Smart Array controller %08lx\n", 
                                (unsigned long)board_id);
-               return -1;
+               err = -ENODEV;
+               goto err_out_free_res;
        }
        if (  (readb(&c->cfgtable->Signature[0]) != 'C') ||
              (readb(&c->cfgtable->Signature[1]) != 'I') ||
@@ -2667,7 +2849,8 @@ static int cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
              (readb(&c->cfgtable->Signature[3]) != 'S') )
        {
                printk("Does not appear to be a valid CISS config table\n");
-               return -1;
+               err = -ENODEV;
+               goto err_out_free_res;
        }
 
 #ifdef CONFIG_X86
@@ -2711,10 +2894,17 @@ static int cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
        {
                printk(KERN_WARNING "cciss: unable to get board into"
                                        " simple mode\n");
-               return -1;
+               err = -ENODEV;
+               goto err_out_free_res;
        }
        return 0;
 
+err_out_free_res:
+       pci_release_regions(pdev);
+
+err_out_disable_pdev:
+       pci_disable_device(pdev);
+       return err;
 }
 
 /* 
@@ -2732,13 +2922,12 @@ static void cciss_getgeometry(int cntl_num)
        int block_size;
        int total_size; 
 
-       ld_buff = kmalloc(sizeof(ReportLunData_struct), GFP_KERNEL);
+       ld_buff = kzalloc(sizeof(ReportLunData_struct), GFP_KERNEL);
        if (ld_buff == NULL)
        {
                printk(KERN_ERR "cciss: out of memory\n");
                return;
        }
-       memset(ld_buff, 0, sizeof(ReportLunData_struct));
        size_buff = kmalloc(sizeof( ReadCapdata_struct), GFP_KERNEL);
         if (size_buff == NULL)
         {
@@ -2852,10 +3041,9 @@ static int alloc_cciss_hba(void)
        for(i=0; i< MAX_CTLR; i++) {
                if (!hba[i]) {
                        ctlr_info_t *p;
-                       p = kmalloc(sizeof(ctlr_info_t), GFP_KERNEL);
+                       p = kzalloc(sizeof(ctlr_info_t), GFP_KERNEL);
                        if (!p)
                                goto Enomem;
-                       memset(p, 0, sizeof(ctlr_info_t));
                        for (n = 0; n < NWD; n++)
                                p->gendisk[n] = disk[n];
                        hba[i] = p;
@@ -2896,11 +3084,8 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
        int i;
        int j;
        int rc;
+       int dac;
 
-       printk(KERN_DEBUG "cciss: Device 0x%x has been found at"
-                       " bus %d dev %d func %d\n",
-               pdev->device, pdev->bus->number, PCI_SLOT(pdev->devfn),
-                       PCI_FUNC(pdev->devfn));
        i = alloc_cciss_hba();
        if(i < 0)
                return (-1);
@@ -2916,11 +3101,11 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
 
        /* configure PCI DMA stuff */
        if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK))
-               printk("cciss: using DAC cycles\n");
+               dac = 1;
        else if (!pci_set_dma_mask(pdev, DMA_32BIT_MASK))
-               printk("cciss: not using DAC cycles\n");
+               dac = 0;
        else {
-               printk("cciss: no suitable DMA available\n");
+               printk(KERN_ERR "cciss: no suitable DMA available\n");
                goto clean1;
        }
 
@@ -2930,7 +3115,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
         * 8 controller support.
         */
        if (i < MAX_CTLR_ORIG)
-               hba[i]->major = MAJOR_NR + i;
+               hba[i]->major = COMPAQ_CISS_MAJOR + i;
        rc = register_blkdev(hba[i]->major, hba[i]->devname);
        if(rc == -EBUSY || rc == -EINVAL) {
                printk(KERN_ERR
@@ -2945,13 +3130,17 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
 
        /* make sure the board interrupts are off */
        hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_OFF);
-       if( request_irq(hba[i]->intr, do_cciss_intr, 
-               SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, 
-                       hba[i]->devname, hba[i])) {
+       if( request_irq(hba[i]->intr[SIMPLE_MODE_INT], do_cciss_intr,
+               SA_INTERRUPT | SA_SHIRQ, hba[i]->devname, hba[i])) {
                printk(KERN_ERR "cciss: Unable to get irq %d for %s\n",
-                       hba[i]->intr, hba[i]->devname);
+                       hba[i]->intr[SIMPLE_MODE_INT], hba[i]->devname);
                goto clean2;
        }
+
+       printk(KERN_INFO "%s: <0x%x> at PCI %s IRQ %d%s using DAC\n",
+               hba[i]->devname, pdev->device, pci_name(pdev),
+               hba[i]->intr[SIMPLE_MODE_INT], dac ? "" : " not");
+
        hba[i]->cmd_pool_bits = kmalloc(((NR_CMDS+BITS_PER_LONG-1)/BITS_PER_LONG)*sizeof(unsigned long), GFP_KERNEL);
        hba[i]->cmd_pool = (CommandList_struct *)pci_alloc_consistent(
                hba[i]->pdev, NR_CMDS * sizeof(CommandList_struct), 
@@ -2965,7 +3154,15 @@ 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]) * 
+                       (NR_CMDS + 5), GFP_KERNEL);
+       if (hba[i]->scsi_rejects.complete == NULL) {
+                printk( KERN_ERR "cciss: out of memory");
+               goto clean4;
+       }
+#endif
        spin_lock_init(&hba[i]->lock);
 
        /* Initialize the pdev driver private data. 
@@ -2987,6 +3184,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
        hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_ON);
 
        cciss_procinit(i);
+       hba[i]->busy_initializing = 0;
 
        for(j=0; j < NWD; j++) { /* mfm */
                drive_info_struct *drv = &(hba[i]->drv[j]);
@@ -3002,15 +3200,17 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
                drv->queue = q;
 
                q->backing_dev_info.ra_pages = READ_AHEAD;
-       blk_queue_bounce_limit(q, hba[i]->pdev->dma_mask);
+               blk_queue_bounce_limit(q, hba[i]->pdev->dma_mask);
+
+               /* This is a hardware imposed limit. */
+               blk_queue_max_hw_segments(q, MAXSGENTRIES);
 
-       /* This is a hardware imposed limit. */
-       blk_queue_max_hw_segments(q, MAXSGENTRIES);
+               /* This is a limit in the driver and could be eliminated. */
+               blk_queue_max_phys_segments(q, MAXSGENTRIES);
 
-       /* This is a limit in the driver and could be eliminated. */
-       blk_queue_max_phys_segments(q, MAXSGENTRIES);
+               blk_queue_max_sectors(q, 512);
 
-       blk_queue_max_sectors(q, 512);
+               blk_queue_softirq_done(q, cciss_softirq_done);
 
                q->queuedata = hba[i];
                sprintf(disk->disk_name, "cciss/c%dd%d", i, j);
@@ -3020,6 +3220,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
                disk->fops = &cciss_fops;
                disk->queue = q;
                disk->private_data = drv;
+               disk->driverfs_dev = &pdev->dev;
                /* we must register the controller even if no disks exist */
                /* this is for the online array utilities */
                if(!drv->heads && j)
@@ -3029,12 +3230,13 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
                add_disk(disk);
        }
 
-       hba[i]->busy_initializing = 0;
        return(1);
 
 clean4:
-       if(hba[i]->cmd_pool_bits)
-                       kfree(hba[i]->cmd_pool_bits);
+#ifdef CONFIG_CISS_SCSI_TAPE
+       kfree(hba[i]->scsi_rejects.complete);
+#endif
+       kfree(hba[i]->cmd_pool_bits);
        if(hba[i]->cmd_pool)
                pci_free_consistent(hba[i]->pdev,
                        NR_CMDS * sizeof(CommandList_struct),
@@ -3044,13 +3246,12 @@ clean4:
                        NR_CMDS * sizeof( ErrorInfo_struct),
                        hba[i]->errinfo_pool,
                        hba[i]->errinfo_pool_dhandle);
-       free_irq(hba[i]->intr, hba[i]);
+       free_irq(hba[i]->intr[SIMPLE_MODE_INT], hba[i]);
 clean2:
        unregister_blkdev(hba[i]->major, hba[i]->devname);
 clean1:
-       release_io_mem(hba[i]);
-       free_hba(i);
        hba[i]->busy_initializing = 0;
+       free_hba(i);
        return(-1);
 }
 
@@ -3085,8 +3286,15 @@ static void __devexit cciss_remove_one (struct pci_dev *pdev)
                printk(KERN_WARNING "Error Flushing cache on controller %d\n", 
                        i);
        }
-       free_irq(hba[i]->intr, hba[i]);
-       pci_set_drvdata(pdev, NULL);
+       free_irq(hba[i]->intr[2], hba[i]);
+
+#ifdef CONFIG_PCI_MSI
+        if (hba[i]->msix_vector)
+                pci_disable_msix(hba[i]->pdev);
+        else if (hba[i]->msi_vector)
+                pci_disable_msi(hba[i]->pdev);
+#endif /* CONFIG_PCI_MSI */
+
        iounmap(hba[i]->vaddr);
        cciss_unregister_scsi(i);  /* unhook from SCSI subsystem */
        unregister_blkdev(hba[i]->major, hba[i]->devname);
@@ -3095,9 +3303,13 @@ static void __devexit cciss_remove_one (struct pci_dev *pdev)
        /* remove it from the disk list */
        for (j = 0; j < NWD; j++) {
                struct gendisk *disk = hba[i]->gendisk[j];
-               if (disk->flags & GENHD_FL_UP) {
-                       del_gendisk(disk);
-                       blk_cleanup_queue(disk->queue);
+               if (disk) {
+                       request_queue_t *q = disk->queue;
+
+                       if (disk->flags & GENHD_FL_UP) 
+                               del_gendisk(disk);
+                       if (q)
+                               blk_cleanup_queue(q);
                }
        }
 
@@ -3106,7 +3318,12 @@ static void __devexit cciss_remove_one (struct pci_dev *pdev)
        pci_free_consistent(hba[i]->pdev, NR_CMDS * sizeof( ErrorInfo_struct),
                hba[i]->errinfo_pool, hba[i]->errinfo_pool_dhandle);
        kfree(hba[i]->cmd_pool_bits);
-       release_io_mem(hba[i]);
+#ifdef CONFIG_CISS_SCSI_TAPE
+       kfree(hba[i]->scsi_rejects.complete);
+#endif
+       pci_release_regions(pdev);
+       pci_disable_device(pdev);
+       pci_set_drvdata(pdev, NULL);
        free_hba(i);
 }      
 
@@ -3126,7 +3343,7 @@ static int __init cciss_init(void)
        printk(KERN_INFO DRIVER_NAME "\n");
 
        /* Register for our PCI devices */
-       return pci_module_init(&cciss_pci_driver);
+       return pci_register_driver(&cciss_pci_driver);
 }
 
 static void __exit cciss_cleanup(void)