]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/block/cciss.c
cciss: set rq->errors more correctly in driver
[linux-2.6-omap-h63xx.git] / drivers / block / cciss.c
index 05dfe357527c231037365be190baef05b39ef5f3..4ec3e75ba4742ebae0e7c35b1a0241a1278281fb 100644 (file)
@@ -45,6 +45,9 @@
 #include <linux/blkdev.h>
 #include <linux/genhd.h>
 #include <linux/completion.h>
+#include <scsi/sg.h>
+#include <scsi/scsi_ioctl.h>
+#include <linux/cdrom.h>
 
 #define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin))
 #define DRIVER_NAME "HP CISS Driver (v 3.6.14)"
@@ -1152,6 +1155,30 @@ static int cciss_ioctl(struct inode *inode, struct file *filep,
                        kfree(ioc);
                        return status;
                }
+
+       /* scsi_cmd_ioctl handles these, below, though some are not */
+       /* very meaningful for cciss.  SG_IO is the main one people want. */
+
+       case SG_GET_VERSION_NUM:
+       case SG_SET_TIMEOUT:
+       case SG_GET_TIMEOUT:
+       case SG_GET_RESERVED_SIZE:
+       case SG_SET_RESERVED_SIZE:
+       case SG_EMULATED_HOST:
+       case SG_IO:
+       case SCSI_IOCTL_SEND_COMMAND:
+               return scsi_cmd_ioctl(filep, disk, cmd, argp);
+
+       /* scsi_cmd_ioctl would normally handle these, below, but */
+       /* they aren't a good fit for cciss, as CD-ROMs are */
+       /* not supported, and we don't have any bus/target/lun */
+       /* which we present to the kernel. */
+
+       case CDROM_SEND_PACKET:
+       case CDROMCLOSETRAY:
+       case CDROMEJECT:
+       case SCSI_IOCTL_GET_IDLUN:
+       case SCSI_IOCTL_GET_BUS_NUMBER:
        default:
                return -ENOTTY;
        }
@@ -1234,7 +1261,7 @@ static void cciss_softirq_done(struct request *rq)
                pci_unmap_page(h->pdev, temp64.val, cmd->SG[i].Len, ddir);
        }
 
