Re: [PATCH v2 20/36] target: Simplify session shutdown code

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, 2017-02-01 at 16:58 -0800, Bart Van Assche wrote:
> Target drivers must call target_sess_cmd_list_set_waiting() and
> target_wait_for_sess_cmds() before freeing a session. Instead of
> setting a flag in each pending command from the former function
> and waiting in the latter function on a per-command completion,
> only set a per-session flag in the former function and wait on
> a per-session completion in the latter function. Note: this patch
> changes the behavior from aborting outstanding commands back to
> waiting for command completion. See also commit 0f4a943168f3
> ("target: Fix remote-port TMR ABORT + se_cmd fabric stop").
> 
> This patch is based on the following two patches:
> * Bart Van Assche, target: Simplify session shutdown code, February 19, 2015
>   (https://github.com/bvanassche/linux/commit/8df5463d7d7619f2f1b70cfe5172eaef0aa52815).
> * Christoph Hellwig, target: Rework session shutdown code, December 7, 2015
>   (http://thread.gmane.org/gmane.linux.scsi.target.devel/10695).
> 
> Signed-off-by: Bart Van Assche <bart.vanassche@xxxxxxxxxxx>
> Reviewed-by: Hannes Reinecke <hare@xxxxxxxx>
> Cc: Christoph Hellwig <hch@xxxxxx>
> Cc: Sagi Grimberg <sagig@xxxxxxxxxxxx>
> Cc: Andy Grover <agrover@xxxxxxxxxx>
> ---
>  drivers/target/target_core_tmr.c       |   4 +-
>  drivers/target/target_core_transport.c | 106 ++++++++-------------------------
>  include/target/target_core_base.h      |   4 +-
>  3 files changed, 29 insertions(+), 85 deletions(-)
> 
> diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c
> index bc07e059fb22..95f8e7d0cf46 100644
> --- a/drivers/target/target_core_tmr.c
> +++ b/drivers/target/target_core_tmr.c
> @@ -114,7 +114,7 @@ static bool __target_check_io_state(struct se_cmd *se_cmd,
>  		spin_unlock(&se_cmd->t_state_lock);
>  		return false;
>  	}
> -	if (sess->sess_tearing_down || se_cmd->cmd_wait_set) {
> +	if (sess->sess_tearing_down) {
>  		pr_debug("Attempted to abort io tag: %llu already shutdown,"
>  			" skipping\n", se_cmd->tag);
>  		spin_unlock(&se_cmd->t_state_lock);
> @@ -247,7 +247,7 @@ static void core_tmr_drain_tmr_list(
>  			spin_unlock(&sess->sess_cmd_lock);
>  			continue;
>  		}
> -		if (sess->sess_tearing_down || cmd->cmd_wait_set) {
> +		if (sess->sess_tearing_down) {
>  			spin_unlock(&cmd->t_state_lock);
>  			spin_unlock(&sess->sess_cmd_lock);
>  			continue;
> diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
> index 8506ecc0d6c0..82930960e722 100644
> --- a/drivers/target/target_core_transport.c
> +++ b/drivers/target/target_core_transport.c
> @@ -237,8 +237,8 @@ struct se_session *transport_init_session(enum target_prot_op sup_prot_ops)
>  	INIT_LIST_HEAD(&se_sess->sess_list);
>  	INIT_LIST_HEAD(&se_sess->sess_acl_list);
>  	INIT_LIST_HEAD(&se_sess->sess_cmd_list);
> -	INIT_LIST_HEAD(&se_sess->sess_wait_list);
>  	spin_lock_init(&se_sess->sess_cmd_lock);
> +	init_waitqueue_head(&se_sess->cmd_list_wq);
>  	se_sess->sup_prot_ops = sup_prot_ops;
>  
>  	return se_sess;
> @@ -1237,7 +1237,6 @@ void transport_init_se_cmd(
>  	INIT_LIST_HEAD(&cmd->se_cmd_list);
>  	INIT_LIST_HEAD(&cmd->state_list);
>  	init_completion(&cmd->t_transport_stop_comp);
> -	init_completion(&cmd->cmd_wait_comp);
>  	init_completion(&cmd->finished);
>  	spin_lock_init(&cmd->t_state_lock);
>  	kref_init(&cmd->cmd_kref);
> @@ -2538,16 +2537,6 @@ int transport_generic_free_cmd(struct se_cmd *cmd, int wait_for_tasks)
>  		if (cmd->se_lun)
>  			transport_lun_remove_cmd(cmd);
>  	}
> -	/*
> -	 * If the task has been internally aborted due to TMR ABORT_TASK
> -	 * or LUN_RESET, target_core_tmr.c is responsible for performing
> -	 * the remaining calls to target_put_sess_cmd(), and not the
> -	 * callers of this function.
> -	 */
> -	if (aborted) {
> -		pr_debug("Detected CMD_T_ABORTED for ITT: %llu\n", cmd->tag);
> -		wait_for_completion(&cmd->cmd_wait_comp);
> -	}
>  	return transport_put_cmd(cmd);
>  }

As alluded to earlier as the second order issue wrt how concurrent
CMD_T_ABORT and fabric se_session shutdown with CMD_T_FABRIC_STOP
interact in existing code.

iscsi-target and iser-target currently depend upon
transport_generic_free_cmd() to both quiesce, and wait for the se_cmd to
complete via se_cmd->cmd_wait_comp in the CMD_T_ABORTED path before
returning to the caller.

This is required because as soon as all the transport_generic_free_cmd()
callers return, iscsi-target + iser-target expect for it to be safe to
release se_session associated memory.

Which means that if transport_generic_free_cmd() doesn't wait during the
second order issue when CMD_T_ABORTED is blocked waiting for se_cmd to
be quiesced from se_device->tmr_wq context, it will trigger
use-after-free OOPsen.

>  EXPORT_SYMBOL(transport_generic_free_cmd);
> @@ -2605,25 +2594,15 @@ static void target_release_cmd_kref(struct kref *kref)
>  	struct se_cmd *se_cmd = container_of(kref, struct se_cmd, cmd_kref);
>  	struct se_session *se_sess = se_cmd->se_sess;
>  	unsigned long flags;
> -	bool fabric_stop;
>  
>  	complete_all(&se_cmd->finished);
>  
>  	spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
> -
> -	spin_lock(&se_cmd->t_state_lock);
> -	fabric_stop = (se_cmd->transport_state & CMD_T_FABRIC_STOP) &&
> -		      (se_cmd->transport_state & CMD_T_ABORTED);
> -	spin_unlock(&se_cmd->t_state_lock);
> -
> -	if (se_cmd->cmd_wait_set || fabric_stop) {
> -		list_del_init(&se_cmd->se_cmd_list);
> -		spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
> -		target_free_cmd_mem(se_cmd);
> -		complete(&se_cmd->cmd_wait_comp);
> -		return;
> +	if (likely(!list_empty(&se_cmd->se_cmd_list))) {
> +		list_del(&se_cmd->se_cmd_list);
> +		if (list_empty(&se_sess->sess_cmd_list))
> +			wake_up(&se_sess->cmd_list_wq);
>  	}
> -	list_del_init(&se_cmd->se_cmd_list);
>  	spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
>  
>  	target_free_cmd_mem(se_cmd);
> @@ -2646,77 +2625,44 @@ int target_put_sess_cmd(struct se_cmd *se_cmd)
>  }
>  EXPORT_SYMBOL(target_put_sess_cmd);
>  
> -/* target_sess_cmd_list_set_waiting - Flag all commands in
> - *         sess_cmd_list to complete cmd_wait_comp.  Set
> - *         sess_tearing_down so no more commands are queued.
> +/**
> + * target_sess_cmd_list_set_waiting - Prevent new commands to be queued
>   * @se_sess:	session to flag
>   */
>  void target_sess_cmd_list_set_waiting(struct se_session *se_sess)
>  {
> -	struct se_cmd *se_cmd, *tmp_cmd;
>  	unsigned long flags;
> -	int rc;
>  
>  	spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
> -	if (se_sess->sess_tearing_down) {
> -		spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
> -		return;
> -	}
>  	se_sess->sess_tearing_down = 1;
> -	list_splice_init(&se_sess->sess_cmd_list, &se_sess->sess_wait_list);
> -
> -	list_for_each_entry_safe(se_cmd, tmp_cmd,
> -				 &se_sess->sess_wait_list, se_cmd_list) {
> -		rc = kref_get_unless_zero(&se_cmd->cmd_kref);
> -		if (rc) {
> -			se_cmd->cmd_wait_set = 1;
> -			spin_lock(&se_cmd->t_state_lock);
> -			se_cmd->transport_state |= CMD_T_FABRIC_STOP;
> -			spin_unlock(&se_cmd->t_state_lock);
> -		} else
> -			list_del_init(&se_cmd->se_cmd_list);
> -	}
> -
>  	spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
>  }
>  EXPORT_SYMBOL(target_sess_cmd_list_set_waiting);
>  
> -/* target_wait_for_sess_cmds - Wait for outstanding descriptors
> +/**
> + * target_wait_for_sess_cmds - Wait for outstanding commands
>   * @se_sess:    session to wait for active I/O
>   */
>  void target_wait_for_sess_cmds(struct se_session *se_sess)
>  {
> -	struct se_cmd *se_cmd, *tmp_cmd;
> -	unsigned long flags;
> -	bool tas;
> -
> -	list_for_each_entry_safe(se_cmd, tmp_cmd,
> -				&se_sess->sess_wait_list, se_cmd_list) {
> -		pr_debug("Waiting for se_cmd: %p t_state: %d, fabric state:"
> -			" %d\n", se_cmd, se_cmd->t_state,
> -			se_cmd->se_tfo->get_cmd_state(se_cmd));
> -
> -		spin_lock_irqsave(&se_cmd->t_state_lock, flags);
> -		tas = (se_cmd->transport_state & CMD_T_TAS);
> -		spin_unlock_irqrestore(&se_cmd->t_state_lock, flags);
> -
> -		if (!target_put_sess_cmd(se_cmd)) {
> -			if (tas)
> -				target_put_sess_cmd(se_cmd);
> -		}
> -
> -		wait_for_completion(&se_cmd->cmd_wait_comp);
> -		pr_debug("After cmd_wait_comp: se_cmd: %p t_state: %d"
> -			" fabric state: %d\n", se_cmd, se_cmd->t_state,
> -			se_cmd->se_tfo->get_cmd_state(se_cmd));
> -
> -		target_put_sess_cmd(se_cmd);
> -	}
> -
> -	spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
> -	WARN_ON(!list_empty(&se_sess->sess_cmd_list));
> -	spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
> +	struct se_cmd *cmd;
> +	int ret;
>  
> +	WARN_ON_ONCE(!se_sess->sess_tearing_down);
> +
> +	spin_lock_irq(&se_sess->sess_cmd_lock);
> +	do {
> +		ret = wait_event_interruptible_lock_irq_timeout(
> +				se_sess->cmd_list_wq,
> +				list_empty(&se_sess->sess_cmd_list),
> +				se_sess->sess_cmd_lock, 180 * HZ);
> +		list_for_each_entry(cmd, &se_sess->sess_cmd_list, se_cmd_list)
> +			pr_debug("%s: ITT %#llx dir %d i_state %d t_state %d len %d\n",
> +				 __func__, cmd->tag, cmd->data_direction,
> +				 cmd->se_tfo->get_cmd_state(cmd), cmd->t_state,
> +				 cmd->data_length);
> +	} while (ret <= 0);
> +	spin_unlock_irq(&se_sess->sess_cmd_lock);
>  }
>  EXPORT_SYMBOL(target_wait_for_sess_cmds);
>  
> diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h
> index e690ce36e592..d6d4465b1694 100644
> --- a/include/target/target_core_base.h
> +++ b/include/target/target_core_base.h
> @@ -441,7 +441,6 @@ struct se_cmd {
>  	u8			scsi_asc;
>  	u8			scsi_ascq;
>  	u16			scsi_sense_length;
> -	unsigned		cmd_wait_set:1;
>  	unsigned		unknown_data_length:1;
>  	bool			state_active:1;
>  	bool			send_abort_response:1;
> @@ -474,7 +473,6 @@ struct se_cmd {
>  	struct se_session	*se_sess;
>  	struct se_tmr_req	*se_tmr_req;
>  	struct list_head	se_cmd_list;
> -	struct completion	cmd_wait_comp;
>  	const struct target_core_fabric_ops *se_tfo;
>  	sense_reason_t		(*execute_cmd)(struct se_cmd *);
>  	sense_reason_t (*transport_complete_callback)(struct se_cmd *, bool, int *);
> @@ -605,8 +603,8 @@ struct se_session {
>  	struct list_head	sess_list;
>  	struct list_head	sess_acl_list;
>  	struct list_head	sess_cmd_list;
> -	struct list_head	sess_wait_list;
>  	spinlock_t		sess_cmd_lock;
> +	wait_queue_head_t	cmd_list_wq;
>  	void			*sess_cmd_map;
>  	struct percpu_ida	sess_tag_pool;
>  	struct workqueue_struct *tmf_wq;


--
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



[Index of Archives]     [Linux SCSI]     [Kernel Newbies]     [Linux SCSI Target Infrastructure]     [Share Photos]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Device Mapper]

  Powered by Linux