@@ -483,9 +483,7 @@ EXPORT_SYMBOL(iscsit_queue_rsp);
void iscsit_aborted_task(struct iscsi_conn *conn, struct iscsi_cmd *cmd)
{
spin_lock_bh(&conn->cmd_lock);
- if (!list_empty(&cmd->i_conn_node) &&
- !(cmd->se_cmd.transport_state & CMD_T_FABRIC_STOP))
- list_del_init(&cmd->i_conn_node);
+ list_del_init(&cmd->i_conn_node);
spin_unlock_bh(&conn->cmd_lock);
__iscsit_free_cmd(cmd, true);
@@ -4056,7 +4054,8 @@ int iscsi_target_rx_thread(void *arg)
static void iscsit_release_commands_from_conn(struct iscsi_conn *conn)
{
- LIST_HEAD(tmp_list);
+ LIST_HEAD(tmp_cmd_list);
+ LIST_HEAD(tmp_tmr_list);
struct iscsi_cmd *cmd = NULL, *cmd_tmp = NULL;
struct iscsi_session *sess = conn->sess;
/*
@@ -4065,9 +4064,9 @@ static void iscsit_release_commands_from_conn(struct iscsi_conn *conn)
* has been reset -> returned sleeping pre-handler state.
*/
spin_lock_bh(&conn->cmd_lock);
- list_splice_init(&conn->conn_cmd_list, &tmp_list);
+ list_splice_init(&conn->conn_cmd_list, &tmp_cmd_list);
- list_for_each_entry(cmd, &tmp_list, i_conn_node) {
+ list_for_each_entry_safe(cmd, cmd_tmp, &tmp_cmd_list, i_conn_node) {
struct se_cmd *se_cmd = &cmd->se_cmd;
if (se_cmd->se_tfo != NULL) {
@@ -4075,11 +4074,44 @@ static void iscsit_release_commands_from_conn(struct iscsi_conn *conn)
se_cmd->transport_state |= CMD_T_FABRIC_STOP;
spin_unlock_irq(&se_cmd->t_state_lock);
}
+
+ if (se_cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)
+ list_move_tail(&cmd->i_conn_node, &tmp_tmr_list);
}
spin_unlock_bh(&conn->cmd_lock);
- list_for_each_entry_safe(cmd, cmd_tmp, &tmp_list, i_conn_node) {
+ /*
+ * We must wait for TMRs to be processed first. Any commands that were
+ * aborted by those TMRs will have been freed and removed from the
+ * tmp_cmd_list once we have finished traversing tmp_tmr_list.
+ */
+ list_for_each_entry_safe(cmd, cmd_tmp, &tmp_tmr_list, i_conn_node) {
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+
+ spin_lock_bh(&conn->cmd_lock);
list_del_init(&cmd->i_conn_node);
+ spin_unlock_bh(&conn->cmd_lock);
+
+ iscsit_increment_maxcmdsn(cmd, sess);
+ iscsit_free_cmd(cmd, true);
+ }
+
+ list_for_each_entry_safe(cmd, cmd_tmp, &tmp_cmd_list, i_conn_node) {
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+
+ /*
+ * We shouldn't be freeing any aborted commands here. Those
+ * commands should be freed by iscsit_aborted_task, and the
+ * last reference will be released by target_put_cmd_and_wait,
+ * called from core_tmr_drain_tmr_list or core_tmr_abort_task.
+ */
+ spin_lock_irq(&se_cmd->t_state_lock);
+ WARN_ON(se_cmd->transport_state & CMD_T_ABORTED);
+ spin_unlock_irq(&se_cmd->t_state_lock);
+
+ spin_lock_bh(&conn->cmd_lock);
+ list_del_init(&cmd->i_conn_node);
+ spin_unlock_bh(&conn->cmd_lock);
iscsit_increment_maxcmdsn(cmd, sess);
iscsit_free_cmd(cmd, true);