aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicholas Bellinger <nab@linux-iscsi.org>2017-03-23 20:19:24 -0400
committerNicholas Bellinger <nab@linux-iscsi.org>2017-03-30 04:36:51 -0400
commitefb2ea770bb3b0f40007530bc8b0c22f36e1c5eb (patch)
tree503c3d80c8872dbf3a1f05bb767c2054c9a65573
parentafea03fcf3d5db8a968f4b97797b8b83ada0dba3 (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.c12
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;