linux-scsi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] target: Fix LUN_RESET active I/O + TMR handling
@ 2016-01-12 10:24 Nicholas A. Bellinger
  2016-01-12 10:24 ` [PATCH 1/2] target: Fix LUN_RESET active I/O handling for ACK_KREF Nicholas A. Bellinger
                   ` (2 more replies)
  0 siblings, 3 replies; 12+ messages in thread
From: Nicholas A. Bellinger @ 2016-01-12 10:24 UTC (permalink / raw)
  To: target-devel
  Cc: linux-scsi, Quinn Tran, Himanshu Madhani, Sagi Grimberg,
	Christoph Hellwig, Hannes Reinecke, Andy Grover,
	Nicholas Bellinger

From: Nicholas Bellinger <nab@linux-iscsi.org>

Hi all,

This -v1 series is to address two LUN_RESET active
I/O + TMR se_cmd->cmd_kref < 0 bugs as reported
recently by Quinn & Co, that can occur during
active I/O LUN_RESET with single / multiple LIO
export port configs.

To address this bug, add __target_check_io_state()
common handler for ABORT_TASK + LUN_RESET I/O abort
cases, and move remaining se_cmd SGL page + release
into target_free_cmd_mem() to now be called directly
from the final target_release_cmd_kref() callback.

Following __target_check_io_state() code, also obtain
kref_get_unless_zero(&se_cmd->cmd_kref) for LUN_RESET
TMR abort in core_tmr_drain_tmr_list(), in order to
utilize common transport_wait_for_tasks() code
when se_tmr_req descriptors are being shutdown.

Note there are a few more cleanups to be had but
have been left off for the moment, ahead of these
specific fixes getting merged for v4.5-rc code.

Please review,

--nab

Nicholas Bellinger (2):
  target: Fix LUN_RESET active I/O handling for ACK_KREF
  target: Fix LUN_RESET active TMR descriptor handling

 drivers/target/target_core_tmr.c       | 96 +++++++++++++++++++++++++++-------
 drivers/target/target_core_transport.c | 66 ++++++++++++++---------
 include/target/target_core_base.h      |  1 +
 3 files changed, 118 insertions(+), 45 deletions(-)

-- 
1.9.1

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 1/2] target: Fix LUN_RESET active I/O handling for ACK_KREF
  2016-01-12 10:24 [PATCH 0/2] target: Fix LUN_RESET active I/O + TMR handling Nicholas A. Bellinger
@ 2016-01-12 10:24 ` Nicholas A. Bellinger
  2016-01-12 15:20   ` Christoph Hellwig
  2016-01-12 10:24 ` [PATCH 2/2] target: Fix LUN_RESET active TMR descriptor handling Nicholas A. Bellinger
  2016-01-13 20:26 ` [PATCH 0/2] target: Fix LUN_RESET active I/O + TMR handling Quinn Tran
  2 siblings, 1 reply; 12+ messages in thread
From: Nicholas A. Bellinger @ 2016-01-12 10:24 UTC (permalink / raw)
  To: target-devel
  Cc: linux-scsi, Quinn Tran, Himanshu Madhani, Sagi Grimberg,
	Christoph Hellwig, Hannes Reinecke, Andy Grover,
	Nicholas Bellinger

From: Nicholas Bellinger <nab@linux-iscsi.org>

This patch fixes a NULL pointer se_cmd->cmd_kref < 0
refcount bug during TMR LUN_RESET with active se_cmd
I/O, that can be triggered during se_cmd descriptor
shutdown + release via core_tmr_drain_state_list() code.

To address this bug, add common __target_check_io_state()
helper for ABORT_TASK + LUN_RESET w/ CMD_T_COMPLETE
checking, and set CMD_T_ABORTED + obtain ->cmd_kref for
both cases ahead of last target_put_sess_cmd() after
TFO->aborted_task() -> transport_cmd_finish_abort()
callback has completed.

It also introduces SCF_ACK_KREF to determine when
transport_cmd_finish_abort() needs to drop the second
extra reference, ahead of calling target_put_sess_cmd()
for the final kref_put(&se_cmd->cmd_kref).

Finally, move transport_put_cmd() release of SGL +
TMR + extended CDB memory into target_free_cmd_mem()
in order to avoid potential resource leaks in TMR
ABORT_TASK + LUN_RESET code-paths.  Also update
target_release_cmd_kref() accordingly.

Cc: Quinn Tran <quinn.tran@qlogic.com>
Cc: Himanshu Madhani <himanshu.madhani@qlogic.com>
Cc: Sagi Grimberg <sagig@mellanox.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Hannes Reinecke <hare@suse.de>
Cc: Andy Grover <agrover@redhat.com>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
---
 drivers/target/target_core_tmr.c       | 72 +++++++++++++++++++++++++---------
 drivers/target/target_core_transport.c | 49 ++++++++++++-----------
 include/target/target_core_base.h      |  1 +
 3 files changed, 78 insertions(+), 44 deletions(-)

diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c
index 7137042..af4adb6 100644
--- a/drivers/target/target_core_tmr.c
+++ b/drivers/target/target_core_tmr.c
@@ -107,6 +107,29 @@ static int target_check_cdb_and_preempt(struct list_head *list,
 	return 1;
 }
 