-       complete_buffers(rq->bio, rq->errors);
+       complete_buffers(rq->bio, (rq->errors == 0));
 
        if (blk_fs_request(rq)) {
                const int rw = rq_data_dir(rq);
@@ -1248,7 +1275,7 @@ static void cciss_softirq_done(struct request *rq)
 
        add_disk_randomness(rq->rq_disk);
        spin_lock_irqsave(&h->lock, flags);
-       end_that_request_last(rq, rq->errors);
+       end_that_request_last(rq, (rq->errors == 0));
        cmd_free(h, cmd, 1);
        cciss_check_queues(h);
        spin_unlock_irqrestore(&h->lock, flags);
@@ -1291,13 +1318,19 @@ static void cciss_update_drive_info(int ctlr, int drv_index)
        if (inq_buff == NULL)
                goto mem_msg;
 
+       /* 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,
+                       &total_size, &block_size);
+               goto geo_inq;
+       }
+
        cciss_read_capacity(ctlr, drv_index, 1,
                            &total_size, &block_size);
 
-       /* total size = last LBA + 1 */
-       /* FFFFFFFF + 1 = 0, cannot have a logical volume of size 0 */
-       /* so we assume this volume this must be >2TB in size */
-       if (total_size == (__u32) 0) {
+       /* 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,
                &total_size, &block_size);
                h->cciss_read = CCISS_READ_16;
@@ -1306,6 +1339,7 @@ static void cciss_update_drive_info(int ctlr, int drv_index)
                h->cciss_read = CCISS_READ_10;
                h->cciss_write = CCISS_WRITE_10;
        }
+geo_inq:
        cciss_geometry_inquiry(ctlr, drv_index, 1, total_size, block_size,
                               inq_buff, &h->drv[drv_index]);
 
@@ -1432,7 +1466,7 @@ static int rebuild_lun_table(ctlr_info_t *h, struct gendisk *del_disk)
 
                if (return_code == IO_OK) {
                        listlength =
-                               be32_to_cpu(*(__u32 *) ld_buff->LUNListLength);
+                               be32_to_cpu(*(__be32 *) ld_buff->LUNListLength);
                } else {        /* reading number of logical volumes failed */
                        printk(KERN_WARNING "cciss: report logical volume"
                               " command failed\n");
@@ -1908,6 +1942,7 @@ static void cciss_geometry_inquiry(int ctlr, int logvol,
                               "does not support reading geometry\n");
                        drv->heads = 255;
                        drv->sectors = 32;      // Sectors per track
+                       drv->cylinders = total_size + 1;
                        drv->raid_level = RAID_UNKNOWN;
                } else {
                        drv->heads = inq_buff->data_byte[6];
@@ -1917,13 +1952,14 @@ static void cciss_geometry_inquiry(int ctlr, int logvol,
                        drv->raid_level = inq_buff->data_byte[8];
                }
                drv->block_size = block_size;
-               drv->nr_blocks = total_size;
+               drv->nr_blocks = total_size + 1;
                t = drv->heads * drv->sectors;
                if (t > 1) {
-                       unsigned rem = sector_div(total_size, t);
+                       sector_t real_size = total_size + 1;
+                       unsigned long rem = sector_div(real_size, t);
                        if (rem)
-                               total_size++;
-                       drv->cylinders = total_size;
+                               real_size++;
+                       drv->cylinders = real_size;
                }
        } else {                /* Get geometry failed */
                printk(KERN_WARNING "cciss: reading geometry failed\n");
@@ -1953,16 +1989,16 @@ cciss_read_capacity(int ctlr, int logvol, int withirq, sector_t *total_size,
                                ctlr, buf, sizeof(ReadCapdata_struct),
                                        1, logvol, 0, NULL, TYPE_CMD);
        if (return_code == IO_OK) {
-               *total_size = be32_to_cpu(*(__u32 *) buf->total_size)+1;
-               *block_size = be32_to_cpu(*(__u32 *) buf->block_size);
+               *total_size = be32_to_cpu(*(__be32 *) buf->total_size);
+               *block_size = be32_to_cpu(*(__be32 *) buf->block_size);
        } else {                /* read capacity command failed */
                printk(KERN_WARNING "cciss: read capacity failed\n");
                *total_size = 0;
                *block_size = BLOCK_SIZE;
        }
-       if (*total_size != (__u32) 0)
+       if (*total_size != 0)
                printk(KERN_INFO "      blocks= %llu block_size= %d\n",
-               (unsigned long long)*total_size, *block_size);
+               (unsigned long long)*total_size+1, *block_size);
        kfree(buf);
        return;
 }
@@ -1989,15 +2025,15 @@ cciss_read_capacity_16(int ctlr, int logvol, int withirq, sector_t *total_size,
                                1, logvol, 0, NULL, TYPE_CMD);
        }
        if (return_code == IO_OK) {
-               *total_size = be64_to_cpu(*(__u64 *) buf->total_size)+1;
-               *block_size = be32_to_cpu(*(__u32 *) buf->block_size);
+               *total_size = be64_to_cpu(*(__be64 *) buf->total_size);
+               *block_size = be32_to_cpu(*(__be32 *) buf->block_size);
        } else {                /* read capacity command failed */
                printk(KERN_WARNING "cciss: read capacity failed\n");
                *total_size = 0;
                *block_size = BLOCK_SIZE;
        }
        printk(KERN_INFO "      blocks= %llu block_size= %d\n",
-              (unsigned long long)*total_size, *block_size);
+              (unsigned long long)*total_size+1, *block_size);
        kfree(buf);
        return;
 }
@@ -2327,6 +2363,44 @@ static inline void resend_cciss_cmd(ctlr_info_t *h, CommandList_struct *c)
        start_io(h);
 }
 
