| From af3779a34a7e2942f9acdef0392e5a768948e1a9 Mon Sep 17 00:00:00 2001 |
| From: Nicholas Bellinger <nab@linux-iscsi.org> |
| Date: Thu, 23 Mar 2017 17:19:24 -0700 |
| Subject: [PATCH] iscsi-target: Fix TMR reference leak during session shutdown |
| |
| commit efb2ea770bb3b0f40007530bc8b0c22f36e1c5eb upstream. |
| |
| 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> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c |
| index 1f38177207e0..da5a5fcb8c29 100644 |
| --- a/drivers/target/iscsi/iscsi_target_util.c |
| +++ b/drivers/target/iscsi/iscsi_target_util.c |
| @@ -735,21 +735,23 @@ void iscsit_free_cmd(struct iscsi_cmd *cmd, bool shutdown) |
| { |
| struct se_cmd *se_cmd = NULL; |
| int rc; |
| + bool op_scsi = false; |
| /* |
| * Determine if a struct se_cmd is associated with |
| * this struct iscsi_cmd. |
| */ |
| switch (cmd->iscsi_opcode) { |
| case ISCSI_OP_SCSI_CMD: |
| - se_cmd = &cmd->se_cmd; |
| - __iscsit_free_cmd(cmd, true, shutdown); |
| + op_scsi = true; |
| /* |
| * Fallthrough |
| */ |
| case ISCSI_OP_SCSI_TMFUNC: |
| - rc = transport_generic_free_cmd(&cmd->se_cmd, shutdown); |
| - if (!rc && shutdown && se_cmd && se_cmd->se_sess) { |
| - __iscsit_free_cmd(cmd, true, shutdown); |
| + se_cmd = &cmd->se_cmd; |
| + __iscsit_free_cmd(cmd, op_scsi, shutdown); |
| + rc = transport_generic_free_cmd(se_cmd, shutdown); |
| + if (!rc && shutdown && se_cmd->se_sess) { |
| + __iscsit_free_cmd(cmd, op_scsi, shutdown); |
| target_put_sess_cmd(se_cmd); |
| } |
| break; |
| -- |
| 2.12.0 |
| |