public inbox for linux-scsi@vger.kernel.org
 help / color / mirror / Atom feed
From: Lee Duncan <lduncan@suse.com>
To: Mike Christie <michael.christie@oracle.com>,
	cleech@redhat.com, martin.petersen@oracle.com,
	linux-scsi@vger.kernel.org, jejb@linux.ibm.com
Subject: Re: [PATCH 09/12] scsi: iscsi: Remove iscsi_get_task back_lock requirement
Date: Fri, 18 Mar 2022 10:31:34 -0700	[thread overview]
Message-ID: <3fd9469f-4b6b-7264-2365-e9aa4e9234fd@suse.com> (raw)
In-Reply-To: <20220308002747.122682-10-michael.christie@oracle.com>

On 3/7/22 16:27, Mike Christie wrote:
> We currently require that the back_lock is held when calling the functions
> that manipulate the iscsi_task refcount. The only reason for this is to
> handle races where we are handing SCSI-ml eh callbacks and the cmd is

handing -> handling

> completing at the same time the normal completion path is running, and we
> can't return from the EH callback until the driver has stopped accessing
> the cmd. By holding the back_lock while also accessing the task->state
> makes it simple to check that a cmd is completing and also get/put a
> refcount at the same time.

This last sentence doesn't completely parse. Maybe leave off the work "By"?

