From: Nicholas Bellinger <nab@xxxxxxxxxxxxxxx> This patch fixes a deadlock in tcm_qla2xxx_release_cmd() with the new shutdown code where qla_hw_data->hardware_lock may already held during an exception path for tcm_qla2xxx_handle_cmd() -> transport_lookup_cmd_lun(). This occurs when the descriptor has to released directly in tcm_qla2xxx_free_cmd() via transport_generic_free_cmd() -> tcm_qla2xxx_release_cmd() due to a missing se_device association. Reported-by: Roland Dreier <roland@xxxxxxxxxxxxxxx> Signed-off-by: Nicholas Bellinger <nab@xxxxxxxxxxxxxxxxxxxxx> --- drivers/target/tcm_qla2xxx/tcm_qla2xxx_fabric.c | 59 ++++++++++++++++------- 1 files changed, 41 insertions(+), 18 deletions(-) diff --git a/drivers/target/tcm_qla2xxx/tcm_qla2xxx_fabric.c b/drivers/target/tcm_qla2xxx/tcm_qla2xxx_fabric.c index 189eabe..2ba34b4 100644 --- a/drivers/target/tcm_qla2xxx/tcm_qla2xxx_fabric.c +++ b/drivers/target/tcm_qla2xxx/tcm_qla2xxx_fabric.c @@ -447,6 +447,33 @@ void tcm_qla2xxx_check_stop_free(struct se_cmd *se_cmd) } /* + * Called with qla_hw_data->hardware_lock held.. + */ +static int tcm_qla2xxx_release_shutdown_check( + struct qla_tgt_cmd *cmd, + struct qla_tgt_sess *sess) +{ + /* + * During shutdown, qla_tgt_wait_for_cmds() will be waiting on + * this outstanding qla_tgt_cmd descriptor. For this case, + * perform the completion and return, and qla_tgt_wait_for_cmds() + * will handle the direct call to qla_tgt_free_cmd() + */ + if (cmd->tgt->tgt_stop || sess->tearing_down) { + if (atomic_read(&cmd->cmd_free_comp_set) || + atomic_read(&cmd->cmd_free)) { + pr_warn("Detected shutdown, calling complete(" + "&cmd->cmd_free_comp): cmd: %p\n", cmd); + complete(&cmd->cmd_free_comp); + return 1; + } + } + list_del(&cmd->cmd_list); + + return 0; +} + +/* * Callback from TCM Core to release underlying fabric descriptor */ void tcm_qla2xxx_release_cmd(struct se_cmd *se_cmd) @@ -455,6 +482,7 @@ void tcm_qla2xxx_release_cmd(struct se_cmd *se_cmd) struct qla_tgt_sess *sess; struct qla_hw_data *ha; unsigned long flags; + int ret; if (se_cmd->se_tmr_req != NULL) return; @@ -467,6 +495,16 @@ void tcm_qla2xxx_release_cmd(struct se_cmd *se_cmd) ha = sess->vha->hw; /* + * Check if qla_hw_data->hardware_lock is already held from an + * exception path.. This also skips the possible wait for + * completion on cmd_stop_free_comp below.. + */ + if (!cmd->locked_rsp) { + if (!tcm_qla2xxx_release_shutdown_check(cmd, sess)) + qla_tgt_free_cmd(cmd); + return; + } + /* * If the callback to tcm_qla2xxx_check_stop_free() has not finished, * before the release path is invoked, go ahead and wait on * cmd_stop_free_comp until tcm_qla2xxx_check_stop_free completes. @@ -480,26 +518,11 @@ void tcm_qla2xxx_release_cmd(struct se_cmd *se_cmd) wait_for_completion(&cmd->cmd_stop_free_comp); spin_lock_irqsave(&ha->hardware_lock, flags); } - /* - * During shutdown, qla_tgt_wait_for_cmds() will be waiting on - * this outstanding qla_tgt_cmd descriptor. For this case, - * perform the completion and return, and qla_tgt_wait_for_cmds() - * will handle the direct call to qla_tgt_free_cmd() - */ - if (cmd->tgt->tgt_stop || sess->tearing_down) { - if (atomic_read(&cmd->cmd_free_comp_set) || - atomic_read(&cmd->cmd_free)) { - pr_warn("Detected shutdown, calling complete(" - "&cmd->cmd_free_comp): cmd: %p\n", cmd); - spin_unlock_irqrestore(&ha->hardware_lock, flags); - complete(&cmd->cmd_free_comp); - return; - } - } - list_del(&cmd->cmd_list); + ret = tcm_qla2xxx_release_shutdown_check(cmd, sess); spin_unlock_irqrestore(&ha->hardware_lock, flags); - qla_tgt_free_cmd(cmd); + if (!ret) + qla_tgt_free_cmd(cmd); } int tcm_qla2xxx_shutdown_session(struct se_session *se_sess) -- 1.7.2.5 -- To unsubscribe from this list: send the line "unsubscribe target-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html