diff options
author | Nicholas Bellinger <nab@linux-iscsi.org> | 2016-05-18 01:19:10 -0400 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2016-07-20 03:58:38 -0400 |
commit | dff0ca9ea7dc8be2181a62df4a722c32ce68ff4a (patch) | |
tree | 5a8fad3a30d78857da00f4be6952c4f16c95f35c | |
parent | ea263c7fada4af8ec7fe5fcfd6e7d7705a89351b (diff) |
target: Fix ordered task target_setup_cmd_from_cdb exception hang
If a command with a Simple task attribute is failed due to a Unit
Attention, then a subsequent command with an Ordered task attribute
will hang forever. The reason for this is that the Unit Attention
status is checked for in target_setup_cmd_from_cdb, before the call
to target_execute_cmd, which calls target_handle_task_attr, which
in turn increments dev->simple_cmds.
However, transport_generic_request_failure still calls
transport_complete_task_attr, which will decrement dev->simple_cmds.
In this case, simple_cmds is now -1. So when a command with the
Ordered task attribute is sent, target_handle_task_attr sees that
dev->simple_cmds is not 0, so it decides it can't execute the
command until all the (nonexistent) Simple commands have completed.
Reported-by: Michael Cyr <mikecyr@linux.vnet.ibm.com>
Tested-by: Michael Cyr <mikecyr@linux.vnet.ibm.com>
Reported-by: Bryant G. Ly <bryantly@linux.vnet.ibm.com>
Tested-by: Bryant G. Ly <bryantly@linux.vnet.ibm.com>
Cc: stable@vger.kernel.org # 4.4+
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
-rw-r--r-- | drivers/target/target_core_internal.h | 1 | ||||
-rw-r--r-- | drivers/target/target_core_sbc.c | 2 | ||||
-rw-r--r-- | drivers/target/target_core_transport.c | 62 | ||||
-rw-r--r-- | include/target/target_core_fabric.h | 1 |
4 files changed, 37 insertions, 29 deletions
diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h index fc91e85f54ba..e2c970a9d61c 100644 --- a/drivers/target/target_core_internal.h +++ b/drivers/target/target_core_internal.h | |||
@@ -146,6 +146,7 @@ sense_reason_t target_cmd_size_check(struct se_cmd *cmd, unsigned int size); | |||
146 | void target_qf_do_work(struct work_struct *work); | 146 | void target_qf_do_work(struct work_struct *work); |
147 | bool target_check_wce(struct se_device *dev); | 147 | bool target_check_wce(struct se_device *dev); |
148 | bool target_check_fua(struct se_device *dev); | 148 | bool target_check_fua(struct se_device *dev); |
149 | void __target_execute_cmd(struct se_cmd *, bool); | ||
149 | 150 | ||
150 | /* target_core_stat.c */ | 151 | /* target_core_stat.c */ |
151 | void target_stat_setup_dev_default_groups(struct se_device *); | 152 | void target_stat_setup_dev_default_groups(struct se_device *); |
diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index a9057aa07176..04f616b3ba0a 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c | |||
@@ -602,7 +602,7 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool succes | |||
602 | cmd->transport_state |= CMD_T_ACTIVE|CMD_T_BUSY|CMD_T_SENT; | 602 | cmd->transport_state |= CMD_T_ACTIVE|CMD_T_BUSY|CMD_T_SENT; |
603 | spin_unlock_irq(&cmd->t_state_lock); | 603 | spin_unlock_irq(&cmd->t_state_lock); |
604 | 604 | ||
605 | __target_execute_cmd(cmd); | 605 | __target_execute_cmd(cmd, false); |
606 | 606 | ||
607 | kfree(buf); | 607 | kfree(buf); |
608 | return ret; | 608 | return ret; |
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 38bf2dcf926a..a6e1edcfefbd 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c | |||
@@ -1303,23 +1303,6 @@ target_setup_cmd_from_cdb(struct se_cmd *cmd, unsigned char *cdb) | |||
1303 | 1303 | ||
1304 | trace_target_sequencer_start(cmd); | 1304 | trace_target_sequencer_start(cmd); |
1305 | 1305 | ||
1306 | /* | ||
1307 | * Check for an existing UNIT ATTENTION condition | ||
1308 | */ | ||
1309 | ret = target_scsi3_ua_check(cmd); | ||
1310 | if (ret) | ||
1311 | return ret; | ||
1312 | |||
1313 | ret = target_alua_state_check(cmd); | ||
1314 | if (ret) | ||
1315 | return ret; | ||
1316 | |||
1317 | ret = target_check_reservation(cmd); | ||
1318 | if (ret) { | ||
1319 | cmd->scsi_status = SAM_STAT_RESERVATION_CONFLICT; | ||
1320 | return ret; | ||
1321 | } | ||
1322 | |||
1323 | ret = dev->transport->parse_cdb(cmd); | 1306 | ret = dev->transport->parse_cdb(cmd); |
1324 | if (ret == TCM_UNSUPPORTED_SCSI_OPCODE) | 1307 | if (ret == TCM_UNSUPPORTED_SCSI_OPCODE) |
1325 | pr_warn_ratelimited("%s/%s: Unsupported SCSI Opcode 0x%02x, sending CHECK_CONDITION.\n", | 1308 | pr_warn_ratelimited("%s/%s: Unsupported SCSI Opcode 0x%02x, sending CHECK_CONDITION.\n", |
@@ -1761,20 +1744,45 @@ queue_full: | |||
1761 | } | 1744 | } |
1762 | EXPORT_SYMBOL(transport_generic_request_failure); | 1745 | EXPORT_SYMBOL(transport_generic_request_failure); |
1763 | 1746 | ||
1764 | void __target_execute_cmd(struct se_cmd *cmd) | 1747 | void __target_execute_cmd(struct se_cmd *cmd, bool do_checks) |
1765 | { | 1748 | { |
1766 | sense_reason_t ret; | 1749 | sense_reason_t ret; |
1767 | 1750 | ||
1768 | if (cmd->execute_cmd) { | 1751 | if (!cmd->execute_cmd) { |
1769 | ret = cmd->execute_cmd(cmd); | 1752 | ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; |
1770 | if (ret) { | 1753 | goto err; |
1771 | spin_lock_irq(&cmd->t_state_lock); | 1754 | } |
1772 | cmd->transport_state &= ~(CMD_T_BUSY|CMD_T_SENT); | 1755 | if (do_checks) { |
1773 | spin_unlock_irq(&cmd->t_state_lock); | 1756 | /* |
1757 | * Check for an existing UNIT ATTENTION condition after | ||
1758 | * target_handle_task_attr() has done SAM task attr | ||
1759 | * checking, and possibly have already defered execution | ||
1760 | * out to target_restart_delayed_cmds() context. | ||
1761 | */ | ||
1762 | ret = target_scsi3_ua_check(cmd); | ||
1763 | if (ret) | ||
1764 | goto err; | ||
1765 | |||
1766 | ret = target_alua_state_check(cmd); | ||
1767 | if (ret) | ||
1768 | goto err; | ||
1774 | 1769 | ||
1775 | transport_generic_request_failure(cmd, ret); | 1770 | ret = target_check_reservation(cmd); |
1771 | if (ret) { | ||
1772 | cmd->scsi_status = SAM_STAT_RESERVATION_CONFLICT; | ||
1773 | goto err; | ||
1776 | } | 1774 | } |
1777 | } | 1775 | } |
1776 | |||
1777 | ret = cmd->execute_cmd(cmd); | ||
1778 | if (!ret) | ||
1779 | return; | ||
1780 | err: | ||
1781 | spin_lock_irq(&cmd->t_state_lock); | ||
1782 | cmd->transport_state &= ~(CMD_T_BUSY|CMD_T_SENT); | ||
1783 | spin_unlock_irq(&cmd->t_state_lock); | ||
1784 | |||
1785 | transport_generic_request_failure(cmd, ret); | ||
1778 | } | 1786 | } |
1779 | 1787 | ||
1780 | static int target_write_prot_action(struct se_cmd *cmd) | 1788 | static int target_write_prot_action(struct se_cmd *cmd) |
@@ -1899,7 +1907,7 @@ void target_execute_cmd(struct se_cmd *cmd) | |||
1899 | return; | 1907 | return; |
1900 | } | 1908 | } |
1901 | 1909 | ||
1902 | __target_execute_cmd(cmd); | 1910 | __target_execute_cmd(cmd, true); |
1903 | } | 1911 | } |
1904 | EXPORT_SYMBOL(target_execute_cmd); | 1912 | EXPORT_SYMBOL(target_execute_cmd); |
1905 | 1913 | ||
@@ -1923,7 +1931,7 @@ static void target_restart_delayed_cmds(struct se_device *dev) | |||
1923 | list_del(&cmd->se_delayed_node); | 1931 | list_del(&cmd->se_delayed_node); |
1924 | spin_unlock(&dev->delayed_cmd_lock); | 1932 | spin_unlock(&dev->delayed_cmd_lock); |
1925 | 1933 | ||
1926 | __target_execute_cmd(cmd); | 1934 | __target_execute_cmd(cmd, true); |
1927 | 1935 | ||
1928 | if (cmd->sam_task_attr == TCM_ORDERED_TAG) | 1936 | if (cmd->sam_task_attr == TCM_ORDERED_TAG) |
1929 | break; | 1937 | break; |
diff --git a/include/target/target_core_fabric.h b/include/target/target_core_fabric.h index de44462a7680..5cd6faa6e0d1 100644 --- a/include/target/target_core_fabric.h +++ b/include/target/target_core_fabric.h | |||
@@ -163,7 +163,6 @@ int core_tmr_alloc_req(struct se_cmd *, void *, u8, gfp_t); | |||
163 | void core_tmr_release_req(struct se_tmr_req *); | 163 | void core_tmr_release_req(struct se_tmr_req *); |
164 | int transport_generic_handle_tmr(struct se_cmd *); | 164 | int transport_generic_handle_tmr(struct se_cmd *); |
165 | void transport_generic_request_failure(struct se_cmd *, sense_reason_t); | 165 | void transport_generic_request_failure(struct se_cmd *, sense_reason_t); |
166 | void __target_execute_cmd(struct se_cmd *); | ||
167 | int transport_lookup_tmr_lun(struct se_cmd *, u64); | 166 | int transport_lookup_tmr_lun(struct se_cmd *, u64); |
168 | void core_allocate_nexus_loss_ua(struct se_node_acl *acl); | 167 | void core_allocate_nexus_loss_ua(struct se_node_acl *acl); |
169 | 168 | ||