+static inline int evaluate_target_status(CommandList_struct *cmd)
+{
+       unsigned char sense_key;
+       int error_count = 1;
+
+       if (cmd->err_info->ScsiStatus != 0x02) { /* not check condition? */
+               if (!blk_pc_request(cmd->rq))
+                       printk(KERN_WARNING "cciss: cmd %p "
+                              "has SCSI Status 0x%x\n",
+                              cmd, cmd->err_info->ScsiStatus);
+               return error_count;
+       }
+
+       /* check the sense key */
+       sense_key = 0xf & cmd->err_info->SenseInfo[2];
+       /* no status or recovered error */
+       if ((sense_key == 0x0) || (sense_key == 0x1))
+               error_count = 0;
+
+       if (!blk_pc_request(cmd->rq)) { /* Not SG_IO or similar? */
+               if (error_count != 0)
+                       printk(KERN_WARNING "cciss: cmd %p has CHECK CONDITION"
+                              " sense key = 0x%x\n", cmd, sense_key);
+               return error_count;
+       }
+
+       /* SG_IO or similar, copy sense data back */
+       if (cmd->rq->sense) {
+               if (cmd->rq->sense_len > cmd->err_info->SenseLen)
+                       cmd->rq->sense_len = cmd->err_info->SenseLen;
+               memcpy(cmd->rq->sense, cmd->err_info->SenseInfo,
+                       cmd->rq->sense_len);
+       } else
+               cmd->rq->sense_len = 0;
+
+       return error_count;
+}
+
 /* checks the status of the job and calls complete buffers to mark all
  * buffers for the completed job. Note that this function does not need
  * to hold the hba/queue lock.
@@ -2334,109 +2408,99 @@ static inline void resend_cciss_cmd(ctlr_info_t *h, CommandList_struct *c)
 static inline void complete_command(ctlr_info_t *h, CommandList_struct *cmd,
                                    int timeout)
 {
-       int status = 1;
        int retry_cmd = 0;
+       struct request *rq = cmd->rq;
+
+       rq->errors = 0;
 
        if (timeout)
-               status = 0;
+               rq->errors = 1;
 
-       if (cmd->err_info->CommandStatus != 0) {        /* an error has occurred */
-               switch (cmd->err_info->CommandStatus) {
-                       unsigned char sense_key;
-               case CMD_TARGET_STATUS:
-                       status = 0;
+       if (cmd->err_info->CommandStatus == 0)  /* no error has occurred */
+               goto after_error_processing;
 
-                       if (cmd->err_info->ScsiStatus == 0x02) {
-                               printk(KERN_WARNING "cciss: cmd %p "
-                                      "has CHECK CONDITION "
-                                      " byte 2 = 0x%x\n", cmd,
-                                      cmd->err_info->SenseInfo[2]
-                                   );
-                               /* check the sense key */
-                               sense_key = 0xf & cmd->err_info->SenseInfo[2];
-                               /* no status or recovered error */
-                               if ((sense_key == 0x0) || (sense_key == 0x1)) {
-                                       status = 1;
-                               }
-                       } else {
-                               printk(KERN_WARNING "cciss: cmd %p "
-                                      "has SCSI Status 0x%x\n",
-                                      cmd, cmd->err_info->ScsiStatus);
-                       }
-                       break;
-               case CMD_DATA_UNDERRUN:
+       switch (cmd->err_info->CommandStatus) {
+       case CMD_TARGET_STATUS:
+               rq->errors = evaluate_target_status(cmd);
+               break;
+       case CMD_DATA_UNDERRUN:
+               if (blk_fs_request(cmd->rq)) {
                        printk(KERN_WARNING "cciss: cmd %p has"
                               " completed with data underrun "
                               "reported\n", cmd);
-                       break;
-               case CMD_DATA_OVERRUN:
+                       cmd->rq->data_len = cmd->err_info->ResidualCnt;
+               }
+               break;
+       case CMD_DATA_OVERRUN:
+               if (blk_fs_request(cmd->rq))
                        printk(KERN_WARNING "cciss: cmd %p has"
                               " completed with data overrun "
                               "reported\n", cmd);
-                       break;
-               case CMD_INVALID:
-                       printk(KERN_WARNING "cciss: cmd %p is "
-                              "reported invalid\n", cmd);
-                       status = 0;
-                       break;
-               case CMD_PROTOCOL_ERR:
-                       printk(KERN_WARNING "cciss: cmd %p has "
-                              "protocol error \n", cmd);
-                       status = 0;
-                       break;
-               case CMD_HARDWARE_ERR:
-                       printk(KERN_WARNING "cciss: cmd %p had "
-                              " hardware error\n", cmd);
-                       status = 0;
-                       break;
-               case CMD_CONNECTION_LOST:
-                       printk(KERN_WARNING "cciss: cmd %p had "
-                              "connection lost\n", cmd);
-                       status = 0;
-                       break;
-               case CMD_ABORTED:
-                       printk(KERN_WARNING "cciss: cmd %p was "
-                              "aborted\n", cmd);
-                       status = 0;
-                       break;
-               case CMD_ABORT_FAILED:
-                       printk(KERN_WARNING "cciss: cmd %p reports "
-                              "abort failed\n", cmd);
-                       status = 0;
-                       break;
-               case CMD_UNSOLICITED_ABORT:
-                       printk(KERN_WARNING "cciss%d: unsolicited "
-                              "abort %p\n", h->ctlr, cmd);
-                       if (cmd->retry_count < MAX_CMD_RETRIES) {
-                               retry_cmd = 1;
-                               printk(KERN_WARNING
-                                      "cciss%d: retrying %p\n", h->ctlr, cmd);
-                               cmd->retry_count++;
-                       } else
-                               printk(KERN_WARNING
-                                      "cciss%d: %p retried too "
-                                      "many times\n", h->ctlr, cmd);
-                       status = 0;
-                       break;
-               case CMD_TIMEOUT:
-                       printk(KERN_WARNING "cciss: cmd %p timedout\n", cmd);
-                       status = 0;
-                       break;
-               default:
-                       printk(KERN_WARNING "cciss: cmd %p returned "
-                              "unknown status %x\n", cmd,
-                              cmd->err_info->CommandStatus);
-                       status = 0;
-               }
+               break;
+       case CMD_INVALID:
+               printk(KERN_WARNING "cciss: cmd %p is "
+                      "reported invalid\n", cmd);
+               rq->errors = 1;
+               break;
+       case CMD_PROTOCOL_ERR:
+               printk(KERN_WARNING "cciss: cmd %p has "
+                      "protocol error \n", cmd);
+               rq->errors = 1;
+               break;
+       case CMD_HARDWARE_ERR:
+               printk(KERN_WARNING "cciss: cmd %p had "
+                      " hardware error\n", cmd);
+               rq->errors = 1;
+               break;
+       case CMD_CONNECTION_LOST:
+               printk(KERN_WARNING "cciss: cmd %p had "
+                      "connection lost\n", cmd);
+               rq->errors = 1;
+               break;
+       case CMD_ABORTED:
+               printk(KERN_WARNING "cciss: cmd %p was "
+                      "aborted\n", cmd);
+               rq->errors = 1;
+               break;
+       case CMD_ABORT_FAILED:
+               printk(KERN_WARNING "cciss: cmd %p reports "
+                      "abort failed\n", cmd);
+               rq->errors = 1;
+               break;
+       case CMD_UNSOLICITED_ABORT:
+               printk(KERN_WARNING "cciss%d: unsolicited "
+                      "abort %p\n", h->ctlr, cmd);
+               if (cmd->retry_count < MAX_CMD_RETRIES) {
+                       retry_cmd = 1;
+                       printk(KERN_WARNING
+                              "cciss%d: retrying %p\n", h->ctlr, cmd);
+                       cmd->retry_count++;
+               } else
+                       printk(KERN_WARNING
+                              "cciss%d: %p retried too "
+                              "many times\n", h->ctlr, cmd);
+               rq->errors = 1;
+               break;
+       case CMD_TIMEOUT:
+               printk(KERN_WARNING "cciss: cmd %p timedout\n", cmd);
+               rq->errors = 1;
+               break;
+       default:
+               printk(KERN_WARNING "cciss: cmd %p returned "
+                      "unknown status %x\n", cmd,
+                      cmd->err_info->CommandStatus);
+               rq->errors = 1;
        }
