Commit fa7e25cf authored by Nicholas Bellinger's avatar Nicholas Bellinger

target: Fix unknown fabric callback queue-full errors

This patch fixes a set of queue-full response handling
bugs, where outgoing responses are leaked when a fabric
driver is propagating non -EAGAIN or -ENOMEM errors
to target-core.

It introduces TRANSPORT_COMPLETE_QF_ERR state used to
signal when CHECK_CONDITION status should be generated,
when fabric driver ->write_pending(), ->queue_data_in(),
or ->queue_status() callbacks fail with non -EAGAIN or
-ENOMEM errors, and data-transfer should not be retried.

Note all fabric driver -EAGAIN and -ENOMEM errors are
still retried indefinately with associated data-transfer
callbacks, following existing queue-full logic.

Also fix two missing ->queue_status() queue-full cases
related to CMD_T_ABORTED w/ TAS status handling.
Reported-by: default avatarPotnuri Bharat Teja <bharat@chelsio.com>
Reviewed-by: default avatarPotnuri Bharat Teja <bharat@chelsio.com>
Tested-by: default avatarPotnuri Bharat Teja <bharat@chelsio.com>
Cc: Potnuri Bharat Teja <bharat@chelsio.com>
Reported-by: default avatarSteve Wise <swise@opengridcomputing.com>
Cc: Steve Wise <swise@opengridcomputing.com>
Cc: Sagi Grimberg <sagi@grimberg.me>
Signed-off-by: default avatarNicholas Bellinger <nab@linux-iscsi.org>
parent abe342a5
...@@ -64,8 +64,9 @@ struct kmem_cache *t10_alua_lba_map_cache; ...@@ -64,8 +64,9 @@ struct kmem_cache *t10_alua_lba_map_cache;
struct kmem_cache *t10_alua_lba_map_mem_cache; struct kmem_cache *t10_alua_lba_map_mem_cache;
static void transport_complete_task_attr(struct se_cmd *cmd); static void transport_complete_task_attr(struct se_cmd *cmd);
static int translate_sense_reason(struct se_cmd *cmd, sense_reason_t reason);
static void transport_handle_queue_full(struct se_cmd *cmd, static void transport_handle_queue_full(struct se_cmd *cmd,
struct se_device *dev); struct se_device *dev, int err, bool write_pending);
static int transport_put_cmd(struct se_cmd *cmd); static int transport_put_cmd(struct se_cmd *cmd);
static void target_complete_ok_work(struct work_struct *work); static void target_complete_ok_work(struct work_struct *work);
...@@ -804,7 +805,8 @@ void target_qf_do_work(struct work_struct *work) ...@@ -804,7 +805,8 @@ void target_qf_do_work(struct work_struct *work)
if (cmd->t_state == TRANSPORT_COMPLETE_QF_WP) if (cmd->t_state == TRANSPORT_COMPLETE_QF_WP)
transport_write_pending_qf(cmd); transport_write_pending_qf(cmd);
else if (cmd->t_state == TRANSPORT_COMPLETE_QF_OK) else if (cmd->t_state == TRANSPORT_COMPLETE_QF_OK ||
cmd->t_state == TRANSPORT_COMPLETE_QF_ERR)
transport_complete_qf(cmd); transport_complete_qf(cmd);
} }
} }
...@@ -1719,7 +1721,7 @@ void transport_generic_request_failure(struct se_cmd *cmd, ...@@ -1719,7 +1721,7 @@ void transport_generic_request_failure(struct se_cmd *cmd,
} }
trace_target_cmd_complete(cmd); trace_target_cmd_complete(cmd);
ret = cmd->se_tfo->queue_status(cmd); ret = cmd->se_tfo->queue_status(cmd);
if (ret == -EAGAIN || ret == -ENOMEM) if (ret)
goto queue_full; goto queue_full;
goto check_stop; goto check_stop;
default: default:
...@@ -1730,7 +1732,7 @@ void transport_generic_request_failure(struct se_cmd *cmd, ...@@ -1730,7 +1732,7 @@ void transport_generic_request_failure(struct se_cmd *cmd,
} }
ret = transport_send_check_condition_and_sense(cmd, sense_reason, 0); ret = transport_send_check_condition_and_sense(cmd, sense_reason, 0);
if (ret == -EAGAIN || ret == -ENOMEM) if (ret)
goto queue_full; goto queue_full;
check_stop: check_stop:
...@@ -1739,8 +1741,7 @@ void transport_generic_request_failure(struct se_cmd *cmd, ...@@ -1739,8 +1741,7 @@ void transport_generic_request_failure(struct se_cmd *cmd,
return; return;
queue_full: queue_full:
cmd->t_state = TRANSPORT_COMPLETE_QF_OK; transport_handle_queue_full(cmd, cmd->se_dev, ret, false);
transport_handle_queue_full(cmd, cmd->se_dev);
} }
EXPORT_SYMBOL(transport_generic_request_failure); EXPORT_SYMBOL(transport_generic_request_failure);
...@@ -1977,13 +1978,29 @@ static void transport_complete_qf(struct se_cmd *cmd) ...@@ -1977,13 +1978,29 @@ static void transport_complete_qf(struct se_cmd *cmd)
int ret = 0; int ret = 0;
transport_complete_task_attr(cmd); transport_complete_task_attr(cmd);
/*
* If a fabric driver ->write_pending() or ->queue_data_in() callback
* has returned neither -ENOMEM or -EAGAIN, assume it's fatal and
* the same callbacks should not be retried. Return CHECK_CONDITION
* if a scsi_status is not already set.
*
* If a fabric driver ->queue_status() has returned non zero, always
* keep retrying no matter what..
*/
if (cmd->t_state == TRANSPORT_COMPLETE_QF_ERR) {
if (cmd->scsi_status)
goto queue_status;
if (cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) { cmd->se_cmd_flags |= SCF_EMULATED_TASK_SENSE;
trace_target_cmd_complete(cmd); cmd->scsi_status = SAM_STAT_CHECK_CONDITION;
ret = cmd->se_tfo->queue_status(cmd); cmd->scsi_sense_length = TRANSPORT_SENSE_BUFFER;
goto out; translate_sense_reason(cmd, TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE);
goto queue_status;
} }
if (cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE)
goto queue_status;
switch (cmd->data_direction) { switch (cmd->data_direction) {
case DMA_FROM_DEVICE: case DMA_FROM_DEVICE:
if (cmd->scsi_status) if (cmd->scsi_status)
...@@ -2007,19 +2024,33 @@ static void transport_complete_qf(struct se_cmd *cmd) ...@@ -2007,19 +2024,33 @@ static void transport_complete_qf(struct se_cmd *cmd)
break; break;
} }
out:
if (ret < 0) { if (ret < 0) {
transport_handle_queue_full(cmd, cmd->se_dev); transport_handle_queue_full(cmd, cmd->se_dev, ret, false);
return; return;
} }
transport_lun_remove_cmd(cmd); transport_lun_remove_cmd(cmd);
transport_cmd_check_stop_to_fabric(cmd); transport_cmd_check_stop_to_fabric(cmd);
} }
static void transport_handle_queue_full( static void transport_handle_queue_full(struct se_cmd *cmd, struct se_device *dev,
struct se_cmd *cmd, int err, bool write_pending)
struct se_device *dev)
{ {
/*
* -EAGAIN or -ENOMEM signals retry of ->write_pending() and/or
* ->queue_data_in() callbacks from new process context.
*
* Otherwise for other errors, transport_complete_qf() will send
* CHECK_CONDITION via ->queue_status() instead of attempting to
* retry associated fabric driver data-transfer callbacks.
*/
if (err == -EAGAIN || err == -ENOMEM) {
cmd->t_state = (write_pending) ? TRANSPORT_COMPLETE_QF_WP :
TRANSPORT_COMPLETE_QF_OK;
} else {
pr_warn_ratelimited("Got unknown fabric queue status: %d\n", err);
cmd->t_state = TRANSPORT_COMPLETE_QF_ERR;
}
spin_lock_irq(&dev->qf_cmd_lock); spin_lock_irq(&dev->qf_cmd_lock);
list_add_tail(&cmd->se_qf_node, &cmd->se_dev->qf_cmd_list); list_add_tail(&cmd->se_qf_node, &cmd->se_dev->qf_cmd_list);
atomic_inc_mb(&dev->dev_qf_count); atomic_inc_mb(&dev->dev_qf_count);
...@@ -2083,7 +2114,7 @@ static void target_complete_ok_work(struct work_struct *work) ...@@ -2083,7 +2114,7 @@ static void target_complete_ok_work(struct work_struct *work)
WARN_ON(!cmd->scsi_status); WARN_ON(!cmd->scsi_status);
ret = transport_send_check_condition_and_sense( ret = transport_send_check_condition_and_sense(
cmd, 0, 1); cmd, 0, 1);
if (ret == -EAGAIN || ret == -ENOMEM) if (ret)
goto queue_full; goto queue_full;
transport_lun_remove_cmd(cmd); transport_lun_remove_cmd(cmd);
...@@ -2109,7 +2140,7 @@ static void target_complete_ok_work(struct work_struct *work) ...@@ -2109,7 +2140,7 @@ static void target_complete_ok_work(struct work_struct *work)
} else if (rc) { } else if (rc) {
ret = transport_send_check_condition_and_sense(cmd, ret = transport_send_check_condition_and_sense(cmd,
rc, 0); rc, 0);
if (ret == -EAGAIN || ret == -ENOMEM) if (ret)
goto queue_full; goto queue_full;
transport_lun_remove_cmd(cmd); transport_lun_remove_cmd(cmd);
...@@ -2134,7 +2165,7 @@ static void target_complete_ok_work(struct work_struct *work) ...@@ -2134,7 +2165,7 @@ static void target_complete_ok_work(struct work_struct *work)
if (target_read_prot_action(cmd)) { if (target_read_prot_action(cmd)) {
ret = transport_send_check_condition_and_sense(cmd, ret = transport_send_check_condition_and_sense(cmd,
cmd->pi_err, 0); cmd->pi_err, 0);
if (ret == -EAGAIN || ret == -ENOMEM) if (ret)
goto queue_full; goto queue_full;
transport_lun_remove_cmd(cmd); transport_lun_remove_cmd(cmd);
...@@ -2144,7 +2175,7 @@ static void target_complete_ok_work(struct work_struct *work) ...@@ -2144,7 +2175,7 @@ static void target_complete_ok_work(struct work_struct *work)
trace_target_cmd_complete(cmd); trace_target_cmd_complete(cmd);
ret = cmd->se_tfo->queue_data_in(cmd); ret = cmd->se_tfo->queue_data_in(cmd);
if (ret == -EAGAIN || ret == -ENOMEM) if (ret)
goto queue_full; goto queue_full;
break; break;
case DMA_TO_DEVICE: case DMA_TO_DEVICE:
...@@ -2157,7 +2188,7 @@ static void target_complete_ok_work(struct work_struct *work) ...@@ -2157,7 +2188,7 @@ static void target_complete_ok_work(struct work_struct *work)
atomic_long_add(cmd->data_length, atomic_long_add(cmd->data_length,
&cmd->se_lun->lun_stats.tx_data_octets); &cmd->se_lun->lun_stats.tx_data_octets);
ret = cmd->se_tfo->queue_data_in(cmd); ret = cmd->se_tfo->queue_data_in(cmd);
if (ret == -EAGAIN || ret == -ENOMEM) if (ret)
goto queue_full; goto queue_full;
break; break;
} }
...@@ -2166,7 +2197,7 @@ static void target_complete_ok_work(struct work_struct *work) ...@@ -2166,7 +2197,7 @@ static void target_complete_ok_work(struct work_struct *work)
queue_status: queue_status:
trace_target_cmd_complete(cmd); trace_target_cmd_complete(cmd);
ret = cmd->se_tfo->queue_status(cmd); ret = cmd->se_tfo->queue_status(cmd);
if (ret == -EAGAIN || ret == -ENOMEM) if (ret)
goto queue_full; goto queue_full;
break; break;
default: default:
...@@ -2180,8 +2211,8 @@ static void target_complete_ok_work(struct work_struct *work) ...@@ -2180,8 +2211,8 @@ static void target_complete_ok_work(struct work_struct *work)
queue_full: queue_full:
pr_debug("Handling complete_ok QUEUE_FULL: se_cmd: %p," pr_debug("Handling complete_ok QUEUE_FULL: se_cmd: %p,"
" data_direction: %d\n", cmd, cmd->data_direction); " data_direction: %d\n", cmd, cmd->data_direction);
cmd->t_state = TRANSPORT_COMPLETE_QF_OK;
transport_handle_queue_full(cmd, cmd->se_dev); transport_handle_queue_full(cmd, cmd->se_dev, ret, false);
} }
void target_free_sgl(struct scatterlist *sgl, int nents) void target_free_sgl(struct scatterlist *sgl, int nents)
...@@ -2449,18 +2480,14 @@ transport_generic_new_cmd(struct se_cmd *cmd) ...@@ -2449,18 +2480,14 @@ transport_generic_new_cmd(struct se_cmd *cmd)
spin_unlock_irqrestore(&cmd->t_state_lock, flags); spin_unlock_irqrestore(&cmd->t_state_lock, flags);
ret = cmd->se_tfo->write_pending(cmd); ret = cmd->se_tfo->write_pending(cmd);
if (ret == -EAGAIN || ret == -ENOMEM) if (ret)
goto queue_full; goto queue_full;
/* fabric drivers should only return -EAGAIN or -ENOMEM as error */ return 0;
WARN_ON(ret);
return (!ret) ? 0 : TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
queue_full: queue_full:
pr_debug("Handling write_pending QUEUE__FULL: se_cmd: %p\n", cmd); pr_debug("Handling write_pending QUEUE__FULL: se_cmd: %p\n", cmd);
cmd->t_state = TRANSPORT_COMPLETE_QF_WP; transport_handle_queue_full(cmd, cmd->se_dev, ret, true);
transport_handle_queue_full(cmd, cmd->se_dev);
return 0; return 0;
} }
EXPORT_SYMBOL(transport_generic_new_cmd); EXPORT_SYMBOL(transport_generic_new_cmd);
...@@ -2470,10 +2497,10 @@ static void transport_write_pending_qf(struct se_cmd *cmd) ...@@ -2470,10 +2497,10 @@ static void transport_write_pending_qf(struct se_cmd *cmd)
int ret; int ret;
ret = cmd->se_tfo->write_pending(cmd); ret = cmd->se_tfo->write_pending(cmd);
if (ret == -EAGAIN || ret == -ENOMEM) { if (ret) {
pr_debug("Handling write_pending QUEUE__FULL: se_cmd: %p\n", pr_debug("Handling write_pending QUEUE__FULL: se_cmd: %p\n",
cmd); cmd);
transport_handle_queue_full(cmd, cmd->se_dev); transport_handle_queue_full(cmd, cmd->se_dev, ret, true);
} }
} }
...@@ -3011,6 +3038,8 @@ static int __transport_check_aborted_status(struct se_cmd *cmd, int send_status) ...@@ -3011,6 +3038,8 @@ static int __transport_check_aborted_status(struct se_cmd *cmd, int send_status)
__releases(&cmd->t_state_lock) __releases(&cmd->t_state_lock)
__acquires(&cmd->t_state_lock) __acquires(&cmd->t_state_lock)
{ {
int ret;
assert_spin_locked(&cmd->t_state_lock); assert_spin_locked(&cmd->t_state_lock);
WARN_ON_ONCE(!irqs_disabled()); WARN_ON_ONCE(!irqs_disabled());
...@@ -3034,7 +3063,9 @@ static int __transport_check_aborted_status(struct se_cmd *cmd, int send_status) ...@@ -3034,7 +3063,9 @@ static int __transport_check_aborted_status(struct se_cmd *cmd, int send_status)
trace_target_cmd_complete(cmd); trace_target_cmd_complete(cmd);
spin_unlock_irq(&cmd->t_state_lock); spin_unlock_irq(&cmd->t_state_lock);
cmd->se_tfo->queue_status(cmd); ret = cmd->se_tfo->queue_status(cmd);
if (ret)
transport_handle_queue_full(cmd, cmd->se_dev, ret, false);
spin_lock_irq(&cmd->t_state_lock); spin_lock_irq(&cmd->t_state_lock);
return 1; return 1;
...@@ -3055,6 +3086,7 @@ EXPORT_SYMBOL(transport_check_aborted_status); ...@@ -3055,6 +3086,7 @@ EXPORT_SYMBOL(transport_check_aborted_status);
void transport_send_task_abort(struct se_cmd *cmd) void transport_send_task_abort(struct se_cmd *cmd)
{ {
unsigned long flags; unsigned long flags;
int ret;
spin_lock_irqsave(&cmd->t_state_lock, flags); spin_lock_irqsave(&cmd->t_state_lock, flags);
if (cmd->se_cmd_flags & (SCF_SENT_CHECK_CONDITION)) { if (cmd->se_cmd_flags & (SCF_SENT_CHECK_CONDITION)) {
...@@ -3090,7 +3122,9 @@ void transport_send_task_abort(struct se_cmd *cmd) ...@@ -3090,7 +3122,9 @@ void transport_send_task_abort(struct se_cmd *cmd)
cmd->t_task_cdb[0], cmd->tag); cmd->t_task_cdb[0], cmd->tag);
trace_target_cmd_complete(cmd); trace_target_cmd_complete(cmd);
cmd->se_tfo->queue_status(cmd); ret = cmd->se_tfo->queue_status(cmd);
if (ret)
transport_handle_queue_full(cmd, cmd->se_dev, ret, false);
} }
static void target_tmr_work(struct work_struct *work) static void target_tmr_work(struct work_struct *work)
......
...@@ -117,6 +117,7 @@ enum transport_state_table { ...@@ -117,6 +117,7 @@ enum transport_state_table {
TRANSPORT_ISTATE_PROCESSING = 11, TRANSPORT_ISTATE_PROCESSING = 11,
TRANSPORT_COMPLETE_QF_WP = 18, TRANSPORT_COMPLETE_QF_WP = 18,
TRANSPORT_COMPLETE_QF_OK = 19, TRANSPORT_COMPLETE_QF_OK = 19,
TRANSPORT_COMPLETE_QF_ERR = 20,
}; };
/* Used for struct se_cmd->se_cmd_flags */ /* Used for struct se_cmd->se_cmd_flags */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment