diff options
author | Vikas Chaudhary <vikas.chaudhary@qlogic.com> | 2010-04-28 02:12:24 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@suse.de> | 2010-05-02 15:40:12 -0400 |
commit | 09a0f719896659a6c32df11426e55795012c06ff (patch) | |
tree | d80dce19cc98f247970c98a0b003fa1f97f98764 /drivers/scsi/qla4xxx | |
parent | 5369887a95da9509163931b21f61a94da09dac15 (diff) |
[SCSI] qla4xxx: added support for abort task management command
* Handles SCSI command aborts.
* Serialization srb between error handler and command
completion path.
Signed-off-by: Vikas Chaudhary <vikas.chaudhary@qlogic.com>
Signed-off-by: Ravi Anand <ravi.anand@qlogic.com>
Reviewed-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
Diffstat (limited to 'drivers/scsi/qla4xxx')
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_def.h | 2 | ||||
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_fw.h | 1 | ||||
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_glbl.h | 3 | ||||
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_isr.c | 6 | ||||
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_mbx.c | 53 | ||||
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_os.c | 68 |
6 files changed, 124 insertions, 9 deletions
diff --git a/drivers/scsi/qla4xxx/ql4_def.h b/drivers/scsi/qla4xxx/ql4_def.h index b6f2d0152454..428802616e33 100644 --- a/drivers/scsi/qla4xxx/ql4_def.h +++ b/drivers/scsi/qla4xxx/ql4_def.h | |||
@@ -172,7 +172,7 @@ struct srb { | |||
172 | 172 | ||
173 | struct scsi_cmnd *cmd; /* (4) SCSI command block */ | 173 | struct scsi_cmnd *cmd; /* (4) SCSI command block */ |
174 | dma_addr_t dma_handle; /* (4) for unmap of single transfers */ | 174 | dma_addr_t dma_handle; /* (4) for unmap of single transfers */ |
175 | atomic_t ref_count; /* reference count for this srb */ | 175 | struct kref srb_ref; /* reference count for this srb */ |
176 | uint32_t fw_ddb_index; | 176 | uint32_t fw_ddb_index; |
177 | uint8_t err_id; /* error id */ | 177 | uint8_t err_id; /* error id */ |
178 | #define SRB_ERR_PORT 1 /* Request failed because "port down" */ | 178 | #define SRB_ERR_PORT 1 /* Request failed because "port down" */ |
diff --git a/drivers/scsi/qla4xxx/ql4_fw.h b/drivers/scsi/qla4xxx/ql4_fw.h index dfe7b4dd3912..855226e08665 100644 --- a/drivers/scsi/qla4xxx/ql4_fw.h +++ b/drivers/scsi/qla4xxx/ql4_fw.h | |||
@@ -215,6 +215,7 @@ union external_hw_config_reg { | |||
215 | /* Mailbox command definitions */ | 215 | /* Mailbox command definitions */ |
216 | #define MBOX_CMD_ABOUT_FW 0x0009 | 216 | #define MBOX_CMD_ABOUT_FW 0x0009 |
217 | #define MBOX_CMD_PING 0x000B | 217 | #define MBOX_CMD_PING 0x000B |
218 | #define MBOX_CMD_ABORT_TASK 0x0015 | ||
218 | #define MBOX_CMD_LUN_RESET 0x0016 | 219 | #define MBOX_CMD_LUN_RESET 0x0016 |
219 | #define MBOX_CMD_TARGET_WARM_RESET 0x0017 | 220 | #define MBOX_CMD_TARGET_WARM_RESET 0x0017 |
220 | #define MBOX_CMD_GET_MANAGEMENT_DATA 0x001E | 221 | #define MBOX_CMD_GET_MANAGEMENT_DATA 0x001E |
diff --git a/drivers/scsi/qla4xxx/ql4_glbl.h b/drivers/scsi/qla4xxx/ql4_glbl.h index 987658f5bc13..c4636f6cb3cb 100644 --- a/drivers/scsi/qla4xxx/ql4_glbl.h +++ b/drivers/scsi/qla4xxx/ql4_glbl.h | |||
@@ -25,6 +25,7 @@ void qla4xxx_process_aen(struct scsi_qla_host * ha, uint8_t process_aen); | |||
25 | int qla4xxx_get_dhcp_ip_address(struct scsi_qla_host * ha); | 25 | int qla4xxx_get_dhcp_ip_address(struct scsi_qla_host * ha); |
26 | int qla4xxx_relogin_device(struct scsi_qla_host * ha, | 26 | int qla4xxx_relogin_device(struct scsi_qla_host * ha, |
27 | struct ddb_entry * ddb_entry); | 27 | struct ddb_entry * ddb_entry); |
28 | int qla4xxx_abort_task(struct scsi_qla_host *ha, struct srb *srb); | ||
28 | int qla4xxx_reset_lun(struct scsi_qla_host * ha, struct ddb_entry * ddb_entry, | 29 | int qla4xxx_reset_lun(struct scsi_qla_host * ha, struct ddb_entry * ddb_entry, |
29 | int lun); | 30 | int lun); |
30 | int qla4xxx_reset_target(struct scsi_qla_host * ha, | 31 | int qla4xxx_reset_target(struct scsi_qla_host * ha, |
@@ -65,7 +66,7 @@ void qla4xxx_interrupt_service_routine(struct scsi_qla_host * ha, | |||
65 | int qla4xxx_init_rings(struct scsi_qla_host * ha); | 66 | int qla4xxx_init_rings(struct scsi_qla_host * ha); |
66 | struct srb * qla4xxx_del_from_active_array(struct scsi_qla_host *ha, | 67 | struct srb * qla4xxx_del_from_active_array(struct scsi_qla_host *ha, |
67 | uint32_t index); | 68 | uint32_t index); |
68 | void qla4xxx_srb_compl(struct scsi_qla_host *ha, struct srb *srb); | 69 | void qla4xxx_srb_compl(struct kref *ref); |
69 | int qla4xxx_reinitialize_ddb_list(struct scsi_qla_host * ha); | 70 | int qla4xxx_reinitialize_ddb_list(struct scsi_qla_host * ha); |
70 | int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, uint32_t fw_ddb_index, | 71 | int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, uint32_t fw_ddb_index, |
71 | uint32_t state, uint32_t conn_error); | 72 | uint32_t state, uint32_t conn_error); |
diff --git a/drivers/scsi/qla4xxx/ql4_isr.c b/drivers/scsi/qla4xxx/ql4_isr.c index ce5838eb685e..596c3031483c 100644 --- a/drivers/scsi/qla4xxx/ql4_isr.c +++ b/drivers/scsi/qla4xxx/ql4_isr.c | |||
@@ -97,7 +97,7 @@ qla4xxx_status_cont_entry(struct scsi_qla_host *ha, | |||
97 | 97 | ||
98 | /* Place command on done queue. */ | 98 | /* Place command on done queue. */ |
99 | if (srb->req_sense_len == 0) { | 99 | if (srb->req_sense_len == 0) { |
100 | qla4xxx_srb_compl(ha, srb); | 100 | kref_put(&srb->srb_ref, qla4xxx_srb_compl); |
101 | ha->status_srb = NULL; | 101 | ha->status_srb = NULL; |
102 | } | 102 | } |
103 | } | 103 | } |
@@ -329,7 +329,7 @@ status_entry_exit: | |||
329 | /* complete the request, if not waiting for status_continuation pkt */ | 329 | /* complete the request, if not waiting for status_continuation pkt */ |
330 | srb->cc_stat = sts_entry->completionStatus; | 330 | srb->cc_stat = sts_entry->completionStatus; |
331 | if (ha->status_srb == NULL) | 331 | if (ha->status_srb == NULL) |
332 | qla4xxx_srb_compl(ha, srb); | 332 | kref_put(&srb->srb_ref, qla4xxx_srb_compl); |
333 | } | 333 | } |
334 | 334 | ||
335 | /** | 335 | /** |
@@ -393,7 +393,7 @@ static void qla4xxx_process_response_queue(struct scsi_qla_host * ha) | |||
393 | /* ETRY normally by sending it back with | 393 | /* ETRY normally by sending it back with |
394 | * DID_BUS_BUSY */ | 394 | * DID_BUS_BUSY */ |
395 | srb->cmd->result = DID_BUS_BUSY << 16; | 395 | srb->cmd->result = DID_BUS_BUSY << 16; |
396 | qla4xxx_srb_compl(ha, srb); | 396 | kref_put(&srb->srb_ref, qla4xxx_srb_compl); |
397 | break; | 397 | break; |
398 | 398 | ||
399 | case ET_CONTINUE: | 399 | case ET_CONTINUE: |
diff --git a/drivers/scsi/qla4xxx/ql4_mbx.c b/drivers/scsi/qla4xxx/ql4_mbx.c index 43581ce3a1b6..e1315cd8c261 100644 --- a/drivers/scsi/qla4xxx/ql4_mbx.c +++ b/drivers/scsi/qla4xxx/ql4_mbx.c | |||
@@ -762,6 +762,59 @@ exit_get_event_log: | |||
762 | } | 762 | } |
763 | 763 | ||
764 | /** | 764 | /** |
765 | * qla4xxx_abort_task - issues Abort Task | ||
766 | * @ha: Pointer to host adapter structure. | ||
767 | * @srb: Pointer to srb entry | ||
768 | * | ||
769 | * This routine performs a LUN RESET on the specified target/lun. | ||
770 | * The caller must ensure that the ddb_entry and lun_entry pointers | ||
771 | * are valid before calling this routine. | ||
772 | **/ | ||
773 | int qla4xxx_abort_task(struct scsi_qla_host *ha, struct srb *srb) | ||
774 | { | ||
775 | uint32_t mbox_cmd[MBOX_REG_COUNT]; | ||
776 | uint32_t mbox_sts[MBOX_REG_COUNT]; | ||
777 | struct scsi_cmnd *cmd = srb->cmd; | ||
778 | int status = QLA_SUCCESS; | ||
779 | unsigned long flags = 0; | ||
780 | uint32_t index; | ||
781 | |||
782 | /* | ||
783 | * Send abort task command to ISP, so that the ISP will return | ||
784 | * request with ABORT status | ||
785 | */ | ||
786 | memset(&mbox_cmd, 0, sizeof(mbox_cmd)); | ||
787 | memset(&mbox_sts, 0, sizeof(mbox_sts)); | ||
788 | |||
789 | spin_lock_irqsave(&ha->hardware_lock, flags); | ||
790 | index = (unsigned long)(unsigned char *)cmd->host_scribble; | ||
791 | spin_unlock_irqrestore(&ha->hardware_lock, flags); | ||
792 | |||
793 | /* Firmware already posted completion on response queue */ | ||
794 | if (index == MAX_SRBS) | ||
795 | return status; | ||
796 | |||
797 | mbox_cmd[0] = MBOX_CMD_ABORT_TASK; | ||
798 | mbox_cmd[1] = srb->fw_ddb_index; | ||
799 | mbox_cmd[2] = index; | ||
800 | /* Immediate Command Enable */ | ||
801 | mbox_cmd[5] = 0x01; | ||
802 | |||
803 | qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 5, &mbox_cmd[0], | ||
804 | &mbox_sts[0]); | ||
805 | if (mbox_sts[0] != MBOX_STS_COMMAND_COMPLETE) { | ||
806 | status = QLA_ERROR; | ||
807 | |||
808 | DEBUG2(printk(KERN_WARNING "scsi%ld:%d:%d: abort task FAILED: " | ||
809 | "mbx0=%04X, mb1=%04X, mb2=%04X, mb3=%04X, mb4=%04X\n", | ||
810 | ha->host_no, cmd->device->id, cmd->device->lun, mbox_sts[0], | ||
811 | mbox_sts[1], mbox_sts[2], mbox_sts[3], mbox_sts[4])); | ||
812 | } | ||
813 | |||
814 | return status; | ||
815 | } | ||
816 | |||
817 | /** | ||
765 | * qla4xxx_reset_lun - issues LUN Reset | 818 | * qla4xxx_reset_lun - issues LUN Reset |
766 | * @ha: Pointer to host adapter structure. | 819 | * @ha: Pointer to host adapter structure. |
767 | * @db_entry: Pointer to device database entry | 820 | * @db_entry: Pointer to device database entry |
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index 2ca43f0ebcd6..38b1d38afca5 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c | |||
@@ -74,6 +74,7 @@ static enum blk_eh_timer_return qla4xxx_eh_cmd_timed_out(struct scsi_cmnd *sc); | |||
74 | */ | 74 | */ |
75 | static int qla4xxx_queuecommand(struct scsi_cmnd *cmd, | 75 | static int qla4xxx_queuecommand(struct scsi_cmnd *cmd, |
76 | void (*done) (struct scsi_cmnd *)); | 76 | void (*done) (struct scsi_cmnd *)); |
77 | static int qla4xxx_eh_abort(struct scsi_cmnd *cmd); | ||
77 | static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd); | 78 | static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd); |
78 | static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd); | 79 | static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd); |
79 | static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd); | 80 | static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd); |
@@ -88,6 +89,7 @@ static struct scsi_host_template qla4xxx_driver_template = { | |||
88 | .proc_name = DRIVER_NAME, | 89 | .proc_name = DRIVER_NAME, |
89 | .queuecommand = qla4xxx_queuecommand, | 90 | .queuecommand = qla4xxx_queuecommand, |
90 | 91 | ||
92 | .eh_abort_handler = qla4xxx_eh_abort, | ||
91 | .eh_device_reset_handler = qla4xxx_eh_device_reset, | 93 | .eh_device_reset_handler = qla4xxx_eh_device_reset, |
92 | .eh_target_reset_handler = qla4xxx_eh_target_reset, | 94 | .eh_target_reset_handler = qla4xxx_eh_target_reset, |
93 | .eh_host_reset_handler = qla4xxx_eh_host_reset, | 95 | .eh_host_reset_handler = qla4xxx_eh_host_reset, |
@@ -384,7 +386,7 @@ static struct srb* qla4xxx_get_new_srb(struct scsi_qla_host *ha, | |||
384 | if (!srb) | 386 | if (!srb) |
385 | return srb; | 387 | return srb; |
386 | 388 | ||
387 | atomic_set(&srb->ref_count, 1); | 389 | kref_init(&srb->srb_ref); |
388 | srb->ha = ha; | 390 | srb->ha = ha; |
389 | srb->ddb = ddb_entry; | 391 | srb->ddb = ddb_entry; |
390 | srb->cmd = cmd; | 392 | srb->cmd = cmd; |
@@ -406,9 +408,11 @@ static void qla4xxx_srb_free_dma(struct scsi_qla_host *ha, struct srb *srb) | |||
406 | CMD_SP(cmd) = NULL; | 408 | CMD_SP(cmd) = NULL; |
407 | } | 409 | } |
408 | 410 | ||
409 | void qla4xxx_srb_compl(struct scsi_qla_host *ha, struct srb *srb) | 411 | void qla4xxx_srb_compl(struct kref *ref) |
410 | { | 412 | { |
413 | struct srb *srb = container_of(ref, struct srb, srb_ref); | ||
411 | struct scsi_cmnd *cmd = srb->cmd; | 414 | struct scsi_cmnd *cmd = srb->cmd; |
415 | struct scsi_qla_host *ha = srb->ha; | ||
412 | 416 | ||
413 | qla4xxx_srb_free_dma(ha, srb); | 417 | qla4xxx_srb_free_dma(ha, srb); |
414 | 418 | ||
@@ -887,7 +891,7 @@ static void qla4xxx_flush_active_srbs(struct scsi_qla_host *ha) | |||
887 | srb = qla4xxx_del_from_active_array(ha, i); | 891 | srb = qla4xxx_del_from_active_array(ha, i); |
888 | if (srb != NULL) { | 892 | if (srb != NULL) { |
889 | srb->cmd->result = DID_RESET << 16; | 893 | srb->cmd->result = DID_RESET << 16; |
890 | qla4xxx_srb_compl(ha, srb); | 894 | kref_put(&srb->srb_ref, qla4xxx_srb_compl); |
891 | } | 895 | } |
892 | } | 896 | } |
893 | spin_unlock_irqrestore(&ha->hardware_lock, flags); | 897 | spin_unlock_irqrestore(&ha->hardware_lock, flags); |
@@ -1501,7 +1505,7 @@ struct srb * qla4xxx_del_from_active_array(struct scsi_qla_host *ha, uint32_t in | |||
1501 | 1505 | ||
1502 | /** | 1506 | /** |
1503 | * qla4xxx_eh_wait_on_command - waits for command to be returned by firmware | 1507 | * qla4xxx_eh_wait_on_command - waits for command to be returned by firmware |
1504 | * @ha: actual ha whose done queue will contain the comd returned by firmware. | 1508 | * @ha: Pointer to host adapter structure. |
1505 | * @cmd: Scsi Command to wait on. | 1509 | * @cmd: Scsi Command to wait on. |
1506 | * | 1510 | * |
1507 | * This routine waits for the command to be returned by the Firmware | 1511 | * This routine waits for the command to be returned by the Firmware |
@@ -1585,6 +1589,62 @@ static int qla4xxx_eh_wait_for_commands(struct scsi_qla_host *ha, | |||
1585 | } | 1589 | } |
1586 | 1590 | ||
1587 | /** | 1591 | /** |
1592 | * qla4xxx_eh_abort - callback for abort task. | ||
1593 | * @cmd: Pointer to Linux's SCSI command structure | ||
1594 | * | ||
1595 | * This routine is called by the Linux OS to abort the specified | ||
1596 | * command. | ||
1597 | **/ | ||
1598 | static int qla4xxx_eh_abort(struct scsi_cmnd *cmd) | ||
1599 | { | ||
1600 | struct scsi_qla_host *ha = to_qla_host(cmd->device->host); | ||
1601 | unsigned int id = cmd->device->id; | ||
1602 | unsigned int lun = cmd->device->lun; | ||
1603 | unsigned long serial = cmd->serial_number; | ||
1604 | struct srb *srb = NULL; | ||
1605 | int ret = SUCCESS; | ||
1606 | int wait = 0; | ||
1607 | |||
1608 | dev_info(&ha->pdev->dev, | ||
1609 | "scsi%ld:%d:%d: Abort command issued cmd=%p, pid=%ld\n", | ||
1610 | ha->host_no, id, lun, cmd, serial); | ||
1611 | |||
1612 | srb = (struct srb *) CMD_SP(cmd); | ||
1613 | |||
1614 | if (!srb) | ||
1615 | return SUCCESS; | ||
1616 | |||
1617 | kref_get(&srb->srb_ref); | ||
1618 | |||
1619 | if (qla4xxx_abort_task(ha, srb) != QLA_SUCCESS) { | ||
1620 | DEBUG3(printk("scsi%ld:%d:%d: Abort_task mbx failed.\n", | ||
1621 | ha->host_no, id, lun)); | ||
1622 | ret = FAILED; | ||
1623 | } else { | ||
1624 | DEBUG3(printk("scsi%ld:%d:%d: Abort_task mbx success.\n", | ||
1625 | ha->host_no, id, lun)); | ||
1626 | wait = 1; | ||
1627 | } | ||
1628 | |||
1629 | kref_put(&srb->srb_ref, qla4xxx_srb_compl); | ||
1630 | |||
1631 | /* Wait for command to complete */ | ||
1632 | if (wait) { | ||
1633 | if (!qla4xxx_eh_wait_on_command(ha, cmd)) { | ||
1634 | DEBUG2(printk("scsi%ld:%d:%d: Abort handler timed out\n", | ||
1635 | ha->host_no, id, lun)); | ||
1636 | ret = FAILED; | ||
1637 | } | ||
1638 | } | ||
1639 | |||
1640 | dev_info(&ha->pdev->dev, | ||
1641 | "scsi%ld:%d:%d: Abort command - %s\n", | ||
1642 | ha->host_no, id, lun, (ret == SUCCESS) ? "succeded" : "failed"); | ||
1643 | |||
1644 | return ret; | ||
1645 | } | ||
1646 | |||
1647 | /** | ||
1588 | * qla4xxx_eh_device_reset - callback for target reset. | 1648 | * qla4xxx_eh_device_reset - callback for target reset. |
1589 | * @cmd: Pointer to Linux's SCSI command structure | 1649 | * @cmd: Pointer to Linux's SCSI command structure |
1590 | * | 1650 | * |