+
+after_error_processing:
+
        /* We need to return this command */
        if (retry_cmd) {
                resend_cciss_cmd(h, cmd);
                return;
        }
-
+       cmd->rq->data_len = 0;
        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);
 }
@@ -2530,32 +2594,40 @@ static void do_cciss_request(request_queue_t *q)
 #endif                         /* CCISS_DEBUG */
 
        c->Header.SGList = c->Header.SGTotal = seg;
-       if(h->cciss_read == CCISS_READ_10) {
-               c->Request.CDB[1] = 0;
-               c->Request.CDB[2] = (start_blk >> 24) & 0xff;   //MSB
-               c->Request.CDB[3] = (start_blk >> 16) & 0xff;
-               c->Request.CDB[4] = (start_blk >> 8) & 0xff;
-               c->Request.CDB[5] = start_blk & 0xff;
-               c->Request.CDB[6] = 0;  // (sect >> 24) & 0xff; MSB
-               c->Request.CDB[7] = (creq->nr_sectors >> 8) & 0xff;
-               c->Request.CDB[8] = creq->nr_sectors & 0xff;
-               c->Request.CDB[9] = c->Request.CDB[11] = c->Request.CDB[12] = 0;
+       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[3] = (start_blk >> 16) & 0xff;
+                       c->Request.CDB[4] = (start_blk >> 8) & 0xff;
+                       c->Request.CDB[5] = start_blk & 0xff;
+                       c->Request.CDB[6] = 0;  // (sect >> 24) & 0xff; MSB
+                       c->Request.CDB[7] = (creq->nr_sectors >> 8) & 0xff;
+                       c->Request.CDB[8] = creq->nr_sectors & 0xff;
+                       c->Request.CDB[9] = c->Request.CDB[11] = c->Request.CDB[12] = 0;
+               } else {
+                       c->Request.CDBLen = 16;
+                       c->Request.CDB[1]= 0;
+                       c->Request.CDB[2]= (start_blk >> 56) & 0xff;    //MSB
+                       c->Request.CDB[3]= (start_blk >> 48) & 0xff;
+                       c->Request.CDB[4]= (start_blk >> 40) & 0xff;
+                       c->Request.CDB[5]= (start_blk >> 32) & 0xff;
+                       c->Request.CDB[6]= (start_blk >> 24) & 0xff;
+                       c->Request.CDB[7]= (start_blk >> 16) & 0xff;
+                       c->Request.CDB[8]= (start_blk >>  8) & 0xff;
+                       c->Request.CDB[9]= start_blk & 0xff;
+                       c->Request.CDB[10]= (creq->nr_sectors >>  24) & 0xff;
+                       c->Request.CDB[11]= (creq->nr_sectors >>  16) & 0xff;
+                       c->Request.CDB[12]= (creq->nr_sectors >>  8) & 0xff;
+                       c->Request.CDB[13]= creq->nr_sectors & 0xff;
+                       c->Request.CDB[14] = c->Request.CDB[15] = 0;
+               }
+       } else if (blk_pc_request(creq)) {
+               c->Request.CDBLen = creq->cmd_len;
+               memcpy(c->Request.CDB, creq->cmd, BLK_MAX_CDB);
        } else {
-               c->Request.CDBLen = 16;
-               c->Request.CDB[1]= 0;
-               c->Request.CDB[2]= (start_blk >> 56) & 0xff;    //MSB
-               c->Request.CDB[3]= (start_blk >> 48) & 0xff;
-               c->Request.CDB[4]= (start_blk >> 40) & 0xff;
-               c->Request.CDB[5]= (start_blk >> 32) & 0xff;
-               c->Request.CDB[6]= (start_blk >> 24) & 0xff;
-               c->Request.CDB[7]= (start_blk >> 16) & 0xff;
-               c->Request.CDB[8]= (start_blk >>  8) & 0xff;
-               c->Request.CDB[9]= start_blk & 0xff;
-               c->Request.CDB[10]= (creq->nr_sectors >>  24) & 0xff;
-               c->Request.CDB[11]= (creq->nr_sectors >>  16) & 0xff;
-               c->Request.CDB[12]= (creq->nr_sectors >>  8) & 0xff;
-               c->Request.CDB[13]= creq->nr_sectors & 0xff;
-               c->Request.CDB[14] = c->Request.CDB[15] = 0;
+               printk(KERN_WARNING "cciss%d: bad request type %d\n", h->ctlr, creq->cmd_type);
+               BUG();
        }
 
        spin_lock_irq(q->queue_lock);
