diff options
author | Nicholas Bellinger <nab@linux-iscsi.org> | 2016-12-07 01:45:46 -0500 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2017-02-08 10:47:19 -0500 |
commit | c54eeffbe9338fa982dc853d816fda9202a13b5a (patch) | |
tree | d2ae7722aa231eb23c2a89fcaf430461f9eed320 /drivers/target | |
parent | 0583c261e6325f392c1f7a1b9112e31298e1a4bd (diff) |
target: Fix early transport_generic_handle_tmr abort scenario
This patch fixes a bug where incoming task management requests
can be explicitly aborted during an active LUN_RESET, but who's
struct work_struct are canceled in-flight before execution.
This occurs when core_tmr_drain_tmr_list() invokes cancel_work_sync()
for the incoming se_tmr_req->task_cmd->work, resulting in cmd->work
for target_tmr_work() never getting invoked and the aborted TMR
waiting indefinately within transport_wait_for_tasks().
To address this case, perform a CMD_T_ABORTED check early in
transport_generic_handle_tmr(), and invoke the normal path via
transport_cmd_check_stop_to_fabric() to complete any TMR kthreads
blocked waiting for CMD_T_STOP in transport_wait_for_tasks().
Also, move the TRANSPORT_ISTATE_PROCESSING assignment earlier
into transport_generic_handle_tmr() so the existing check in
core_tmr_drain_tmr_list() avoids attempting abort the incoming
se_tmr_req->task_cmd->work if it has already been queued into
se_device->tmr_wq.
Reported-by: Rob Millner <rlm@daterainc.com>
Tested-by: Rob Millner <rlm@daterainc.com>
Cc: Rob Millner <rlm@daterainc.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Cc: stable@vger.kernel.org # 3.14+
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
Diffstat (limited to 'drivers/target')
-rw-r--r-- | drivers/target/target_core_transport.c | 17 |
1 files changed, 15 insertions, 2 deletions
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 1cadc9eefa21..8b698432aea5 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c | |||
@@ -3110,7 +3110,6 @@ static void target_tmr_work(struct work_struct *work) | |||
3110 | spin_unlock_irqrestore(&cmd->t_state_lock, flags); | 3110 | spin_unlock_irqrestore(&cmd->t_state_lock, flags); |
3111 | goto check_stop; | 3111 | goto check_stop; |
3112 | } | 3112 | } |
3113 | cmd->t_state = TRANSPORT_ISTATE_PROCESSING; | ||
3114 | spin_unlock_irqrestore(&cmd->t_state_lock, flags); | 3113 | spin_unlock_irqrestore(&cmd->t_state_lock, flags); |
3115 | 3114 | ||
3116 | cmd->se_tfo->queue_tm_rsp(cmd); | 3115 | cmd->se_tfo->queue_tm_rsp(cmd); |
@@ -3123,11 +3122,25 @@ int transport_generic_handle_tmr( | |||
3123 | struct se_cmd *cmd) | 3122 | struct se_cmd *cmd) |
3124 | { | 3123 | { |
3125 | unsigned long flags; | 3124 | unsigned long flags; |
3125 | bool aborted = false; | ||
3126 | 3126 | ||
3127 | spin_lock_irqsave(&cmd->t_state_lock, flags); | 3127 | spin_lock_irqsave(&cmd->t_state_lock, flags); |
3128 | cmd->transport_state |= CMD_T_ACTIVE; | 3128 | if (cmd->transport_state & CMD_T_ABORTED) { |
3129 | aborted = true; | ||
3130 | } else { | ||
3131 | cmd->t_state = TRANSPORT_ISTATE_PROCESSING; | ||
3132 | cmd->transport_state |= CMD_T_ACTIVE; | ||
3133 | } | ||
3129 | spin_unlock_irqrestore(&cmd->t_state_lock, flags); | 3134 | spin_unlock_irqrestore(&cmd->t_state_lock, flags); |
3130 | 3135 | ||
3136 | if (aborted) { | ||
3137 | pr_warn_ratelimited("handle_tmr caught CMD_T_ABORTED TMR %d" | ||
3138 | "ref_tag: %llu tag: %llu\n", cmd->se_tmr_req->function, | ||
3139 | cmd->se_tmr_req->ref_task_tag, cmd->tag); | ||
3140 | transport_cmd_check_stop_to_fabric(cmd); | ||
3141 | return 0; | ||
3142 | } | ||
3143 | |||
3131 | INIT_WORK(&cmd->work, target_tmr_work); | 3144 | INIT_WORK(&cmd->work, target_tmr_work); |
3132 | queue_work(cmd->se_dev->tmr_wq, &cmd->work); | 3145 | queue_work(cmd->se_dev->tmr_wq, &cmd->work); |
3133 | return 0; | 3146 | return 0; |