diff options
author | Andrew Vasquez <andrew.vasquez@qlogic.com> | 2008-04-03 16:13:24 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-04-07 13:19:15 -0400 |
commit | 523ec773b8ffb1c607bc3a54c9526558e3b1eab1 (patch) | |
tree | 9cb3fc8a68af97a6359704e4341652aad9cc65d1 /drivers/scsi/qla2xxx/qla_os.c | |
parent | 3fe7cfb910ea138ae623d1320c71e2a7a0bdc527 (diff) |
[SCSI] qla2xxx: Add midlayer target/device reset support.
Now that infrastructure is present within the midlayer and there
is a clear distinction between what is expected from a device and
target reset, convert the current device-reset codes to a
target-reset, and add codes to perform a proper device-reset (LUN
reset).
In the process of adding reset support, collapse and consolidate
large sections of mailbox-command (TMF issuance) codes,
generalize the two 'wait-for-commands-to-complete' functions, and
add a generic-reset routine for use by midlayer reset functions.
Signed-off-by: Andrew Vasquez <andrew.vasquez@qlogic.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers/scsi/qla2xxx/qla_os.c')
-rw-r--r-- | drivers/scsi/qla2xxx/qla_os.c | 254 |
1 files changed, 101 insertions, 153 deletions
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index ba7d2ca3a0e8..5cddc503bd2b 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c | |||
@@ -100,6 +100,7 @@ static int qla24xx_queuecommand(struct scsi_cmnd *cmd, | |||
100 | void (*fn)(struct scsi_cmnd *)); | 100 | void (*fn)(struct scsi_cmnd *)); |
101 | static int qla2xxx_eh_abort(struct scsi_cmnd *); | 101 | static int qla2xxx_eh_abort(struct scsi_cmnd *); |
102 | static int qla2xxx_eh_device_reset(struct scsi_cmnd *); | 102 | static int qla2xxx_eh_device_reset(struct scsi_cmnd *); |
103 | static int qla2xxx_eh_target_reset(struct scsi_cmnd *); | ||
103 | static int qla2xxx_eh_bus_reset(struct scsi_cmnd *); | 104 | static int qla2xxx_eh_bus_reset(struct scsi_cmnd *); |
104 | static int qla2xxx_eh_host_reset(struct scsi_cmnd *); | 105 | static int qla2xxx_eh_host_reset(struct scsi_cmnd *); |
105 | 106 | ||
@@ -113,6 +114,7 @@ static struct scsi_host_template qla2x00_driver_template = { | |||
113 | 114 | ||
114 | .eh_abort_handler = qla2xxx_eh_abort, | 115 | .eh_abort_handler = qla2xxx_eh_abort, |
115 | .eh_device_reset_handler = qla2xxx_eh_device_reset, | 116 | .eh_device_reset_handler = qla2xxx_eh_device_reset, |
117 | .eh_target_reset_handler = qla2xxx_eh_target_reset, | ||
116 | .eh_bus_reset_handler = qla2xxx_eh_bus_reset, | 118 | .eh_bus_reset_handler = qla2xxx_eh_bus_reset, |
117 | .eh_host_reset_handler = qla2xxx_eh_host_reset, | 119 | .eh_host_reset_handler = qla2xxx_eh_host_reset, |
118 | 120 | ||
@@ -144,6 +146,7 @@ struct scsi_host_template qla24xx_driver_template = { | |||
144 | 146 | ||
145 | .eh_abort_handler = qla2xxx_eh_abort, | 147 | .eh_abort_handler = qla2xxx_eh_abort, |
146 | .eh_device_reset_handler = qla2xxx_eh_device_reset, | 148 | .eh_device_reset_handler = qla2xxx_eh_device_reset, |
149 | .eh_target_reset_handler = qla2xxx_eh_target_reset, | ||
147 | .eh_bus_reset_handler = qla2xxx_eh_bus_reset, | 150 | .eh_bus_reset_handler = qla2xxx_eh_bus_reset, |
148 | .eh_host_reset_handler = qla2xxx_eh_host_reset, | 151 | .eh_host_reset_handler = qla2xxx_eh_host_reset, |
149 | 152 | ||
@@ -566,8 +569,6 @@ qla2x00_wait_for_hba_online(scsi_qla_host_t *ha) | |||
566 | else | 569 | else |
567 | return_status = QLA_FUNCTION_FAILED; | 570 | return_status = QLA_FUNCTION_FAILED; |
568 | 571 | ||
569 | DEBUG2(printk("%s return_status=%d\n",__func__,return_status)); | ||
570 | |||
571 | return (return_status); | 572 | return (return_status); |
572 | } | 573 | } |
573 | 574 | ||
@@ -714,181 +715,122 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) | |||
714 | return ret; | 715 | return ret; |
715 | } | 716 | } |
716 | 717 | ||
717 | /************************************************************************** | 718 | enum nexus_wait_type { |
718 | * qla2x00_eh_wait_for_pending_target_commands | 719 | WAIT_HOST = 0, |
719 | * | 720 | WAIT_TARGET, |
720 | * Description: | 721 | WAIT_LUN, |
721 | * Waits for all the commands to come back from the specified target. | 722 | }; |
722 | * | 723 | |
723 | * Input: | ||
724 | * ha - pointer to scsi_qla_host structure. | ||
725 | * t - target | ||
726 | * Returns: | ||
727 | * Either SUCCESS or FAILED. | ||
728 | * | ||
729 | * Note: | ||
730 | **************************************************************************/ | ||
731 | static int | 724 | static int |
732 | qla2x00_eh_wait_for_pending_target_commands(scsi_qla_host_t *ha, unsigned int t) | 725 | qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *ha, unsigned int t, |
726 | unsigned int l, enum nexus_wait_type type) | ||
733 | { | 727 | { |
734 | int cnt; | 728 | int cnt, match, status; |
735 | int status; | 729 | srb_t *sp; |
736 | srb_t *sp; | ||
737 | struct scsi_cmnd *cmd; | ||
738 | unsigned long flags; | 730 | unsigned long flags; |
739 | scsi_qla_host_t *pha = to_qla_parent(ha); | 731 | scsi_qla_host_t *pha = to_qla_parent(ha); |
740 | 732 | ||
741 | status = 0; | 733 | status = QLA_SUCCESS; |
742 | 734 | spin_lock_irqsave(&pha->hardware_lock, flags); | |
743 | /* | 735 | for (cnt = 1; status == QLA_SUCCESS && cnt < MAX_OUTSTANDING_COMMANDS; |
744 | * Waiting for all commands for the designated target in the active | 736 | cnt++) { |
745 | * array | ||
746 | */ | ||
747 | for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) { | ||
748 | spin_lock_irqsave(&pha->hardware_lock, flags); | ||
749 | sp = pha->outstanding_cmds[cnt]; | 737 | sp = pha->outstanding_cmds[cnt]; |
750 | if (sp) { | 738 | if (!sp) |
751 | cmd = sp->cmd; | 739 | continue; |
752 | spin_unlock_irqrestore(&pha->hardware_lock, flags); | 740 | if (ha->vp_idx != sp->ha->vp_idx) |
753 | if (cmd->device->id == t && | 741 | continue; |
754 | ha->vp_idx == sp->ha->vp_idx) { | 742 | match = 0; |
755 | if (!qla2x00_eh_wait_on_command(ha, cmd)) { | 743 | switch (type) { |
756 | status = 1; | 744 | case WAIT_HOST: |
757 | break; | 745 | match = 1; |
758 | } | 746 | break; |
759 | } | 747 | case WAIT_TARGET: |
760 | } else { | 748 | match = sp->cmd->device->id == t; |
761 | spin_unlock_irqrestore(&pha->hardware_lock, flags); | 749 | break; |
750 | case WAIT_LUN: | ||
751 | match = (sp->cmd->device->id == t && | ||
752 | sp->cmd->device->lun == l); | ||
753 | break; | ||
762 | } | 754 | } |
755 | if (!match) | ||
756 | continue; | ||
757 | |||
758 | spin_unlock_irqrestore(&pha->hardware_lock, flags); | ||
759 | status = qla2x00_eh_wait_on_command(ha, sp->cmd); | ||
760 | spin_lock_irqsave(&pha->hardware_lock, flags); | ||
763 | } | 761 | } |
764 | return (status); | 762 | spin_unlock_irqrestore(&pha->hardware_lock, flags); |
763 | |||
764 | return status; | ||
765 | } | 765 | } |
766 | 766 | ||
767 | static char *reset_errors[] = { | ||
768 | "HBA not online", | ||
769 | "HBA not ready", | ||
770 | "Task management failed", | ||
771 | "Waiting for command completions", | ||
772 | }; | ||
767 | 773 | ||
768 | /************************************************************************** | ||
769 | * qla2xxx_eh_device_reset | ||
770 | * | ||
771 | * Description: | ||
772 | * The device reset function will reset the target and abort any | ||
773 | * executing commands. | ||
774 | * | ||
775 | * NOTE: The use of SP is undefined within this context. Do *NOT* | ||
776 | * attempt to use this value, even if you determine it is | ||
777 | * non-null. | ||
778 | * | ||
779 | * Input: | ||
780 | * cmd = Linux SCSI command packet of the command that cause the | ||
781 | * bus device reset. | ||
782 | * | ||
783 | * Returns: | ||
784 | * SUCCESS/FAILURE (defined as macro in scsi.h). | ||
785 | * | ||
786 | **************************************************************************/ | ||
787 | static int | 774 | static int |
788 | qla2xxx_eh_device_reset(struct scsi_cmnd *cmd) | 775 | __qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type, |
776 | struct scsi_cmnd *cmd, int (*do_reset)(struct fc_port *, unsigned int)) | ||
789 | { | 777 | { |
790 | scsi_qla_host_t *ha = shost_priv(cmd->device->host); | 778 | scsi_qla_host_t *ha = shost_priv(cmd->device->host); |
791 | fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata; | 779 | fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata; |
792 | int ret = FAILED; | 780 | int err; |
793 | unsigned int id, lun; | ||
794 | unsigned long serial; | ||
795 | 781 | ||
796 | qla2x00_block_error_handler(cmd); | 782 | qla2x00_block_error_handler(cmd); |
797 | 783 | ||
798 | id = cmd->device->id; | ||
799 | lun = cmd->device->lun; | ||
800 | serial = cmd->serial_number; | ||
801 | |||
802 | if (!fcport) | 784 | if (!fcport) |
803 | return ret; | 785 | return FAILED; |
804 | 786 | ||
805 | qla_printk(KERN_INFO, ha, | 787 | qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d): %s RESET ISSUED.\n", |
806 | "scsi(%ld:%d:%d): DEVICE RESET ISSUED.\n", ha->host_no, id, lun); | 788 | ha->host_no, cmd->device->id, cmd->device->lun, name); |
807 | 789 | ||
790 | err = 0; | ||
808 | if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS) | 791 | if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS) |
809 | goto eh_dev_reset_done; | 792 | goto eh_reset_failed; |
810 | 793 | err = 1; | |
811 | if (qla2x00_wait_for_loop_ready(ha) == QLA_SUCCESS) { | 794 | if (qla2x00_wait_for_loop_ready(ha) != QLA_SUCCESS) |
812 | if (ha->isp_ops->abort_target(fcport) == 0) | 795 | goto eh_reset_failed; |
813 | ret = SUCCESS; | 796 | err = 2; |
814 | } else { | 797 | if (do_reset(fcport, cmd->device->lun) != QLA_SUCCESS) |
815 | DEBUG2(printk(KERN_INFO | 798 | goto eh_reset_failed; |
816 | "%s failed: loop not ready\n",__func__)); | 799 | err = 3; |
817 | } | 800 | if (qla2x00_eh_wait_for_pending_commands(ha, cmd->device->id, |
818 | 801 | cmd->device->lun, type) != QLA_SUCCESS) | |
819 | if (ret == FAILED) { | 802 | goto eh_reset_failed; |
820 | DEBUG3(printk("%s(%ld): device reset failed\n", | 803 | |
821 | __func__, ha->host_no)); | 804 | qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d): %s RESET SUCCEEDED.\n", |
822 | qla_printk(KERN_INFO, ha, "%s: device reset failed\n", | 805 | ha->host_no, cmd->device->id, cmd->device->lun, name); |
823 | __func__); | 806 | |
807 | return SUCCESS; | ||
808 | |||
809 | eh_reset_failed: | ||
810 | qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d): %s RESET FAILED: %s.\n", | ||
811 | ha->host_no, cmd->device->id, cmd->device->lun, name, | ||
812 | reset_errors[err]); | ||
813 | return FAILED; | ||
814 | } | ||
824 | 815 | ||
825 | goto eh_dev_reset_done; | 816 | static int |
826 | } | 817 | qla2xxx_eh_device_reset(struct scsi_cmnd *cmd) |
818 | { | ||
819 | scsi_qla_host_t *ha = shost_priv(cmd->device->host); | ||
827 | 820 | ||
828 | /* Flush outstanding commands. */ | 821 | return __qla2xxx_eh_generic_reset("DEVICE", WAIT_LUN, cmd, |
829 | if (qla2x00_eh_wait_for_pending_target_commands(ha, id)) | 822 | ha->isp_ops->lun_reset); |
830 | ret = FAILED; | ||
831 | if (ret == FAILED) { | ||
832 | DEBUG3(printk("%s(%ld): failed while waiting for commands\n", | ||
833 | __func__, ha->host_no)); | ||
834 | qla_printk(KERN_INFO, ha, | ||
835 | "%s: failed while waiting for commands\n", __func__); | ||
836 | } else | ||
837 | qla_printk(KERN_INFO, ha, | ||
838 | "scsi(%ld:%d:%d): DEVICE RESET SUCCEEDED.\n", ha->host_no, | ||
839 | id, lun); | ||
840 | eh_dev_reset_done: | ||
841 | return ret; | ||
842 | } | 823 | } |
843 | 824 | ||
844 | /************************************************************************** | ||
845 | * qla2x00_eh_wait_for_pending_commands | ||
846 | * | ||
847 | * Description: | ||
848 | * Waits for all the commands to come back from the specified host. | ||
849 | * | ||
850 | * Input: | ||
851 | * ha - pointer to scsi_qla_host structure. | ||
852 | * | ||
853 | * Returns: | ||
854 | * 1 : SUCCESS | ||
855 | * 0 : FAILED | ||
856 | * | ||
857 | * Note: | ||
858 | **************************************************************************/ | ||
859 | static int | 825 | static int |
860 | qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *ha) | 826 | qla2xxx_eh_target_reset(struct scsi_cmnd *cmd) |
861 | { | 827 | { |
862 | int cnt; | 828 | scsi_qla_host_t *ha = shost_priv(cmd->device->host); |
863 | int status; | ||
864 | srb_t *sp; | ||
865 | struct scsi_cmnd *cmd; | ||
866 | unsigned long flags; | ||
867 | |||
868 | status = 1; | ||
869 | 829 | ||
870 | /* | 830 | return __qla2xxx_eh_generic_reset("TARGET", WAIT_TARGET, cmd, |
871 | * Waiting for all commands for the designated target in the active | 831 | ha->isp_ops->target_reset); |
872 | * array | ||
873 | */ | ||
874 | for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) { | ||
875 | spin_lock_irqsave(&ha->hardware_lock, flags); | ||
876 | sp = ha->outstanding_cmds[cnt]; | ||
877 | if (sp) { | ||
878 | cmd = sp->cmd; | ||
879 | spin_unlock_irqrestore(&ha->hardware_lock, flags); | ||
880 | status = qla2x00_eh_wait_on_command(ha, cmd); | ||
881 | if (status == 0) | ||
882 | break; | ||
883 | } | ||
884 | else { | ||
885 | spin_unlock_irqrestore(&ha->hardware_lock, flags); | ||
886 | } | ||
887 | } | ||
888 | return (status); | ||
889 | } | 832 | } |
890 | 833 | ||
891 | |||
892 | /************************************************************************** | 834 | /************************************************************************** |
893 | * qla2xxx_eh_bus_reset | 835 | * qla2xxx_eh_bus_reset |
894 | * | 836 | * |
@@ -939,7 +881,8 @@ qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd) | |||
939 | goto eh_bus_reset_done; | 881 | goto eh_bus_reset_done; |
940 | 882 | ||
941 | /* Flush outstanding commands. */ | 883 | /* Flush outstanding commands. */ |
942 | if (!qla2x00_eh_wait_for_pending_commands(pha)) | 884 | if (qla2x00_eh_wait_for_pending_commands(pha, 0, 0, WAIT_HOST) != |
885 | QLA_SUCCESS) | ||
943 | ret = FAILED; | 886 | ret = FAILED; |
944 | 887 | ||
945 | eh_bus_reset_done: | 888 | eh_bus_reset_done: |
@@ -1010,7 +953,8 @@ qla2xxx_eh_host_reset(struct scsi_cmnd *cmd) | |||
1010 | clear_bit(ABORT_ISP_ACTIVE, &pha->dpc_flags); | 953 | clear_bit(ABORT_ISP_ACTIVE, &pha->dpc_flags); |
1011 | 954 | ||
1012 | /* Waiting for our command in done_queue to be returned to OS.*/ | 955 | /* Waiting for our command in done_queue to be returned to OS.*/ |
1013 | if (qla2x00_eh_wait_for_pending_commands(pha)) | 956 | if (qla2x00_eh_wait_for_pending_commands(pha, 0, 0, WAIT_HOST) == |
957 | QLA_SUCCESS) | ||
1014 | ret = SUCCESS; | 958 | ret = SUCCESS; |
1015 | 959 | ||
1016 | if (ha->parent) | 960 | if (ha->parent) |
@@ -1066,7 +1010,7 @@ qla2x00_loop_reset(scsi_qla_host_t *ha) | |||
1066 | if (fcport->port_type != FCT_TARGET) | 1010 | if (fcport->port_type != FCT_TARGET) |
1067 | continue; | 1011 | continue; |
1068 | 1012 | ||
1069 | ret = ha->isp_ops->abort_target(fcport); | 1013 | ret = ha->isp_ops->target_reset(fcport, 0); |
1070 | if (ret != QLA_SUCCESS) { | 1014 | if (ret != QLA_SUCCESS) { |
1071 | DEBUG2_3(printk("%s(%ld): bus_reset failed: " | 1015 | DEBUG2_3(printk("%s(%ld): bus_reset failed: " |
1072 | "target_reset=%d d_id=%x.\n", __func__, | 1016 | "target_reset=%d d_id=%x.\n", __func__, |
@@ -1258,7 +1202,8 @@ static struct isp_operations qla2100_isp_ops = { | |||
1258 | .enable_intrs = qla2x00_enable_intrs, | 1202 | .enable_intrs = qla2x00_enable_intrs, |
1259 | .disable_intrs = qla2x00_disable_intrs, | 1203 | .disable_intrs = qla2x00_disable_intrs, |
1260 | .abort_command = qla2x00_abort_command, | 1204 | .abort_command = qla2x00_abort_command, |
1261 | .abort_target = qla2x00_abort_target, | 1205 | .target_reset = qla2x00_abort_target, |
1206 | .lun_reset = qla2x00_lun_reset, | ||
1262 | .fabric_login = qla2x00_login_fabric, | 1207 | .fabric_login = qla2x00_login_fabric, |
1263 | .fabric_logout = qla2x00_fabric_logout, | 1208 | .fabric_logout = qla2x00_fabric_logout, |
1264 | .calc_req_entries = qla2x00_calc_iocbs_32, | 1209 | .calc_req_entries = qla2x00_calc_iocbs_32, |
@@ -1291,7 +1236,8 @@ static struct isp_operations qla2300_isp_ops = { | |||
1291 | .enable_intrs = qla2x00_enable_intrs, | 1236 | .enable_intrs = qla2x00_enable_intrs, |
1292 | .disable_intrs = qla2x00_disable_intrs, | 1237 | .disable_intrs = qla2x00_disable_intrs, |
1293 | .abort_command = qla2x00_abort_command, | 1238 | .abort_command = qla2x00_abort_command, |
1294 | .abort_target = qla2x00_abort_target, | 1239 | .target_reset = qla2x00_abort_target, |
1240 | .lun_reset = qla2x00_lun_reset, | ||
1295 | .fabric_login = qla2x00_login_fabric, | 1241 | .fabric_login = qla2x00_login_fabric, |
1296 | .fabric_logout = qla2x00_fabric_logout, | 1242 | .fabric_logout = qla2x00_fabric_logout, |
1297 | .calc_req_entries = qla2x00_calc_iocbs_32, | 1243 | .calc_req_entries = qla2x00_calc_iocbs_32, |
@@ -1324,7 +1270,8 @@ static struct isp_operations qla24xx_isp_ops = { | |||
1324 | .enable_intrs = qla24xx_enable_intrs, | 1270 | .enable_intrs = qla24xx_enable_intrs, |
1325 | .disable_intrs = qla24xx_disable_intrs, | 1271 | .disable_intrs = qla24xx_disable_intrs, |
1326 | .abort_command = qla24xx_abort_command, | 1272 | .abort_command = qla24xx_abort_command, |
1327 | .abort_target = qla24xx_abort_target, | 1273 | .target_reset = qla24xx_abort_target, |
1274 | .lun_reset = qla24xx_lun_reset, | ||
1328 | .fabric_login = qla24xx_login_fabric, | 1275 | .fabric_login = qla24xx_login_fabric, |
1329 | .fabric_logout = qla24xx_fabric_logout, | 1276 | .fabric_logout = qla24xx_fabric_logout, |
1330 | .calc_req_entries = NULL, | 1277 | .calc_req_entries = NULL, |
@@ -1357,7 +1304,8 @@ static struct isp_operations qla25xx_isp_ops = { | |||
1357 | .enable_intrs = qla24xx_enable_intrs, | 1304 | .enable_intrs = qla24xx_enable_intrs, |
1358 | .disable_intrs = qla24xx_disable_intrs, | 1305 | .disable_intrs = qla24xx_disable_intrs, |
1359 | .abort_command = qla24xx_abort_command, | 1306 | .abort_command = qla24xx_abort_command, |
1360 | .abort_target = qla24xx_abort_target, | 1307 | .target_reset = qla24xx_abort_target, |
1308 | .lun_reset = qla24xx_lun_reset, | ||
1361 | .fabric_login = qla24xx_login_fabric, | 1309 | .fabric_login = qla24xx_login_fabric, |
1362 | .fabric_logout = qla24xx_fabric_logout, | 1310 | .fabric_logout = qla24xx_fabric_logout, |
1363 | .calc_req_entries = NULL, | 1311 | .calc_req_entries = NULL, |