|
@@ -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);
|
|
|
|