+/*
+ * Called with se_session->sess_cmd_lock held with irq disabled
+ */
+static bool __target_check_io_state(struct se_cmd *se_cmd)
+{
+	struct se_session *sess = se_cmd->se_sess;
+
+	if (!sess)
+		return false;
+
+	spin_lock(&se_cmd->t_state_lock);
+	if (se_cmd->transport_state & CMD_T_COMPLETE) {
+		printk("Attempted to abort io tag: %llu already complete,"
+			" skipping\n", se_cmd->tag);
+		spin_unlock(&se_cmd->t_state_lock);
+		return false;
+	}
+	se_cmd->transport_state |= CMD_T_ABORTED;
+	spin_unlock(&se_cmd->t_state_lock);
+
+	return kref_get_unless_zero(&se_cmd->cmd_kref);
+}
+
 void core_tmr_abort_task(
 	struct se_device *dev,
 	struct se_tmr_req *tmr,
@@ -132,27 +155,23 @@ void core_tmr_abort_task(
 
 		printk("ABORT_TASK: Found referenced %s task_tag: %llu\n",
 			se_cmd->se_tfo->get_fabric_name(), ref_tag);
-
-		spin_lock(&se_cmd->t_state_lock);
-		if (se_cmd->transport_state & CMD_T_COMPLETE) {
-			printk("ABORT_TASK: ref_tag: %llu already complete,"
-			       " skipping\n", ref_tag);
-			spin_unlock(&se_cmd->t_state_lock);
+		/*
+		 * Obtain cmd_kref and move to list for shutdown processing
+		 * if se_cmd->cmd_kref is still active, the command has not
+		 * already reached CMD_T_COMPLETE
+		 */
+		if (!__target_check_io_state(se_cmd)) {
 			spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
 			goto out;
 		}
-		se_cmd->transport_state |= CMD_T_ABORTED;
-		spin_unlock(&se_cmd->t_state_lock);
-
 		list_del_init(&se_cmd->se_cmd_list);
-		kref_get(&se_cmd->cmd_kref);
 		spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
 
 		cancel_work_sync(&se_cmd->work);
 		transport_wait_for_tasks(se_cmd);
 
-		target_put_sess_cmd(se_cmd);
 		transport_cmd_finish_abort(se_cmd, true);
+		target_put_sess_cmd(se_cmd);
 
 		printk("ABORT_TASK: Sending TMR_FUNCTION_COMPLETE for"
 				" ref_tag: %llu\n", ref_tag);
@@ -237,8 +256,10 @@ static void core_tmr_drain_state_list(
 	struct list_head *preempt_and_abort_list)
 {
 	LIST_HEAD(drain_task_list);
+	struct se_session *sess;
 	struct se_cmd *cmd, *next;
 	unsigned long flags;
+	int rc;
 
 	/*
 	 * Complete outstanding commands with TASK_ABORTED SAM status.
@@ -277,6 +298,24 @@ static void core_tmr_drain_state_list(
 		if (prout_cmd == cmd)
 			continue;
 
+		sess = cmd->se_sess;
+		if (!sess) {
+			pr_err("NULL cmd->sess for tag: %llu\n", cmd->tag);
+			dump_stack();
+			continue;
+		}
+		/*
+		 * Obtain cmd_kref and move to list for shutdown processing
+		 * if se_cmd->cmd_kref is still active, the command has not
+		 * already reached CMD_T_COMPLETE
+		 */
+		spin_lock(&sess->sess_cmd_lock);
+		rc = __target_check_io_state(cmd);
+		spin_unlock(&sess->sess_cmd_lock);
+		if (!rc) {
+			printk("LUN_RESET I/O: non-zero kref_get_unless_zero\n");
+			continue;
+		}
 		list_move_tail(&cmd->state_list, &drain_task_list);
 		cmd->state_active = false;
 	}
@@ -308,16 +347,11 @@ static void core_tmr_drain_state_list(
 		 * loop above, but we do it down here given that
 		 * cancel_work_sync may block.
 		 */
-		if (cmd->t_state == TRANSPORT_COMPLETE)
-			cancel_work_sync(&cmd->work);
-
-		spin_lock_irqsave(&cmd->t_state_lock, flags);
-		target_stop_cmd(cmd, &flags);
-
-		cmd->transport_state |= CMD_T_ABORTED;
-		spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+		cancel_work_sync(&cmd->work);
+		transport_wait_for_tasks(cmd);
 
 		core_tmr_handle_tas_abort(tmr_nacl, cmd, tas);
+		target_put_sess_cmd(cmd);
 	}
 }
 
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index c5035b9..f2c596b 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -626,6 +626,8 @@ static void transport_lun_remove_cmd(struct se_cmd *cmd)
 
 void transport_cmd_finish_abort(struct se_cmd *cmd, int remove)
 {
+	bool ack_kref = (cmd->se_cmd_flags & SCF_ACK_KREF);
+
 	if (cmd->se_cmd_flags & SCF_SE_LUN_CMD)
 		transport_lun_remove_cmd(cmd);
 	/*
@@ -637,7 +639,7 @@ void transport_cmd_finish_abort(struct se_cmd *cmd, int remove)
 
 	if (transport_cmd_check_stop_to_fabric(cmd))
 		return;
-	if (remove)
+	if (remove && ack_kref)
 		transport_put_cmd(cmd);
 }
 
@@ -2219,20 +2221,14 @@ static inline void transport_free_pages(struct se_cmd *cmd)
 }
 
 /**
- * transport_release_cmd - free a command
- * @cmd:       command to free
+ * transport_put_cmd - release a reference to a command
+ * @cmd:       command to release
  *
- * This routine unconditionally frees a command, and reference counting
- * or list removal must be done in the caller.
+ * This routine releases our reference to the command and frees it if possible.
  */
-static int transport_release_cmd(struct se_cmd *cmd)
+static int transport_put_cmd(struct se_cmd *cmd)
 {
 	BUG_ON(!cmd->se_tfo);
-
-	if (cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)
-		core_tmr_release_req(cmd->se_tmr_req);
-	if (cmd->t_task_cdb != cmd->__t_task_cdb)
-		kfree(cmd->t_task_cdb);
 	/*
 	 * If this cmd has been setup with target_get_sess_cmd(), drop
 	 * the kref and call ->release_cmd() in kref callback.
@@ -2240,18 +2236,6 @@ static int transport_release_cmd(struct se_cmd *cmd)
 	return target_put_sess_cmd(cmd);
 }
 
-/**
- * transport_put_cmd - release a reference to a command
- * @cmd:       command to release
- *
- * This routine releases our reference to the command and frees it if possible.
- */
-static int transport_put_cmd(struct se_cmd *cmd)
-{
-	transport_free_pages(cmd);
-	return transport_release_cmd(cmd);
-}
-
 void *transport_kmap_data_sg(struct se_cmd *cmd)
 {
 	struct scatterlist *sg = cmd->t_data_sg;
@@ -2456,7 +2440,7 @@ int transport_generic_free_cmd(struct se_cmd *cmd, int wait_for_tasks)
 		if (wait_for_tasks && (cmd->se_cmd_flags & SCF_SCSI_TMR_CDB))
 			 transport_wait_for_tasks(cmd);
 
-		ret = transport_release_cmd(cmd);
+		ret = transport_put_cmd(cmd);
 	} else {
 		if (wait_for_tasks)
 			transport_wait_for_tasks(cmd);
@@ -2495,8 +2479,10 @@ int target_get_sess_cmd(struct se_cmd *se_cmd, bool ack_kref)
 	 * fabric acknowledgement that requires two target_put_sess_cmd()
 	 * invocations before se_cmd descriptor release.
 	 */
-	if (ack_kref)
+	if (ack_kref) {
+		se_cmd->se_cmd_flags |= SCF_ACK_KREF;
 		kref_get(&se_cmd->cmd_kref);
+	}
 
 	spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
 	if (se_sess->sess_tearing_down) {
@@ -2514,6 +2500,16 @@ out:
 }
 EXPORT_SYMBOL(target_get_sess_cmd);
 
+static void target_free_cmd_mem(struct se_cmd *cmd)
+{
+	transport_free_pages(cmd);
+
+	if (cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)
+		core_tmr_release_req(cmd->se_tmr_req);
+	if (cmd->t_task_cdb != cmd->__t_task_cdb)
+		kfree(cmd->t_task_cdb);
+}
+
 static void target_release_cmd_kref(struct kref *kref)
 		__releases(&se_cmd->se_sess->sess_cmd_lock)
 {
@@ -2522,17 +2518,20 @@ static void target_release_cmd_kref(struct kref *kref)
 
 	if (list_empty(&se_cmd->se_cmd_list)) {
 		spin_unlock(&se_sess->sess_cmd_lock);
+		target_free_cmd_mem(se_cmd);
 		se_cmd->se_tfo->release_cmd(se_cmd);
 		return;
 	}
 	if (se_sess->sess_tearing_down && se_cmd->cmd_wait_set) {
 		spin_unlock(&se_sess->sess_cmd_lock);
+		target_free_cmd_mem(se_cmd);
 		complete(&se_cmd->cmd_wait_comp);
 		return;
 	}
 	list_del(&se_cmd->se_cmd_list);
 	spin_unlock(&se_sess->sess_cmd_lock);
 
+	target_free_cmd_mem(se_cmd);
 	se_cmd->se_tfo->release_cmd(se_cmd);
 }
 
diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h
index a4bed07..1060c52 100644
--- a/include/target/target_core_base.h
+++ b/include/target/target_core_base.h
@@ -140,6 +140,7 @@ enum se_cmd_flags_table {
 	SCF_COMPARE_AND_WRITE		= 0x00080000,
 	SCF_COMPARE_AND_WRITE_POST	= 0x00100000,
 	SCF_PASSTHROUGH_PROT_SG_TO_MEM_NOALLOC = 0x00200000,
+	SCF_ACK_KREF			= 0x00400000,
 };
 
 /* struct se_dev_entry->lun_flags and struct se_lun->lun_access */
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 2/2] target: Fix LUN_RESET active TMR descriptor handling
  2016-01-12 10:24 [PATCH 0/2] target: Fix LUN_RESET active I/O + TMR handling Nicholas A. Bellinger
  2016-01-12 10:24 ` [PATCH 1/2] target: Fix LUN_RESET active I/O handling for ACK_KREF Nicholas A. Bellinger
@ 2016-01-12 10:24 ` Nicholas A. Bellinger
  2016-01-12 15:25   ` Christoph Hellwig
  2016-01-13 20:26 ` [PATCH 0/2] target: Fix LUN_RESET active I/O + TMR handling Quinn Tran
  2 siblings, 1 reply; 12+ messages in thread
From: Nicholas A. Bellinger @ 2016-01-12 10:24 UTC (permalink / raw)
  To: target-devel
  Cc: linux-scsi, Quinn Tran, Himanshu Madhani, Sagi Grimberg,
	Christoph Hellwig, Hannes Reinecke, Andy Grover,
	Nicholas Bellinger

From: Nicholas Bellinger <nab@linux-iscsi.org>

This patch fixes a NULL pointer se_cmd->cmd_kref < 0
refcount bug during TMR LUN_RESET with TMR se_cmd,
triggered during se_cmd + se_tmr_req descriptor
shutdown + release via core_tmr_drain_tmr_list().

It obtains kref_get_unless_zero(&se_cmd->cmd_kref)
following __target_check_io_state() for active I/O
CMD_T_ABORTED, and adds transport_wait_for_tasks()
followed by the final target_put_sess_cmd() to
release TMR logic locally obtained ->cmd_kref.

Also add two new checks within target_tmr_work() to
avoid CMD_T_ABORTED -> TFO->queue_tm_rsp() callbacks
ahead of invoking the backend -> fabric put in
transport_cmd_check_stop_to_fabric().

For good measure, also change core_tmr_release_req()
to use !list_empty() + list_del_init() ahead of
se_tmr_req memory free.

Cc: Quinn Tran <quinn.tran@qlogic.com>
Cc: Himanshu Madhani <himanshu.madhani@qlogic.com>
Cc: Sagi Grimberg <sagig@mellanox.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Hannes Reinecke <hare@suse.de>
Cc: Andy Grover <agrover@redhat.com>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
---
 drivers/target/target_core_tmr.c       | 24 +++++++++++++++++++++++-
 drivers/target/target_core_transport.c | 17 +++++++++++++++++
 2 files changed, 40 insertions(+), 1 deletion(-)

diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c
index af4adb6..894a0b0 100644
--- a/drivers/target/target_core_tmr.c
+++ b/drivers/target/target_core_tmr.c
@@ -68,7 +68,8 @@ void core_tmr_release_req(struct se_tmr_req *tmr)
 
 	if (dev) {
 		spin_lock_irqsave(&dev->se_tmr_lock, flags);
-		list_del(&tmr->tmr_list);
+		if (!list_empty(&tmr->tmr_list))
+			list_del_init(&tmr->tmr_list);
 		spin_unlock_irqrestore(&dev->se_tmr_lock, flags);
 	}
 
@@ -192,9 +193,12 @@ static void core_tmr_drain_tmr_list(
 	struct list_head *preempt_and_abort_list)
 {
 	LIST_HEAD(drain_tmr_list);
+	struct se_session *sess;
 	struct se_tmr_req *tmr_p, *tmr_pp;
 	struct se_cmd *cmd;
 	unsigned long flags;
+	bool rc;
+
 	/*
 	 * Release all pending and outgoing TMRs aside from the received
 	 * LUN_RESET tmr..
@@ -220,6 +224,12 @@ static void core_tmr_drain_tmr_list(
 		if (target_check_cdb_and_preempt(preempt_and_abort_list, cmd))
 			continue;
 
+		sess = cmd->se_sess;
+		if (!sess) {
+			dump_stack();
+			continue;
+		}
+
 		spin_lock(&cmd->t_state_lock);
 		if (!(cmd->transport_state & CMD_T_ACTIVE)) {
 			spin_unlock(&cmd->t_state_lock);
@@ -229,8 +239,16 @@ static void core_tmr_drain_tmr_list(
 			spin_unlock(&cmd->t_state_lock);
 			continue;
 		}
+		cmd->transport_state |= CMD_T_ABORTED;
 		spin_unlock(&cmd->t_state_lock);
 
+		spin_lock(&sess->sess_cmd_lock);
+		rc = kref_get_unless_zero(&cmd->cmd_kref);
+		spin_unlock(&sess->sess_cmd_lock);
+		if (!rc) {
+			printk("LUN_RESET TMR: non-zero kref_get_unless_zero\n");
+			continue;
+		}
 		list_move_tail(&tmr_p->tmr_list, &drain_tmr_list);
 	}
 	spin_unlock_irqrestore(&dev->se_tmr_lock, flags);
@@ -244,7 +262,11 @@ static void core_tmr_drain_tmr_list(
 			(preempt_and_abort_list) ? "Preempt" : "", tmr_p,
 			tmr_p->function, tmr_p->response, cmd->t_state);
 
+		cancel_work_sync(&cmd->work);
+		transport_wait_for_tasks(cmd);
+
 		transport_cmd_finish_abort(cmd, 1);
+		target_put_sess_cmd(cmd);
 	}
 }
 
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index f2c596b..f6ecb60 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -2905,8 +2905,17 @@ static void target_tmr_work(struct work_struct *work)
 	struct se_cmd *cmd = container_of(work, struct se_cmd, work);
 	struct se_device *dev = cmd->se_dev;
 	struct se_tmr_req *tmr = cmd->se_tmr_req;
+	unsigned long flags;
 	int ret;
 
+	spin_lock_irqsave(&cmd->t_state_lock, flags);
+	if (cmd->transport_state & CMD_T_ABORTED) {
+		tmr->response = TMR_FUNCTION_REJECTED;
+		spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+		goto check_stop;
+	}
+	spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+
 	switch (tmr->function) {
 	case TMR_ABORT_TASK:
 		core_tmr_abort_task(dev, tmr, cmd->se_sess);
@@ -2939,9 +2948,17 @@ static void target_tmr_work(struct work_struct *work)
 		break;
 	}
 
+	spin_lock_irqsave(&cmd->t_state_lock, flags);
+	if (cmd->transport_state & CMD_T_ABORTED) {
+		spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+		goto check_stop;
+	}
 	cmd->t_state = TRANSPORT_ISTATE_PROCESSING;
+	spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+
 	cmd->se_tfo->queue_tm_rsp(cmd);
 
+check_stop:
 	transport_cmd_check_stop_to_fabric(cmd);
 }
 
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH 1/2] target: Fix LUN_RESET active I/O handling for ACK_KREF
  2016-01-12 10:24 ` [PATCH 1/2] target: Fix LUN_RESET active I/O handling for ACK_KREF Nicholas A. Bellinger
@ 2016-01-12 15:20   ` Christoph Hellwig
  2016-01-12 16:32     ` Bart Van Assche
  2016-01-23  1:45     ` Nicholas A. Bellinger
  0 siblings, 2 replies; 12+ messages in thread
From: Christoph Hellwig @ 2016-01-12 15:20 UTC (permalink / raw)
  To: Nicholas A. Bellinger
  Cc: target-devel, linux-scsi, Quinn Tran, Himanshu Madhani,
	Sagi Grimberg, Christoph Hellwig, Hannes Reinecke, Andy Grover,
	Nicholas Bellinger

> It also introduces SCF_ACK_KREF to determine when
> transport_cmd_finish_abort() needs to drop the second
> extra reference, ahead of calling target_put_sess_cmd()
> for the final kref_put(&se_cmd->cmd_kref).

It would be really useful to have all drivers follow that
ACK KREF model instead of needing to deal with driver
differences everywhere..

> Finally, move transport_put_cmd() release of SGL +
> TMR + extended CDB memory into target_free_cmd_mem()
> in order to avoid potential resource leaks in TMR
> ABORT_TASK + LUN_RESET code-paths.  Also update
> target_release_cmd_kref() accordingly.

Sounds like that should be a separate patch.

> +/*
> + * Called with se_session->sess_cmd_lock held with irq disabled
> + */

Please enforce this in the code instead of the comments, e.g.

	assert_spin_locked(&se_session->sess_cmd_lock);
	WARN_ON_ONCE(!irqs_disabled());

> +static bool __target_check_io_state(struct se_cmd *se_cmd)
> +{
> +	struct se_session *sess = se_cmd->se_sess;
> +
> +	if (!sess)
> +		return false;

Given that you expect the session lock to be held this doesn't
make sense.

> +		/*
> +		 * Obtain cmd_kref and move to list for shutdown processing
> +		 * if se_cmd->cmd_kref is still active, the command has not
> +		 * already reached CMD_T_COMPLETE
> +		 */

This just explains what __target_check_io_state does, but no why.
I'd suggest to remove the comment as it doesn't add any value.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 2/2] target: Fix LUN_RESET active TMR descriptor handling
  2016-01-12 10:24 ` [PATCH 2/2] target: Fix LUN_RESET active TMR descriptor handling Nicholas A. Bellinger
@ 2016-01-12 15:25   ` Christoph Hellwig
  2016-01-23  2:02     ` Nicholas A. Bellinger
  0 siblings, 1 reply; 12+ messages in thread
From: Christoph Hellwig @ 2016-01-12 15:25 UTC (permalink / raw)
  To: Nicholas A. Bellinger
  Cc: target-devel, linux-scsi, Quinn Tran, Himanshu Madhani,
	Sagi Grimberg, Christoph Hellwig, Hannes Reinecke, Andy Grover,
	Nicholas Bellinger

>  	if (dev) {
>  		spin_lock_irqsave(&dev->se_tmr_lock, flags);
> -		list_del(&tmr->tmr_list);
> +		if (!list_empty(&tmr->tmr_list))
> +			list_del_init(&tmr->tmr_list);

list_del_init on a empty list is fine.

> +		sess = cmd->se_sess;
> +		if (!sess) {
> +			dump_stack();
> +			continue;
> +		}

Why not something like:

		if (WARN_ON_ONCE(!sess))
			continue;

same for the previous patch.

> +		spin_lock(&sess->sess_cmd_lock);
> +		rc = kref_get_unless_zero(&cmd->cmd_kref);
> +		spin_unlock(&sess->sess_cmd_lock);

no need to take a lock around kref_get_unless_zero, it's not going to
help with anything.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 1/2] target: Fix LUN_RESET active I/O handling for ACK_KREF
  2016-01-12 15:20   ` Christoph Hellwig
@ 2016-01-12 16:32     ` Bart Van Assche
  2016-01-13  8:34       ` Christoph Hellwig
  2016-01-23  1:45     ` Nicholas A. Bellinger
  1 sibling, 1 reply; 12+ messages in thread
From: Bart Van Assche @ 2016-01-12 16:32 UTC (permalink / raw)
  To: Christoph Hellwig, Nicholas A. Bellinger
  Cc: target-devel, linux-scsi, Quinn Tran, Himanshu Madhani,
	Sagi Grimberg, Hannes Reinecke, Andy Grover, Nicholas Bellinger

On 01/12/2016 07:20 AM, Christoph Hellwig wrote:
>> It also introduces SCF_ACK_KREF to determine when
>> transport_cmd_finish_abort() needs to drop the second
>> extra reference, ahead of calling target_put_sess_cmd()
>> for the final kref_put(&se_cmd->cmd_kref).
>
> It would be really useful to have all drivers follow that
> ACK KREF model instead of needing to deal with driver
> differences everywhere..

Hello Christoph,

How about eliminating the TARGET_SCF_ACK_KREF flag ? If a command would 
only be freed upon the last target_put_sess_cmd() call then I think that 
the check_stop_free() callback wouldn't have to call 
target_put_sess_cmd() and hence that the TARGET_SCF_ACK_KREF flag could 
be removed.

Bart.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 1/2] target: Fix LUN_RESET active I/O handling for ACK_KREF
  2016-01-12 16:32     ` Bart Van Assche
@ 2016-01-13  8:34       ` Christoph Hellwig
  2016-01-13  9:14         ` Nicholas A. Bellinger
  0 siblings, 1 reply; 12+ messages in thread
From: Christoph Hellwig @ 2016-01-13  8:34 UTC (permalink / raw)
  To: Bart Van Assche
  Cc: Christoph Hellwig, Nicholas A. Bellinger, target-devel,
	linux-scsi, Quinn Tran, Himanshu Madhani, Sagi Grimberg,
	Hannes Reinecke, Andy Grover, Nicholas Bellinger

On Tue, Jan 12, 2016 at 08:32:48AM -0800, Bart Van Assche wrote:
> Hello Christoph,
>
> How about eliminating the TARGET_SCF_ACK_KREF flag ? If a command would 
> only be freed upon the last target_put_sess_cmd() call then I think that 
> the check_stop_free() callback wouldn't have to call target_put_sess_cmd() 
> and hence that the TARGET_SCF_ACK_KREF flag could be removed.

Yes, that's what I meant.  I think it shoul be generally feasibly, but
would require a careful audit of the !TARGET_SCF_ACK_KREF code path
first.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 1/2] target: Fix LUN_RESET active I/O handling for ACK_KREF
  2016-01-13  8:34       ` Christoph Hellwig
@ 2016-01-13  9:14         ` Nicholas A. Bellinger
  2016-01-13  9:29           ` Christoph Hellwig
  0 siblings, 1 reply; 12+ messages in thread
From: Nicholas A. Bellinger @ 2016-01-13  9:14 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Bart Van Assche, Nicholas A. Bellinger, target-devel, linux-scsi,
	Quinn Tran, Himanshu Madhani, Sagi Grimberg, Hannes Reinecke,
	Andy Grover

On Wed, 2016-01-13 at 09:34 +0100, Christoph Hellwig wrote:
> On Tue, Jan 12, 2016 at 08:32:48AM -0800, Bart Van Assche wrote:
> > Hello Christoph,
> >
> > How about eliminating the TARGET_SCF_ACK_KREF flag ? If a command would 
> > only be freed upon the last target_put_sess_cmd() call then I think that 
> > the check_stop_free() callback wouldn't have to call target_put_sess_cmd() 
> > and hence that the TARGET_SCF_ACK_KREF flag could be removed.
> 
> Yes, that's what I meant.  I think it shoul be generally feasibly, but
> would require a careful audit of the !TARGET_SCF_ACK_KREF code path
> first.

No, no, no.

The whole point of TARGET_SCF_ACK_KREF was so the backend driver
completion path calling check_stop_free() for one ->cmd_kref, and the
fabric driver patch calling target_put_sess_cmd() for the second
->cmd_kref both complete before attempting to free se_cmd memory.

The hw fabric drivers that have a hard requirement for
TARGET_SCF_ACK_KREF already use it, but it wouldn't hurt to convert the
remaining ones to use it.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 1/2] target: Fix LUN_RESET active I/O handling for ACK_KREF
  2016-01-13  9:14         ` Nicholas A. Bellinger
@ 2016-01-13  9:29           ` Christoph Hellwig
  0 siblings, 0 replies; 12+ messages in thread
From: Christoph Hellwig @ 2016-01-13  9:29 UTC (permalink / raw)
  To: Nicholas A. Bellinger
  Cc: Christoph Hellwig, Bart Van Assche, Nicholas A. Bellinger,
	target-devel, linux-scsi, Quinn Tran, Himanshu Madhani,
	Sagi Grimberg, Hannes Reinecke, Andy Grover

On Wed, Jan 13, 2016 at 01:14:37AM -0800, Nicholas A. Bellinger wrote:
> > Yes, that's what I meant.  I think it shoul be generally feasibly, but
> > would require a careful audit of the !TARGET_SCF_ACK_KREF code path
> > first.
> 
> No, no, no.
> 
> The whole point of TARGET_SCF_ACK_KREF was so the backend driver
> completion path calling check_stop_free() for one ->cmd_kref, and the
> fabric driver patch calling target_put_sess_cmd() for the second
> ->cmd_kref both complete before attempting to free se_cmd memory.
> 
> The hw fabric drivers that have a hard requirement for
> TARGET_SCF_ACK_KREF already use it, but it wouldn't hurt to convert the
> remaining ones to use it.

Oh, I misread what Bart said above - I though he meant to always
take the ref as well.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 0/2] target: Fix LUN_RESET active I/O + TMR handling
  2016-01-12 10:24 [PATCH 0/2] target: Fix LUN_RESET active I/O + TMR handling Nicholas A. Bellinger
  2016-01-12 10:24 ` [PATCH 1/2] target: Fix LUN_RESET active I/O handling for ACK_KREF Nicholas A. Bellinger
  2016-01-12 10:24 ` [PATCH 2/2] target: Fix LUN_RESET active TMR descriptor handling Nicholas A. Bellinger
@ 2016-01-13 20:26 ` Quinn Tran
  2 siblings, 0 replies; 12+ messages in thread
From: Quinn Tran @ 2016-01-13 20:26 UTC (permalink / raw)
  To: Nicholas A. Bellinger, target-devel
  Cc: linux-scsi, Himanshu Madhani, Sagi Grimberg, Christoph Hellwig,
	Hannes Reinecke, Andy Grover, Nicholas Bellinger

[-- Attachment #1: Type: text/plain, Size: 1574 bytes --]


On 1/12/16, 2:24 AM, "Nicholas A. Bellinger" <nab@daterainc.com> wrote:

>From: Nicholas Bellinger <nab@linux-iscsi.org>
>
>Hi all,
>
>This -v1 series is to address two LUN_RESET active
>I/O + TMR se_cmd->cmd_kref < 0 bugs as reported
>recently by Quinn & Co, that can occur during
>active I/O LUN_RESET with single / multiple LIO
>export port configs.
>
>To address this bug, add __target_check_io_state()
>common handler for ABORT_TASK + LUN_RESET I/O abort
>cases, and move remaining se_cmd SGL page + release
>into target_free_cmd_mem() to now be called directly
>from the final target_release_cmd_kref() callback.
>
>Following __target_check_io_state() code, also obtain
>kref_get_unless_zero(&se_cmd->cmd_kref) for LUN_RESET
>TMR abort in core_tmr_drain_tmr_list(), in order to
>utilize common transport_wait_for_tasks() code
>when se_tmr_req descriptors are being shutdown.
>
>Note there are a few more cleanups to be had but
>have been left off for the moment, ahead of these
>specific fixes getting merged for v4.5-rc code.
>
>Please review,
>
>--nab
>
>Nicholas Bellinger (2):
>  target: Fix LUN_RESET active I/O handling for ACK_KREF
>  target: Fix LUN_RESET active TMR descriptor handling
>
> drivers/target/target_core_tmr.c       | 96 +++++++++++++++++++++++++++-------
> drivers/target/target_core_transport.c | 66 ++++++++++++++---------
> include/target/target_core_base.h      |  1 +
> 3 files changed, 118 insertions(+), 45 deletions(-)
>
>-- 
>1.9.1
>

QT:  Nicholas,  both patches looks good.  Thanks.
>

[-- Attachment #2: winmail.dat --]
[-- Type: application/ms-tnef, Size: 4592 bytes --]

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 1/2] target: Fix LUN_RESET active I/O handling for ACK_KREF
  2016-01-12 15:20   ` Christoph Hellwig
  2016-01-12 16:32     ` Bart Van Assche
@ 2016-01-23  1:45     ` Nicholas A. Bellinger
  1 sibling, 0 replies; 12+ messages in thread
From: Nicholas A. Bellinger @ 2016-01-23  1:45 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Nicholas A. Bellinger, target-devel, linux-scsi, Quinn Tran,
	Himanshu Madhani, Sagi Grimberg, Hannes Reinecke, Andy Grover

On Tue, 2016-01-12 at 16:20 +0100, Christoph Hellwig wrote:
> > It also introduces SCF_ACK_KREF to determine when
> > transport_cmd_finish_abort() needs to drop the second
> > extra reference, ahead of calling target_put_sess_cmd()
> > for the final kref_put(&se_cmd->cmd_kref).
> 
> It would be really useful to have all drivers follow that
> ACK KREF model instead of needing to deal with driver
> differences everywhere..
> 

tcm_fc, usb-gadget, sbp-target and xen-scsiback are whom
need to be converted.

It should be easy enough to do for v4.6 code.

> > Finally, move transport_put_cmd() release of SGL +
> > TMR + extended CDB memory into target_free_cmd_mem()
> > in order to avoid potential resource leaks in TMR
> > ABORT_TASK + LUN_RESET code-paths.  Also update
> > target_release_cmd_kref() accordingly.
> 
> Sounds like that should be a separate patch.
> 

This should to stay with the original bug-fix for stable back-porting
purposes, and it's been kept together with the original for now.

It's time-consuming enough to back-port given the various upstream
changes recently.

> > +/*
> > + * Called with se_session->sess_cmd_lock held with irq disabled
> > + */
> 
> Please enforce this in the code instead of the comments, e.g.
> 
> 	assert_spin_locked(&se_session->sess_cmd_lock);
> 	WARN_ON_ONCE(!irqs_disabled());
> 

Done.

> > +static bool __target_check_io_state(struct se_cmd *se_cmd)
> > +{
> > +	struct se_session *sess = se_cmd->se_sess;
> > +
> > +	if (!sess)
> > +		return false;
> 
> Given that you expect the session lock to be held this doesn't
> make sense.
> 

Dropped.

> > +		/*
> > +		 * Obtain cmd_kref and move to list for shutdown processing
> > +		 * if se_cmd->cmd_kref is still active, the command has not
> > +		 * already reached CMD_T_COMPLETE
> > +		 */
> 
> This just explains what __target_check_io_state does, but no why.
> I'd suggest to remove the comment as it doesn't add any value.

How about the following for __target_check_io_state()..?

       /*
        * If command already reached CMD_T_COMPLETE state within
        * target_complete_cmd(), this se_cmd has been passed to
        * fabric driver and will not be aborted.
        *
        * Otherwise, obtain a local se_cmd->cmd_kref now for TMR
        * ABORT_TASK + LUN_RESET for CMD_T_ABORTED processing as
        * long as se_cmd->cmd_kref is still active unless zero.
        */


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 2/2] target: Fix LUN_RESET active TMR descriptor handling
  2016-01-12 15:25   ` Christoph Hellwig
@ 2016-01-23  2:02     ` Nicholas A. Bellinger
  0 siblings, 0 replies; 12+ messages in thread
From: Nicholas A. Bellinger @ 2016-01-23  2:02 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Nicholas A. Bellinger, target-devel, linux-scsi, Quinn Tran,
	Himanshu Madhani, Sagi Grimberg, Hannes Reinecke, Andy Grover

On Tue, 2016-01-12 at 16:25 +0100, Christoph Hellwig wrote:
> >  	if (dev) {
> >  		spin_lock_irqsave(&dev->se_tmr_lock, flags);
> > -		list_del(&tmr->tmr_list);
> > +		if (!list_empty(&tmr->tmr_list))
> > +			list_del_init(&tmr->tmr_list);
> 
> list_del_init on a empty list is fine.
> 

Done.

> > +		sess = cmd->se_sess;
> > +		if (!sess) {
> > +			dump_stack();
> > +			continue;
> > +		}
> 
> Why not something like:
> 
> 		if (WARN_ON_ONCE(!sess))
> 			continue;
> 
> same for the previous patch.
> 

Done.

> > +		spin_lock(&sess->sess_cmd_lock);
> > +		rc = kref_get_unless_zero(&cmd->cmd_kref);
> > +		spin_unlock(&sess->sess_cmd_lock);
> 
> no need to take a lock around kref_get_unless_zero, it's not going to
> help with anything.

So for -v2, this lock is being obtained before ->t_state_lock above, to
follow how __target_check_io_state() works with aborted I/O + LUN_RESET
and explicit TMR_ABORT_TASK.

It's been made consistent with the other cases for now, but depending on
how the bug-fix for se_session shutdown plus remote port LUN_RESET works
out, this may or may-not be able to go away for -v3.

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2016-01-23  2:02 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-01-12 10:24 [PATCH 0/2] target: Fix LUN_RESET active I/O + TMR handling Nicholas A. Bellinger
2016-01-12 10:24 ` [PATCH 1/2] target: Fix LUN_RESET active I/O handling for ACK_KREF Nicholas A. Bellinger
2016-01-12 15:20   ` Christoph Hellwig
2016-01-12 16:32     ` Bart Van Assche
2016-01-13  8:34       ` Christoph Hellwig
2016-01-13  9:14         ` Nicholas A. Bellinger
2016-01-13  9:29           ` Christoph Hellwig
2016-01-23  1:45     ` Nicholas A. Bellinger
2016-01-12 10:24 ` [PATCH 2/2] target: Fix LUN_RESET active TMR descriptor handling Nicholas A. Bellinger
2016-01-12 15:25   ` Christoph Hellwig
2016-01-23  2:02     ` Nicholas A. Bellinger
2016-01-13 20:26 ` [PATCH 0/2] target: Fix LUN_RESET active I/O + TMR handling Quinn Tran

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).