diff options
-rw-r--r-- | drivers/block/cciss.c | 88 | ||||
-rw-r--r-- | drivers/block/cciss.h | 2 | ||||
-rw-r--r-- | drivers/block/cciss_cmd.h | 23 |
3 files changed, 111 insertions, 2 deletions
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index f15b17708537..4e5441baa49d 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -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))) { @@ -2593,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; @@ -2626,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" @@ -2665,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)) { @@ -3016,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. @@ -3761,6 +3837,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: @@ -3836,6 +3917,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) { @@ -3844,6 +3926,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); diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h index 15e2b84734e3..703e08038fb9 100644 --- a/drivers/block/cciss.h +++ b/drivers/block/cciss.h @@ -121,6 +121,8 @@ struct ctlr_info struct sendcmd_reject_list scsi_rejects; #endif unsigned char alive; + struct completion *rescan_wait; + struct task_struct *cciss_scan_thread; }; /* Defining the diffent access_menthods */ diff --git a/drivers/block/cciss_cmd.h b/drivers/block/cciss_cmd.h index 24e22dea1a99..40b1b92dae7f 100644 --- a/drivers/block/cciss_cmd.h +++ b/drivers/block/cciss_cmd.h @@ -25,6 +25,29 @@ #define CMD_TIMEOUT 0x000B #define CMD_UNABORTABLE 0x000C +/* Unit Attentions ASC's as defined for the MSA2012sa */ +#define POWER_OR_RESET 0x29 +#define STATE_CHANGED 0x2a +#define UNIT_ATTENTION_CLEARED 0x2f +#define LUN_FAILED 0x3e +#define REPORT_LUNS_CHANGED 0x3f + +/* Unit Attentions ASCQ's as defined for the MSA2012sa */ + + /* These ASCQ's defined for ASC = POWER_OR_RESET */ +#define POWER_ON_RESET 0x00 +#define POWER_ON_REBOOT 0x01 +#define SCSI_BUS_RESET 0x02 +#define MSA_TARGET_RESET 0x03 +#define CONTROLLER_FAILOVER 0x04 +#define TRANSCEIVER_SE 0x05 +#define TRANSCEIVER_LVD 0x06 + + /* These ASCQ's defined for ASC = STATE_CHANGED */ +#define RESERVATION_PREEMPTED 0x03 +#define ASYM_ACCESS_CHANGED 0x06 +#define LUN_CAPACITY_CHANGED 0x09 + //transfer direction #define XFER_NONE 0x00 #define XFER_WRITE 0x01 |