> 
> The problem is that we don't want to take the back_lock from the xmit path
> for normal IO since it causes contention with the completion path if the
> user has chosen to try and split those paths on different CPUs (in this
> case abusing the CPUs and igoring caching improves perf for some uses).
> 
> This patch begins to remove the back_lock requirement for
> iscsi_get/put_task by removing the requirement for the get path. Instead
> of always holding the back_lock we detect if something has done the last
> put and is about to call iscsi_free_task. The next patch will then allow
> iscsi code to do the last put on a task and only grab the back_lock if
> the refcount is now zero and it's going to call iscsi_free_task.
> 
> Signed-off-by: Mike Christie <michael.christie@oracle.com>
> ---
>   drivers/scsi/be2iscsi/be_main.c | 19 ++++++-
>   drivers/scsi/libiscsi.c         | 95 +++++++++++++++++++++++----------
>   drivers/scsi/libiscsi_tcp.c     |  5 +-
>   include/scsi/libiscsi.h         |  2 +-
>   4 files changed, 88 insertions(+), 33 deletions(-)
> 
> diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c
> index ab55681145f8..26e5446ac237 100644
> --- a/drivers/scsi/be2iscsi/be_main.c
> +++ b/drivers/scsi/be2iscsi/be_main.c
> @@ -231,6 +231,7 @@ static int beiscsi_eh_abort(struct scsi_cmnd *sc)
>   	cls_session = starget_to_session(scsi_target(sc->device));
>   	session = cls_session->dd_data;
>   
> +completion_check:
>   	/* check if we raced, task just got cleaned up under us */
>   	spin_lock_bh(&session->back_lock);
>   	if (!abrt_task || !abrt_task->sc) {
> @@ -238,7 +239,13 @@ static int beiscsi_eh_abort(struct scsi_cmnd *sc)
>   		return SUCCESS;
>   	}
>   	/* get a task ref till FW processes the req for the ICD used */
> -	__iscsi_get_task(abrt_task);
> +	if (!iscsi_get_task(abrt_task)) {
> +		spin_unlock(&session->back_lock);
> +		/* We are just about to call iscsi_free_task so wait for it. */
> +		udelay(5);
> +		goto completion_check;
> +	}
> +
>   	abrt_io_task = abrt_task->dd_data;
>   	conn = abrt_task->conn;
>   	beiscsi_conn = conn->dd_data;
> @@ -323,7 +330,15 @@ static int beiscsi_eh_device_reset(struct scsi_cmnd *sc)
>   		}
>   
>   		/* get a task ref till FW processes the req for the ICD used */
> -		__iscsi_get_task(task);
> +		if (!iscsi_get_task(task)) {
> +			/*
> +			 * The task has completed in the driver and is
> +			 * completing in libiscsi. Just ignore it here. When we
> +			 * call iscsi_eh_device_reset, it will wait for us.
> +			 */
> +			continue;
> +		}
> +
>   		io_task = task->dd_data;
>   		/* mark WRB invalid which have been not processed by FW yet */
>   		if (is_chip_be2_be3r(phba)) {
> diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
> index 5c74ab92725f..a2d0daf5bd60 100644
> --- a/drivers/scsi/libiscsi.c
> +++ b/drivers/scsi/libiscsi.c
> @@ -83,6 +83,8 @@ MODULE_PARM_DESC(debug_libiscsi_eh,
>   				"%s " dbg_fmt, __func__, ##arg);	\
>   	} while (0);
>   
> +#define ISCSI_CMD_COMPL_WAIT 5
> +
>   inline void iscsi_conn_queue_xmit(struct iscsi_conn *conn)
>   {
>   	struct Scsi_Host *shost = conn->session->host;
> @@ -482,11 +484,11 @@ static void iscsi_free_task(struct iscsi_task *task)
>   	}
>   }
>   
> -void __iscsi_get_task(struct iscsi_task *task)
> +bool iscsi_get_task(struct iscsi_task *task)
>   {
> -	refcount_inc(&task->refcount);
> +	return refcount_inc_not_zero(&task->refcount);
>   }
> -EXPORT_SYMBOL_GPL(__iscsi_get_task);
> +EXPORT_SYMBOL_GPL(iscsi_get_task);
>   
>   void __iscsi_put_task(struct iscsi_task *task)
>   {
> @@ -600,20 +602,17 @@ static bool cleanup_queued_task(struct iscsi_task *task)
>   }
>   
>   /*
> - * session frwd lock must be held and if not called for a task that is still
> - * pending or from the xmit thread, then xmit thread must be suspended
> + * session back and frwd lock must be held and if not called for a task that
> + * is still pending or from the xmit thread, then xmit thread must be suspended
>    */
> -static void fail_scsi_task(struct iscsi_task *task, int err)
> +static void __fail_scsi_task(struct iscsi_task *task, int err)
>   {
>   	struct iscsi_conn *conn = task->conn;
>   	struct scsi_cmnd *sc;
>   	int state;
>   
> -	spin_lock_bh(&conn->session->back_lock);
> -	if (cleanup_queued_task(task)) {
> -		spin_unlock_bh(&conn->session->back_lock);
> +	if (cleanup_queued_task(task))
>   		return;
> -	}
>   
>   	if (task->state == ISCSI_TASK_PENDING) {
>   		/*
> @@ -632,7 +631,15 @@ static void fail_scsi_task(struct iscsi_task *task, int err)
>   	sc->result = err << 16;
>   	scsi_set_resid(sc, scsi_bufflen(sc));
>   	iscsi_complete_task(task, state);
> -	spin_unlock_bh(&conn->session->back_lock);
> +}
> +
> +static void fail_scsi_task(struct iscsi_task *task, int err)
> +{
> +	struct iscsi_session *session = task->conn->session;
> +
> +	spin_lock_bh(&session->back_lock);
> +	__fail_scsi_task(task, err);
> +	spin_unlock_bh(&session->back_lock);
>   }
>   
>   static int iscsi_prep_mgmt_task(struct iscsi_conn *conn,
> @@ -1449,8 +1456,17 @@ static int iscsi_xmit_task(struct iscsi_conn *conn, struct iscsi_task *task,
>   	spin_lock_bh(&conn->session->back_lock);
>   
>   	if (!conn->task) {
> -		/* Take a ref so we can access it after xmit_task() */
> -		__iscsi_get_task(task);
> +		/*
> +		 * Take a ref so we can access it after xmit_task().
> +		 *
> +		 * This should never fail because the failure paths will have
> +		 * stopped the xmit thread. WARN on move on.
> +		 */
> +		if (!iscsi_get_task(task)) {
> +			spin_unlock_bh(&conn->session->back_lock);
> +			WARN_ON_ONCE(1);
> +			return 0;
> +		}
>   	} else {
>   		/* Already have a ref from when we failed to send it last call */
>   		conn->task = NULL;
> @@ -1492,7 +1508,7 @@ static int iscsi_xmit_task(struct iscsi_conn *conn, struct iscsi_task *task,
>   		 * get an extra ref that is released next time we access it
>   		 * as conn->task above.
>   		 */
> -		__iscsi_get_task(task);
> +		iscsi_get_task(task);
>   		conn->task = task;
>   	}
>   
> @@ -1907,6 +1923,7 @@ static void fail_scsi_tasks(struct iscsi_conn *conn, u64 lun, int error)
>   	struct iscsi_task *task;
>   	int i;
>   
> +restart_cmd_loop:
>   	spin_lock_bh(&session->back_lock);
>   	for (i = 0; i < session->cmds_max; i++) {
>   		task = session->cmds[i];
> @@ -1915,22 +1932,25 @@ static void fail_scsi_tasks(struct iscsi_conn *conn, u64 lun, int error)
>   
>   		if (lun != -1 && lun != task->sc->device->lun)
>   			continue;
> -
> -		__iscsi_get_task(task);
> -		spin_unlock_bh(&session->back_lock);
> +		/*
> +		 * The cmd is completing but if this is called from an eh
> +		 * callout path then when we return scsi-ml owns the cmd. Wait
> +		 * for the completion path to finish freeing the cmd.
> +		 */
> +		if (!iscsi_get_task(task)) {
> +			spin_unlock_bh(&session->back_lock);
> +			spin_unlock_bh(&session->frwd_lock);
> +			udelay(ISCSI_CMD_COMPL_WAIT);
> +			spin_lock_bh(&session->frwd_lock);
> +			goto restart_cmd_loop;
> +		}
>   
>   		ISCSI_DBG_SESSION(session,
>   				  "failing sc %p itt 0x%x state %d\n",
>   				  task->sc, task->itt, task->state);
> -		fail_scsi_task(task, error);
> -
> -		spin_unlock_bh(&session->frwd_lock);
> -		iscsi_put_task(task);
> -		spin_lock_bh(&session->frwd_lock);
> -
> -		spin_lock_bh(&session->back_lock);
> +		__fail_scsi_task(task, error);
> +		__iscsi_put_task(task);
>   	}
> -
>   	spin_unlock_bh(&session->back_lock);
>   }
>   
> @@ -2035,7 +2055,16 @@ enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc)
>   		spin_unlock(&session->back_lock);
>   		goto done;
>   	}
> -	__iscsi_get_task(task);
> +	if (!iscsi_get_task(task)) {
> +		/*
> +		 * Racing with the completion path right now, so give it more
> +		 * time so that path can complete it like normal.
> +		 */
> +		rc = BLK_EH_RESET_TIMER;
> +		task = NULL;
> +		spin_unlock(&session->back_lock);
> +		goto done;
> +	}
>   	spin_unlock(&session->back_lock);
>   
>   	if (session->state != ISCSI_STATE_LOGGED_IN) {
> @@ -2282,6 +2311,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
>   
>   	ISCSI_DBG_EH(session, "aborting sc %p\n", sc);
>   
> +completion_check:
>   	mutex_lock(&session->eh_mutex);
>   	spin_lock_bh(&session->frwd_lock);
>   	/*
> @@ -2321,13 +2351,20 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
>   		return SUCCESS;
>   	}
>   
> +	if (!iscsi_get_task(task)) {
> +		spin_unlock(&session->back_lock);
> +		spin_unlock_bh(&session->frwd_lock);
> +		mutex_unlock(&session->eh_mutex);
> +		/* We are just about to call iscsi_free_task so wait for it. */
> +		udelay(ISCSI_CMD_COMPL_WAIT);
> +		goto completion_check;
> +	}
> +
> +	ISCSI_DBG_EH(session, "aborting [sc %p itt 0x%x]\n", sc, task->itt);
>   	conn = session->leadconn;
>   	iscsi_get_conn(conn->cls_conn);
>   	conn->eh_abort_cnt++;
>   	age = session->age;
> -
> -	ISCSI_DBG_EH(session, "aborting [sc %p itt 0x%x]\n", sc, task->itt);
> -	__iscsi_get_task(task);
>   	spin_unlock(&session->back_lock);
>   
>   	if (task->state == ISCSI_TASK_PENDING) {
> diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c
> index 883005757ddb..defe08142b75 100644
> --- a/drivers/scsi/libiscsi_tcp.c
> +++ b/drivers/scsi/libiscsi_tcp.c
> @@ -558,7 +558,10 @@ static int iscsi_tcp_r2t_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
>   		return 0;
>   	}
>   	task->last_xfer = jiffies;
> -	__iscsi_get_task(task);
> +	if (!iscsi_get_task(task)) {
> +		/* Let the path that got the early rsp complete it */
> +		return 0;
> +	}
>   
>   	tcp_conn = conn->dd_data;
>   	rhdr = (struct iscsi_r2t_rsp *)tcp_conn->in.hdr;
> diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h
> index 522fd16f1dbb..9032a214104c 100644
> --- a/include/scsi/libiscsi.h
> +++ b/include/scsi/libiscsi.h
> @@ -470,7 +470,7 @@ extern struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *, itt_t);
>   extern void iscsi_requeue_task(struct iscsi_task *task);
>   extern void iscsi_put_task(struct iscsi_task *task);
>   extern void __iscsi_put_task(struct iscsi_task *task);
> -extern void __iscsi_get_task(struct iscsi_task *task);
> +extern bool iscsi_get_task(struct iscsi_task *task);
>   extern void iscsi_complete_scsi_task(struct iscsi_task *task,
>   				     uint32_t exp_cmdsn, uint32_t max_cmdsn);
>   

