Merge branch 'for-linus' of git://git.kernel.dk/linux-2.6-block
[sfrench/cifs-2.6.git] / drivers / block / cciss.c
index a6c55432819bd17fc14e42e5edd1a4cbb7219a94..0ef6f08aa6eadfbdb685704eb5fe5f039ff8e474 100644 (file)
@@ -51,6 +51,7 @@
 #include <scsi/scsi_ioctl.h>
 #include <linux/cdrom.h>
 #include <linux/scatterlist.h>
+#include <linux/kthread.h>
 
 #define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin))
 #define DRIVER_NAME "HP CISS Driver (v 3.6.20)"
@@ -186,6 +187,8 @@ static int sendcmd_withirq(__u8 cmd, int ctlr, void *buff, size_t size,
                           __u8 page_code, int cmd_type);
 
 static void fail_all_cmds(unsigned long ctlr);
+static int scan_thread(void *data);
+static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c);
 
 #ifdef CONFIG_PROC_FS
 static void cciss_procinit(int i);
@@ -735,6 +738,12 @@ static int cciss_getgeo(struct block_device *bdev, struct hd_geometry *geo)
        return 0;
 }
 
+static void check_ioctl_unit_attention(ctlr_info_t *host, CommandList_struct *c)
+{
+       if (c->err_info->CommandStatus == CMD_TARGET_STATUS &&
+                       c->err_info->ScsiStatus != SAM_STAT_CHECK_CONDITION)
+               (void)check_for_unit_attention(host, c);
+}
 /*
  * ioctl
  */
@@ -1029,6 +1038,8 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
                                         iocommand.buf_size,
                                         PCI_DMA_BIDIRECTIONAL);
 
+                       check_ioctl_unit_attention(host, c);
+
                        /* Copy the error information out */
                        iocommand.error_info = *(c->err_info);
                        if (copy_to_user
@@ -1180,6 +1191,7 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
                                        (dma_addr_t) temp64.val, buff_size[i],
                                        PCI_DMA_BIDIRECTIONAL);
                        }
+                       check_ioctl_unit_attention(host, c);
                        /* Copy the error information out */
                        ioc->error_info = *(c->err_info);
                        if (copy_to_user(argp, ioc, sizeof(*ioc))) {
@@ -1287,6 +1299,7 @@ static void cciss_softirq_done(struct request *rq)
 {
        CommandList_struct *cmd = rq->completion_data;
        ctlr_info_t *h = hba[cmd->ctlr];
+       unsigned int nr_bytes;
        unsigned long flags;
        u64bit temp64;
        int i, ddir;
@@ -1308,7 +1321,14 @@ static void cciss_softirq_done(struct request *rq)
        printk("Done with %p\n", rq);
 #endif                         /* CCISS_DEBUG */
 
-       if (blk_end_request(rq, (rq->errors == 0) ? 0 : -EIO, blk_rq_bytes(rq)))
+       /*
+        * Store the full size and set the residual count for pc requests
+        */
+       nr_bytes = blk_rq_bytes(rq);
+       if (blk_pc_request(rq))
+               rq->data_len = cmd->err_info->ResidualCnt;
+
+       if (blk_end_request(rq, (rq->errors == 0) ? 0 : -EIO, nr_bytes))
                BUG();
 
        spin_lock_irqsave(&h->lock, flags);
@@ -2585,12 +2605,14 @@ static inline unsigned int make_status_bytes(unsigned int scsi_status_byte,
                ((driver_byte & 0xff) << 24);
 }
 
-static inline int evaluate_target_status(CommandList_struct *cmd)
+static inline int evaluate_target_status(ctlr_info_t *h,
+                       CommandList_struct *cmd, int *retry_cmd)
 {
        unsigned char sense_key;
        unsigned char status_byte, msg_byte, host_byte, driver_byte;
        int error_value;
 
+       *retry_cmd = 0;
        /* If we get in here, it means we got "target status", that is, scsi status */
        status_byte = cmd->err_info->ScsiStatus;
        driver_byte = DRIVER_OK;
@@ -2618,6 +2640,11 @@ static inline int evaluate_target_status(CommandList_struct *cmd)
        if (((sense_key == 0x0) || (sense_key == 0x1)) && !blk_pc_request(cmd->rq))
                error_value = 0;
 
+       if (check_for_unit_attention(h, cmd)) {
+               *retry_cmd = !blk_pc_request(cmd->rq);
+               return 0;
+       }
+
        if (!blk_pc_request(cmd->rq)) { /* Not SG_IO or similar? */
                if (error_value != 0)
                        printk(KERN_WARNING "cciss: cmd %p has CHECK CONDITION"
@@ -2657,7 +2684,7 @@ static inline void complete_command(ctlr_info_t *h, CommandList_struct *cmd,
 
        switch (cmd->err_info->CommandStatus) {
        case CMD_TARGET_STATUS:
-               rq->errors = evaluate_target_status(cmd);
+               rq->errors = evaluate_target_status(h, cmd, &retry_cmd);
                break;
        case CMD_DATA_UNDERRUN:
                if (blk_fs_request(cmd->rq)) {
@@ -3008,6 +3035,63 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+static int scan_thread(void *data)
+{
+       ctlr_info_t *h = data;
+       int rc;
+       DECLARE_COMPLETION_ONSTACK(wait);
+       h->rescan_wait = &wait;
+
+       for (;;) {
+               rc = wait_for_completion_interruptible(&wait);
+               if (kthread_should_stop())
+                       break;
+               if (!rc)
+                       rebuild_lun_table(h, 0);
+       }
+       return 0;
+}
+
+static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c)
+{
+       if (c->err_info->SenseInfo[2] != UNIT_ATTENTION)
+               return 0;
+
+       switch (c->err_info->SenseInfo[12]) {
+       case STATE_CHANGED:
+               printk(KERN_WARNING "cciss%d: a state change "
+                       "detected, command retried\n", h->ctlr);
+               return 1;
+       break;
+       case LUN_FAILED:
+               printk(KERN_WARNING "cciss%d: LUN failure "
+                       "detected, action required\n", h->ctlr);
+               return 1;
+       break;
+       case REPORT_LUNS_CHANGED:
+               printk(KERN_WARNING "cciss%d: report LUN data "
+                       "changed\n", h->ctlr);
+               if (h->rescan_wait)
+                       complete(h->rescan_wait);
+               return 1;
+       break;
+       case POWER_OR_RESET:
+               printk(KERN_WARNING "cciss%d: a power on "
+                       "or device reset detected\n", h->ctlr);
+               return 1;
+       break;
+       case UNIT_ATTENTION_CLEARED:
+               printk(KERN_WARNING "cciss%d: unit attention "
+                   "cleared by another initiator\n", h->ctlr);
+               return 1;
+       break;
+       default:
+               printk(KERN_WARNING "cciss%d: unknown "
+                       "unit attention detected\n", h->ctlr);
+                               return 1;
+       }
+}
+
 /*
  *  We cannot read the structure directly, for portability we must use
  *   the io functions.
@@ -3181,12 +3265,21 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
  */
        cciss_interrupt_mode(c, pdev, board_id);
 
-       /*
-        * Memory base addr is first addr , the second points to the config
-        *   table
-        */
+       /* find the memory BAR */
+       for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
+               if (pci_resource_flags(pdev, i) & IORESOURCE_MEM)
+                       break;
+       }
+       if (i == DEVICE_COUNT_RESOURCE) {
+               printk(KERN_WARNING "cciss: No memory BAR found\n");
+               err = -ENODEV;
+               goto err_out_free_res;
+       }
+
+       c->paddr = pci_resource_start(pdev, i); /* addressing mode bits
+                                                * already removed
+                                                */
 
-       c->paddr = pci_resource_start(pdev, 0); /* addressing mode bits already removed */
 #ifdef CCISS_DEBUG
        printk("address 0 = %lx\n", c->paddr);
 #endif                         /* CCISS_DEBUG */
@@ -3753,6 +3846,11 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
        hba[i]->busy_initializing = 0;
 
        rebuild_lun_table(hba[i], 1);
+       hba[i]->cciss_scan_thread = kthread_run(scan_thread, hba[i],
+                               "cciss_scan%02d", i);
+       if (IS_ERR(hba[i]->cciss_scan_thread))
+               return PTR_ERR(hba[i]->cciss_scan_thread);
+
        return 1;
 
 clean4:
@@ -3828,6 +3926,7 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev)
                printk(KERN_ERR "cciss: Unable to remove device \n");
                return;
        }
+
        tmp_ptr = pci_get_drvdata(pdev);
        i = tmp_ptr->ctlr;
        if (hba[i] == NULL) {
@@ -3836,6 +3935,8 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev)
                return;
        }
 
+       kthread_stop(hba[i]->cciss_scan_thread);
+
        remove_proc_entry(hba[i]->devname, proc_cciss);
        unregister_blkdev(hba[i]->major, hba[i]->devname);