Skip to content

DLPX-72513 cmd_kref leak prevents reestablishing connection for iSCSI initiator #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 3, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 42 additions & 7 deletions drivers/target/iscsi/iscsi_target.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -4071,7 +4069,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;
/*
Expand All @@ -4080,21 +4079,57 @@ 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) {
spin_lock_irq(&se_cmd->t_state_lock);
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);
pr_debug("%s: freeing TMR icmd 0x%px cmd 0x%px\n",
__func__, cmd, se_cmd);
iscsit_free_cmd(cmd, true);
pr_debug("%s: TMR freed\n", __func__);
}

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);
Expand Down