fbdev: move FBIO_WAITFORVSYNC to linux/fb.h
[safe/jmp/linux-2.6] / drivers / block / cciss.c
index 429b9b6..51ceaee 100644 (file)
@@ -179,19 +179,17 @@ 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 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,
-                  __u8 page_code, unsigned char *scsi3addr, int cmd_type);
 static int sendcmd_withirq(__u8 cmd, int ctlr, void *buff, size_t size,
                        __u8 page_code, unsigned char scsi3addr[],
                        int cmd_type);
@@ -259,6 +257,79 @@ static inline void removeQ(CommandList_struct *c)
        hlist_del_init(&c->list);
 }
 
+static void cciss_free_sg_chain_blocks(SGDescriptor_struct **cmd_sg_list,
+       int nr_cmds)
+{
+       int i;
+
+       if (!cmd_sg_list)
+               return;
+       for (i = 0; i < nr_cmds; i++) {
+               kfree(cmd_sg_list[i]);
+               cmd_sg_list[i] = NULL;
+       }
+       kfree(cmd_sg_list);
+}
+
+static SGDescriptor_struct **cciss_allocate_sg_chain_blocks(
+       ctlr_info_t *h, int chainsize, int nr_cmds)
+{
+       int j;
+       SGDescriptor_struct **cmd_sg_list;
+
+       if (chainsize <= 0)
+               return NULL;
+
+       cmd_sg_list = kmalloc(sizeof(*cmd_sg_list) * nr_cmds, GFP_KERNEL);
+       if (!cmd_sg_list)
+               return NULL;
+
+       /* Build up chain blocks for each command */
+       for (j = 0; j < nr_cmds; j++) {
+               /* Need a block of chainsized s/g elements. */
+               cmd_sg_list[j] = kmalloc((chainsize *
+                       sizeof(*cmd_sg_list[j])), GFP_KERNEL);
+               if (!cmd_sg_list[j]) {
+                       dev_err(&h->pdev->dev, "Cannot get memory "
+                               "for s/g chains.\n");
+                       goto clean;
+               }
+       }
+       return cmd_sg_list;
+clean:
+       cciss_free_sg_chain_blocks(cmd_sg_list, nr_cmds);
+       return NULL;
+}
+
+static void cciss_unmap_sg_chain_block(ctlr_info_t *h, CommandList_struct *c)
+{
+       SGDescriptor_struct *chain_sg;
+       u64bit temp64;
+
+       if (c->Header.SGTotal <= h->max_cmd_sgentries)
+               return;
+
+       chain_sg = &c->SG[h->max_cmd_sgentries - 1];
+       temp64.val32.lower = chain_sg->Addr.lower;
+       temp64.val32.upper = chain_sg->Addr.upper;
+       pci_unmap_single(h->pdev, temp64.val, chain_sg->Len, PCI_DMA_TODEVICE);
+}
+
+static void cciss_map_sg_chain_block(ctlr_info_t *h, CommandList_struct *c,
+       SGDescriptor_struct *chain_block, int len)
+{
+       SGDescriptor_struct *chain_sg;
+       u64bit temp64;
+
+       chain_sg = &c->SG[h->max_cmd_sgentries - 1];
+       chain_sg->Ext = CCISS_SG_CHAIN;
+       chain_sg->Len = len;
+       temp64.val = pci_map_single(h->pdev, chain_block, len,
+                               PCI_DMA_TODEVICE);
+       chain_sg->Addr.lower = temp64.val32.lower;
+       chain_sg->Addr.upper = temp64.val32.upper;
+}
+
 #include "cciss_scsi.c"                /* For SCSI tape support */
 
 static const char *raid_label[] = { "0", "4", "1(1+0)", "5", "5+1", "ADG",
@@ -339,6 +410,9 @@ static int cciss_seq_show(struct seq_file *seq, void *v)
        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;
 
@@ -424,12 +498,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 */
@@ -1346,26 +1417,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,
@@ -1373,7 +1445,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;
 
@@ -1516,7 +1588,6 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
 
                        c->Request = ioc->Request;
                        if (ioc->buf_size > 0) {
-                               int i;
                                for (i = 0; i < sg_used; i++) {
                                        temp64.val =
                                            pci_map_single(host->pdev, buff[i],
@@ -1657,9 +1728,11 @@ static void cciss_softirq_done(struct request *rq)
 {
        CommandList_struct *cmd = rq->completion_data;
        ctlr_info_t *h = hba[cmd->ctlr];
+       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;
@@ -1669,9 +1742,17 @@ 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) {
+                       cciss_unmap_sg_chain_block(h, cmd);
+                       /* Point to the next block */
+                       curr_sg = h->cmd_sg_list[cmd->cmdindex];
+                       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
@@ -1701,7 +1782,7 @@ static inline void log_unit_to_scsi3addr(ctlr_info_t *h,
  * 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, int withirq,
+static void cciss_get_device_descr(int ctlr, int logvol,
                                   char *vendor, char *model, char *rev)
 {
        int rc;
@@ -1717,14 +1798,8 @@ static void cciss_get_device_descr(int ctlr, int logvol, int withirq,
                return;
 
        log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
-       if (withirq)
-               rc = sendcmd_withirq(CISS_INQUIRY, ctlr, inq_buf,
-                            sizeof(InquiryData_struct), 0,
-                               scsi3addr, TYPE_CMD);
-       else
-               rc = sendcmd(CISS_INQUIRY, ctlr, inq_buf,
-                            sizeof(InquiryData_struct), 0,
-                               scsi3addr, TYPE_CMD);
+       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';
@@ -1743,7 +1818,7 @@ static void cciss_get_device_descr(int ctlr, int logvol, int withirq,
  * 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
@@ -1759,12 +1834,8 @@ static void cciss_get_serial_no(int ctlr, int logvol, int withirq,
                return;
        memset(serial_no, 0, buflen);
        log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
-       if (withirq)
-               rc = sendcmd_withirq(CISS_INQUIRY, ctlr, buf,
-                       PAGE_83_INQ_BYTES, 0x83, scsi3addr, TYPE_CMD);
-       else
-               rc = sendcmd(CISS_INQUIRY, ctlr, buf,
-                       PAGE_83_INQ_BYTES, 0x83, scsi3addr, TYPE_CMD);
+       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);
@@ -1793,12 +1864,9 @@ static int cciss_add_disk(ctlr_info_t *h, struct gendisk *disk,
        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_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_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);
 
@@ -1852,18 +1920,16 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time,
 
        /* 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;
@@ -1873,14 +1939,14 @@ 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_device_descr(ctlr, drv_index, 1, drvinfo->vendor,
+       cciss_get_device_descr(ctlr, drv_index, drvinfo->vendor,
                                drvinfo->model, drvinfo->rev);
-       cciss_get_serial_no(ctlr, drv_index, 1, drvinfo->serial_no,
+       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,
@@ -2367,7 +2433,7 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
 
        /* if it was the last disk, find the new hightest lun */
        if (clear_all && recalculate_highest_lun) {
-               int i, newhighest = -1;
+               int 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)
@@ -2424,7 +2490,7 @@ static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff,
                        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;
@@ -2674,7 +2740,7 @@ static int sendcmd_withirq(__u8 cmd, int ctlr, void *buff, size_t 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)
@@ -2685,21 +2751,15 @@ static void cciss_geometry_inquiry(int ctlr, int logvol,
 
        memset(inq_buff, 0, sizeof(InquiryData_struct));
        log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
-       if (withirq)
-               return_code = sendcmd_withirq(CISS_INQUIRY, ctlr,
-                                             inq_buff, sizeof(*inq_buff),
-                                             0xC1, scsi3addr, TYPE_CMD);
-       else
-               return_code = sendcmd(CISS_INQUIRY, ctlr, inq_buff,
-                                     sizeof(*inq_buff), 0xC1, scsi3addr,
-                                     TYPE_CMD);
+       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 {
@@ -2725,7 +2785,7 @@ static void cciss_geometry_inquiry(int ctlr, int logvol,
 }
 
 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;
@@ -2739,14 +2799,8 @@ cciss_read_capacity(int ctlr, int logvol, int withirq, sector_t *total_size,
        }
 
        log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
-       if (withirq)
-               return_code = sendcmd_withirq(CCISS_READ_CAPACITY,
-                               ctlr, buf, sizeof(ReadCapdata_struct),
-                                       0, scsi3addr, TYPE_CMD);
-       else
-               return_code = sendcmd(CCISS_READ_CAPACITY,
-                               ctlr, buf, sizeof(ReadCapdata_struct),
-                                       0, scsi3addr, TYPE_CMD);
+       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);
@@ -2758,8 +2812,8 @@ cciss_read_capacity(int ctlr, int logvol, int withirq, sector_t *total_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;
@@ -2772,16 +2826,9 @@ cciss_read_capacity_16(int ctlr, int logvol, int withirq, sector_t *total_size,
        }
 
        log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
-       if (withirq) {
-               return_code = sendcmd_withirq(CCISS_READ_CAPACITY_16,
-                       ctlr, buf, sizeof(ReadCapdata_struct_16),
-                               0, scsi3addr, TYPE_CMD);
-       }
-       else {
-               return_code = sendcmd(CCISS_READ_CAPACITY_16,
-                       ctlr, buf, sizeof(ReadCapdata_struct_16),
-                               0, scsi3addr, TYPE_CMD);
-       }
+       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);
@@ -2822,13 +2869,13 @@ 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_logical_block_size(drv->queue, drv->block_size);
@@ -2839,167 +2886,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;
-}
-
-/* Send command c to controller h and poll for it to complete.
- * Turns interrupts off on the board.  Used at driver init time
- * and during SCSI error recovery.
- */
-static int sendcmd_core(ctlr_info_t *h, CommandList_struct *c)
-{
-       int i;
-       unsigned long complete;
-       int status = IO_ERROR;
-       u64bit buff_dma_handle;
-
-resend_cmd1:
-
-       /* Disable interrupt on the board. */
-       h->access.set_intr_mask(h, 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 (!(h->access.fifo_full(h)))
-                       break;
-               udelay(10);
-               printk(KERN_WARNING "cciss cciss%d: SendCmd FIFO full,"
-                      " waiting!\n", h->ctlr);
-       }
-       h->access.submit_command(h, c); /* Send the cmd */
-       do {
-               complete = pollcomplete(h->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", h->ctlr);
-                       status = IO_ERROR;
-                       break;
-               }
-
-               /* Make sure it's the command we're expecting. */
-               if ((complete & ~CISS_ERROR_BIT) != c->busaddr) {
-                       printk(KERN_WARNING "cciss%d: Unexpected command "
-                               "completion.\n", h->ctlr);
-                       continue;
-               }
-
-               /* It is our command.  If no error, we're done. */
-               if (!(complete & CISS_ERROR_BIT)) {
-                       status = IO_OK;
-                       break;
-               }
-
-               /* There is an error... */
-
-               /* 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;
-                       status = IO_OK;
-                       break;
-               }
-
-               if (c->err_info->CommandStatus == CMD_UNSOLICITED_ABORT) {
-                       printk(KERN_WARNING "cciss%d: unsolicited abort %p\n",
-                               h->ctlr, c);
-                       if (c->retry_count < MAX_CMD_RETRIES) {
-                               printk(KERN_WARNING "cciss%d: retrying %p\n",
-                                  h->ctlr, c);
-                               c->retry_count++;
-                               /* erase the old error information */
-                               memset(c->err_info, 0, sizeof(c->err_info));
-                               goto resend_cmd1;
-                       }
-                       printk(KERN_WARNING "cciss%d: retried %p too many "
-                               "times\n", h->ctlr, c);
-                       status = IO_ERROR;
-                       break;
-               }
-
-               if (c->err_info->CommandStatus == CMD_UNABORTABLE) {
-                       printk(KERN_WARNING "cciss%d: command could not be "
-                               "aborted.\n", h->ctlr);
-                       status = IO_ERROR;
-                       break;
-               }
-
-               if (c->err_info->CommandStatus == CMD_TARGET_STATUS) {
-                       status = check_target_status(h, c);
-                       break;
-               }
-
-               printk(KERN_WARNING "cciss%d: sendcmd error\n", h->ctlr);
-               printk(KERN_WARNING "cmd = 0x%02x, CommandStatus = 0x%02x\n",
-                       c->Request.CDB[0], c->err_info->CommandStatus);
-               status = IO_ERROR;
-               break;
-
-       } while (1);
-
-       /* 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(h->pdev, (dma_addr_t) buff_dma_handle.val,
-                        c->SG[0].Len, PCI_DMA_BIDIRECTIONAL);
-       return status;
-}
-
-/*
- * Send a command to the controller, and wait for it to complete.
- * Used at init time, and during SCSI error recovery.
- */
-static int sendcmd(__u8 cmd, int ctlr, void *buff, size_t size,
-       __u8 page_code, unsigned char *scsi3addr, int cmd_type)
-{
-       CommandList_struct *c;
-       int status;
-
-       c = cmd_alloc(hba[ctlr], 1);
-       if (!c) {
-               printk(KERN_WARNING "cciss: unable to get memory");
-               return IO_ERROR;
-       }
-       status = fill_cmd(c, cmd, ctlr, buff, size, page_code,
-               scsi3addr, cmd_type);
-       if (status == IO_OK)
-               status = sendcmd_core(hba[ctlr], c);
-       cmd_free(hba[ctlr], c, 1);
-       return status;
-}
-
-/*
  * Map (physical) PCI mem into (virtual) kernel space
  */
 static void __iomem *remap_pci_mem(ulong base, ulong size)
@@ -3257,9 +3143,12 @@ 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 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.
@@ -3272,13 +3161,14 @@ static void do_cciss_request(struct request_queue *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;
 
        blk_start_request(creq);
 
+       tmp_sg = h->scatter_list[c->cmdindex];
        spin_unlock_irq(q->queue_lock);
 
        c->cmd_type = CMD_RWREQ;
@@ -3286,19 +3176,19 @@ 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. */
        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.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 = blk_rq_pos(creq);
@@ -3307,7 +3197,7 @@ static void do_cciss_request(struct request_queue *q)
               (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 */
@@ -3316,33 +3206,54 @@ 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)) {
+                       /* Point to next chain block. */
+                       curr_sg = h->cmd_sg_list[c->cmdindex];
+                       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)
+               cciss_map_sg_chain_block(h, c, h->cmd_sg_list[c->cmdindex],
+                       (seg - (h->max_cmd_sgentries - 1)) *
+                               sizeof(SGDescriptor_struct));
+
        /* track how many SG entries we are using */
        if (seg > h->maxSG)
                h->maxSG = seg;
 
 #ifdef CCISS_DEBUG
-       printk(KERN_DEBUG "cciss: Submitting %u sectors in %d segments\n",
-              blk_rq_sectors(creq), 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[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;
@@ -3351,7 +3262,7 @@ static void do_cciss_request(struct request_queue *q)
 
                        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;
@@ -3429,6 +3340,7 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id)
                                        printk(KERN_WARNING
                                               "cciss: controller cciss%d failed, stopping.\n",
                                               h->ctlr);
+                                       spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
                                        fail_all_cmds(h->ctlr);
                                        return IRQ_HANDLED;
                                }
@@ -3580,13 +3492,11 @@ static int scan_thread(void *data)
                        h->busy_scanning = 1;
                        mutex_unlock(&scan_mutex);
 
-                       if (h) {
-                               rebuild_lun_table(h, 0, 0);
-                               complete_all(&h->scan_wait);
-                               mutex_lock(&scan_mutex);
-                               h->busy_scanning = 0;
-                               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);
                }
        }
 
@@ -3612,8 +3522,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);
-               add_to_scan_list(h);
-               wake_up_process(cciss_scan_thread);
+       /*
+        * 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:
@@ -3895,6 +3819,23 @@ 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));
+       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;
@@ -4221,6 +4162,7 @@ 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;
@@ -4324,6 +4266,26 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
                printk(KERN_ERR "cciss: out of memory");
                goto clean4;
        }
+
+       /* 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 = cciss_allocate_sg_chain_blocks(hba[i],
+               hba[i]->chainsize, hba[i]->nr_cmds);
+       if (!hba[i]->cmd_sg_list && hba[i]->chainsize > 0)
+               goto clean4;
+
        spin_lock_init(&hba[i]->lock);
 
        /* Initialize the pdev driver private data.
@@ -4369,7 +4331,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
 
        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;
@@ -4377,6 +4339,11 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
 
 clean4:
        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);
+       cciss_free_sg_chain_blocks(hba[i]->cmd_sg_list, hba[i]->nr_cmds);
        if (hba[i]->cmd_pool)
                pci_free_consistent(hba[i]->pdev,
                                    hba[i]->nr_cmds * sizeof(CommandList_struct),
@@ -4407,30 +4374,28 @@ clean_no_release_regions:
 
 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,
-               CTLR_LUNID, 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)
@@ -4492,6 +4457,11 @@ 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);
+       /* Free up sg elements */
+       for (j = 0; j < hba[i]->nr_cmds; j++)
+               kfree(hba[i]->scatter_list[j]);
+       kfree(hba[i]->scatter_list);
+       cciss_free_sg_chain_blocks(hba[i]->cmd_sg_list, hba[i]->nr_cmds);
        /*
         * Deliberately omit pci_disable_device(): it does something nasty to
         * Smart Array controllers that pci_enable_device does not undo
@@ -4524,7 +4494,7 @@ static int __init cciss_init(void)
         * 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);
+       BUILD_BUG_ON(sizeof(CommandList_struct) % COMMANDLIST_ALIGNMENT);
 
        printk(KERN_INFO DRIVER_NAME "\n");