@@ -3119,8 +3191,9 @@ static void cciss_getgeometry(int cntl_num)
                }
                cciss_read_capacity(cntl_num, i, 0, &total_size, &block_size);
 
-               /* total_size = last LBA + 1 */
-               if(total_size == (__u32) 0) {
+               /* If read_capacity returns all F's the logical is >2TB */
+               /* so we switch to 16-byte CDBs for all read/write ops */
+               if(total_size == 0xFFFFFFFFULL) {
                        cciss_read_capacity_16(cntl_num, i, 0,
                        &total_size, &block_size);
                        hba[cntl_num]->cciss_read = CCISS_READ_16;
@@ -3395,7 +3468,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
        return -1;
 }
 
-static void __devexit cciss_remove_one(struct pci_dev *pdev)
+static void cciss_remove_one(struct pci_dev *pdev)
 {
        ctlr_info_t *tmp_ptr;
        int i, j;
@@ -3413,15 +3486,35 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev)
                       "already be removed \n");
                return;
        }
+
+       remove_proc_entry(hba[i]->devname, proc_cciss);
+       unregister_blkdev(hba[i]->major, hba[i]->devname);
+
+       /* remove it from the disk list */
+       for (j = 0; j < CISS_MAX_LUN; j++) {
+               struct gendisk *disk = hba[i]->gendisk[j];
+               if (disk) {
+                       request_queue_t *q = disk->queue;
+
+                       if (disk->flags & GENHD_FL_UP)
+                               del_gendisk(disk);
+                       if (q)
+                               blk_cleanup_queue(q);
+               }
+       }
+
+       cciss_unregister_scsi(i);       /* unhook from SCSI subsystem */
+
        /* Turn board interrupts off  and send the flush cache command */
        /* sendcmd will turn off interrupt, and send the flush...
         * To write all data in the battery backed cache to disks */
        memset(flush_buf, 0, 4);
        return_code = sendcmd(CCISS_CACHE_FLUSH, i, flush_buf, 4, 0, 0, 0, NULL,
                              TYPE_CMD);
-       if (return_code != IO_OK) {
-               printk(KERN_WARNING "Error Flushing cache on controller %d\n",
-                      i);
+       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]);
 
@@ -3433,22 +3526,6 @@ static void __devexit cciss_remove_one(struct pci_dev *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);
-       remove_proc_entry(hba[i]->devname, proc_cciss);
-
-       /* remove it from the disk list */
-       for (j = 0; j < CISS_MAX_LUN; j++) {
-               struct gendisk *disk = hba[i]->gendisk[j];
-               if (disk) {
-                       request_queue_t *q = disk->queue;
-
-                       if (disk->flags & GENHD_FL_UP)
-                               del_gendisk(disk);
-                       if (q)
-                               blk_cleanup_queue(q);
-               }
-       }
 
        pci_free_consistent(hba[i]->pdev, hba[i]->nr_cmds * sizeof(CommandList_struct),
                            hba[i]->cmd_pool, hba[i]->cmd_pool_dhandle);
@@ -3472,6 +3549,7 @@ static struct pci_driver cciss_pci_driver = {
        .probe = cciss_init_one,
        .remove = __devexit_p(cciss_remove_one),
        .id_table = cciss_pci_device_id,        /* id_table */
+       .shutdown = cciss_remove_one,
 };
 
 /*