Reviewed-by: Lee Duncan <lduncan@suse.com>


  parent reply	other threads:[~2022-03-18 17:31 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-03-08  0:27 [[PATCH 00/12] misc iscsi patches Mike Christie
2022-03-08  0:27 ` [PATCH 01/12] scsi: iscsi: Merge suspend fields Mike Christie
2022-03-08 18:47   ` Lee Duncan
2022-03-08  0:27 ` [PATCH 02/12] scsi: iscsi: Rename iscsi_conn_queue_work Mike Christie
2022-03-08 18:49   ` Lee Duncan
2022-03-09  0:55   ` Wu Bo
2022-03-08  0:27 ` [PATCH 03/12] scsi: iscsi: Add recv workqueue helpers Mike Christie
2022-03-10 22:50   ` Lee Duncan
2022-03-08  0:27 ` [PATCH 04/12] scsi: iscsi: Allow a recv and xmit work to run Mike Christie
2022-03-08 19:00   ` Lee Duncan
2022-03-08  0:27 ` [PATCH 05/12] scsi: iscsi: Run recv path from workqueue Mike Christie
2022-03-18 16:45   ` Lee Duncan
2022-03-18 22:11     ` Mike Christie
2022-03-08  0:27 ` [PATCH 06/12] scsi: iscsi_tcp: Use sendpage for the PDU header Mike Christie
2022-03-12 19:42   ` Lee Duncan
2022-03-08  0:27 ` [PATCH 07/12] scsi: iscsi_tcp: Drop target_alloc use Mike Christie
2022-03-09  1:31   ` Wu Bo
2022-03-12 19:43   ` Lee Duncan
2022-03-08  0:27 ` [PATCH 08/12] scsi: iscsi: remove unneeded task state check Mike Christie
2022-03-09  1:33   ` Wu Bo
2022-03-14 17:13   ` Lee Duncan
2022-03-08  0:27 ` [PATCH 09/12] scsi: iscsi: Remove iscsi_get_task back_lock requirement Mike Christie
2022-03-14 17:44   ` Lee Duncan
2022-03-18 17:31   ` Lee Duncan [this message]
2022-03-08  0:27 ` [PATCH 10/12] scsi: iscsi: Try to avoid taking back_lock in xmit path Mike Christie
2022-03-18 17:37   ` Lee Duncan
2022-03-08  0:27 ` [PATCH 11/12] scsi: libiscsi: improve conn_send_pdu API Mike Christie
2022-03-18 16:49   ` Lee Duncan
2022-03-08  0:27 ` [PATCH 12/12] scsi: iscsi: Fix race between recovery and task xmit Mike Christie
2022-03-14 17:46   ` Lee Duncan

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=3fd9469f-4b6b-7264-2365-e9aa4e9234fd@suse.com \
    --to=lduncan@suse.com \
    --cc=cleech@redhat.com \
    --cc=jejb@linux.ibm.com \
    --cc=linux-scsi@vger.kernel.org \
    --cc=martin.petersen@oracle.com \
    --cc=michael.christie@oracle.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox