scsi: target: iscsi: Fix cmd abort fabric stop race
Maurizio found a race where the abort and cmd stop paths can race as follows: 1. thread1 runs iscsit_release_commands_from_conn and sets CMD_T_FABRIC_STOP. 2. thread2 runs iscsit_aborted_task and then does __iscsit_free_cmd. It then returns from the aborted_task callout and we finish target_handle_abort and do: target_handle_abort -> transport_cmd_check_stop_to_fabric -> lio_check_stop_free -> target_put_sess_cmd The cmd is now freed. 3. thread1 now finishes iscsit_release_commands_from_conn and runs iscsit_free_cmd while accessing a command we just released. In __target_check_io_state we check for CMD_T_FABRIC_STOP and set the CMD_T_ABORTED if the driver is not cleaning up the cmd because of a session shutdown. However, iscsit_release_commands_from_conn only sets the CMD_T_FABRIC_STOP and does not check to see if the abort path has claimed completion ownership of the command. This adds a check in iscsit_release_commands_from_conn so only the abort or fabric stop path cleanup the command. Link: https://lore.kernel.org/r/1605318378-9269-1-git-send-email-michael.christie@oracle.com Reported-by: Maurizio Lombardi <mlombard@redhat.com> Reviewed-by: Maurizio Lombardi <mlombard@redhat.com> Signed-off-by: Mike Christie <michael.christie@oracle.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
parent
fe0a8a95e7
commit
f36199355c
|
@ -483,8 +483,7 @@ EXPORT_SYMBOL(iscsit_queue_rsp);
|
||||||
void iscsit_aborted_task(struct iscsi_conn *conn, struct iscsi_cmd *cmd)
|
void iscsit_aborted_task(struct iscsi_conn *conn, struct iscsi_cmd *cmd)
|
||||||
{
|
{
|
||||||
spin_lock_bh(&conn->cmd_lock);
|
spin_lock_bh(&conn->cmd_lock);
|
||||||
if (!list_empty(&cmd->i_conn_node) &&
|
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);
|
spin_unlock_bh(&conn->cmd_lock);
|
||||||
|
|
||||||
|
@ -4083,12 +4082,22 @@ static void iscsit_release_commands_from_conn(struct iscsi_conn *conn)
|
||||||
spin_lock_bh(&conn->cmd_lock);
|
spin_lock_bh(&conn->cmd_lock);
|
||||||
list_splice_init(&conn->conn_cmd_list, &tmp_list);
|
list_splice_init(&conn->conn_cmd_list, &tmp_list);
|
||||||
|
|
||||||
list_for_each_entry(cmd, &tmp_list, i_conn_node) {
|
list_for_each_entry_safe(cmd, cmd_tmp, &tmp_list, i_conn_node) {
|
||||||
struct se_cmd *se_cmd = &cmd->se_cmd;
|
struct se_cmd *se_cmd = &cmd->se_cmd;
|
||||||
|
|
||||||
if (se_cmd->se_tfo != NULL) {
|
if (se_cmd->se_tfo != NULL) {
|
||||||
spin_lock_irq(&se_cmd->t_state_lock);
|
spin_lock_irq(&se_cmd->t_state_lock);
|
||||||
|
if (se_cmd->transport_state & CMD_T_ABORTED) {
|
||||||
|
/*
|
||||||
|
* LIO's abort path owns the cleanup for this,
|
||||||
|
* so put it back on the list and let
|
||||||
|
* aborted_task handle it.
|
||||||
|
*/
|
||||||
|
list_move_tail(&cmd->i_conn_node,
|
||||||
|
&conn->conn_cmd_list);
|
||||||
|
} else {
|
||||||
se_cmd->transport_state |= CMD_T_FABRIC_STOP;
|
se_cmd->transport_state |= CMD_T_FABRIC_STOP;
|
||||||
|
}
|
||||||
spin_unlock_irq(&se_cmd->t_state_lock);
|
spin_unlock_irq(&se_cmd->t_state_lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue