diff options
author | Nicholas Bellinger <nab@linux-iscsi.org> | 2017-03-23 20:19:24 -0400 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2017-03-30 04:36:51 -0400 |
commit | efb2ea770bb3b0f40007530bc8b0c22f36e1c5eb (patch) | |
tree | 503c3d80c8872dbf3a1f05bb767c2054c9a65573 | |
parent | afea03fcf3d5db8a968f4b97797b8b83ada0dba3 (diff) |
iscsi-target: Fix TMR reference leak during session shutdown
This patch fixes a iscsi-target specific TMR reference leak
during session shutdown, that could occur when a TMR was
quiesced before the hand-off back to iscsi-target code
via transport_cmd_check_stop_to_fabric().
The reference leak happens because iscsit_free_cmd() was
incorrectly skipping the final target_put_sess_cmd() for
TMRs when transport_generic_free_cmd() returned zero because
the se_cmd->cmd_kref did not reach zero, due to the missing
se_cmd assignment in original code.
The result was iscsi_cmd and it's associated se_cmd memory
would be freed once se_sess->sess_cmd_map where released,
but the associated se_tmr_req was leaked and remained part
of se_device->dev_tmr_list.
This bug would manfiest itself as kernel paging request
OOPsen in core_tmr_lun_reset(), when a left-over se_tmr_req
attempted to dereference it's se_cmd pointer that had
already been released during normal session shutdown.
To address this bug, go ahead and treat ISCSI_OP_SCSI_CMD
and ISCSI_OP_SCSI_TMFUNC the same when there is an extra
se_cmd->cmd_kref to drop in iscsit_free_cmd(), and use
op_scsi to signal __iscsit_free_cmd() when the former
needs to clear any further iscsi related I/O state.
Reported-by: Rob Millner <rlm@daterainc.com>
Cc: Rob Millner <rlm@daterainc.com>
Reported-by: Chu Yuan Lin <cyl@datera.io>
Cc: Chu Yuan Lin <cyl@datera.io>
Tested-by: Chu Yuan Lin <cyl@datera.io>
Cc: stable@vger.kernel.org # 3.10+
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
-rw-r--r-- | drivers/target/iscsi/iscsi_target_util.c | 12 |
1 files changed, 7 insertions, 5 deletions
diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index 5041a9c8bdcb..b4640338f8d8 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c | |||
@@ -737,21 +737,23 @@ void iscsit_free_cmd(struct iscsi_cmd *cmd, bool shutdown) | |||
737 | { | 737 | { |
738 | struct se_cmd *se_cmd = NULL; | 738 | struct se_cmd *se_cmd = NULL; |
739 | int rc; | 739 | int rc; |
740 | bool op_scsi = false; | ||
740 | /* | 741 | /* |
741 | * Determine if a struct se_cmd is associated with | 742 | * Determine if a struct se_cmd is associated with |
742 | * this struct iscsi_cmd. | 743 | * this struct iscsi_cmd. |
743 | */ | 744 | */ |
744 | switch (cmd->iscsi_opcode) { | 745 | switch (cmd->iscsi_opcode) { |
745 | case ISCSI_OP_SCSI_CMD: | 746 | case ISCSI_OP_SCSI_CMD: |
746 | se_cmd = &cmd->se_cmd; | 747 | op_scsi = true; |
747 | __iscsit_free_cmd(cmd, true, shutdown); | ||
748 | /* | 748 | /* |
749 | * Fallthrough | 749 | * Fallthrough |
750 | */ | 750 | */ |
751 | case ISCSI_OP_SCSI_TMFUNC: | 751 | case ISCSI_OP_SCSI_TMFUNC: |
752 | rc = transport_generic_free_cmd(&cmd->se_cmd, shutdown); | 752 | se_cmd = &cmd->se_cmd; |
753 | if (!rc && shutdown && se_cmd && se_cmd->se_sess) { | 753 | __iscsit_free_cmd(cmd, op_scsi, shutdown); |
754 | __iscsit_free_cmd(cmd, true, shutdown); | 754 | rc = transport_generic_free_cmd(se_cmd, shutdown); |
755 | if (!rc && shutdown && se_cmd->se_sess) { | ||
756 | __iscsit_free_cmd(cmd, op_scsi, shutdown); | ||
755 | target_put_sess_cmd(se_cmd); | 757 | target_put_sess_cmd(se_cmd); |
756 | } | 758 | } |
757 | break; | 759 | break; |