diff mbox series

[v6,03/35] scsi: Add struct for args to execution functions

Message ID 20221104231927.9613-4-michael.christie@oracle.com
State New
Headers show
Series Allow scsi_execute users to control retries | expand

Commit Message

Mike Christie Nov. 4, 2022, 11:18 p.m. UTC
This begins to move the SCSI execution functions to use a struct for
passing in args. This patch adds the new struct, converts the low level
helpers and then adds a new helper the next patches will convert the rest
of the code to.

Signed-off-by: Mike Christie <michael.christie@oracle.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
---
 drivers/scsi/scsi_lib.c    | 69 +++++++++++++++-----------------------
 include/scsi/scsi_device.h | 69 ++++++++++++++++++++++++++++++--------
 2 files changed, 82 insertions(+), 56 deletions(-)

Comments

John Garry Nov. 10, 2022, 11:15 a.m. UTC | #1
On 04/11/2022 23:18, Mike Christie wrote:
> -		 req_flags_t rq_flags, int *resid)
> +int __scsi_exec_req(const struct scsi_exec_args *args)

nit: I would expect a req to be passed based on the name, like 
blk_execute_rq()

>   {
>   	struct request *req;
>   	struct scsi_cmnd *scmd;
>   	int ret;
>   
> -	req = scsi_alloc_request(sdev->request_queue,
> -			data_direction == DMA_TO_DEVICE ?
> -			REQ_OP_DRV_OUT : REQ_OP_DRV_IN,
> -			rq_flags & RQF_PM ? BLK_MQ_REQ_PM : 0);
> +	req = scsi_alloc_request(args->sdev->request_queue,
> +				 args->data_dir == DMA_TO_DEVICE ?
> +				 REQ_OP_DRV_OUT : REQ_OP_DRV_IN,

Did you ever consider just putting the scsi_alloc_request() opf arg in 
struct scsi_exec_args (rather than data_dir), i.e. have the caller 
evaluate? We already do it in other callers to scsi_alloc_request().

Current method means a store (in scsi_exec_args struct), a load, a 
comparison, and a mov value to register whose value depends on 
comparison. That's most relevant on performance being a concern.

Thanks,
John
Bart Van Assche Nov. 10, 2022, 5:26 p.m. UTC | #2
On 11/10/22 03:15, John Garry wrote:
> Current method means a store (in scsi_exec_args struct), a load, a 
> comparison, and a mov value to register whose value depends on 
> comparison. That's most relevant on performance being a concern.

Hi John,

Is there any code that calls scsi_execute() from a code path in which 
performance matters?

Thanks,

Bart.
John Garry Nov. 10, 2022, 6:09 p.m. UTC | #3
On 10/11/2022 17:26, Bart Van Assche wrote:
> n 11/10/22 03:15, John Garry wrote:
>> Current method means a store (in scsi_exec_args struct), a load, a 
>> comparison, and a mov value to register whose value depends on 
>> comparison. That's most relevant on performance being a concern.
> 
> Hi John,
> 
> Is there any code that calls scsi_execute() from a code path in which 
> performance matters?

Eh, I don't know. Does performance matter for the touched ioctls or 
probe lun code?

Anyway, this code I mention is just the same as before. It just bugs me 
when I see code which accepts a hardcoded value (dma dir, in this case) 
and translates into another value, when the translated value could be 
just passed.

Thanks,
John
Mike Christie Nov. 10, 2022, 6:21 p.m. UTC | #4
On 11/10/22 12:09 PM, John Garry wrote:
> On 10/11/2022 17:26, Bart Van Assche wrote:
>> n 11/10/22 03:15, John Garry wrote:
>>> Current method means a store (in scsi_exec_args struct), a load, a comparison, and a mov value to register whose value depends on comparison. That's most relevant on performance being a concern.
>>
>> Hi John,
>>
>> Is there any code that calls scsi_execute() from a code path in which performance matters?
> 
> Eh, I don't know. Does performance matter for the touched ioctls or probe lun code?

No.
Mike Christie Nov. 10, 2022, 6:40 p.m. UTC | #5
On 11/10/22 5:15 AM, John Garry wrote:
> On 04/11/2022 23:18, Mike Christie wrote:
>> -         req_flags_t rq_flags, int *resid)
>> +int __scsi_exec_req(const struct scsi_exec_args *args)
> 
> nit: I would expect a req to be passed based on the name, like blk_execute_rq()
> 

We have scsi_exeucute_req now which works like scsi_exec_req. I carried it over
because it seemed nice that it reflects we are executing a request vs something
like a TMF. I don't care either way if people have a preference.


>>   {
>>       struct request *req;
>>       struct scsi_cmnd *scmd;
>>       int ret;
>>   -    req = scsi_alloc_request(sdev->request_queue,
>> -            data_direction == DMA_TO_DEVICE ?
>> -            REQ_OP_DRV_OUT : REQ_OP_DRV_IN,
>> -            rq_flags & RQF_PM ? BLK_MQ_REQ_PM : 0);
>> +    req = scsi_alloc_request(args->sdev->request_queue,
>> +                 args->data_dir == DMA_TO_DEVICE ?
>> +                 REQ_OP_DRV_OUT : REQ_OP_DRV_IN,
> 
> Did you ever consider just putting the scsi_alloc_request() opf arg in struct scsi_exec_args (rather than data_dir), i.e. have the caller evaluate? We already do it in other callers to scsi_alloc_request().

I did, but this part of the patches just convert us to the args struct
use. I tried to not change the types to keep the patchset down.

I'll change the types in another patchset, but I think it's better to
do in a separate patchset because I think we can do some more cleanup.
The users that use scsi_allocate_request are kind of a mix match mess
in that we use the scsi helper to allocate the request and scsi_cmnd,
then setup the request directly and then use the blk_execute_rq helper.
So I was thinking they use the flags directly since they are using the
block layer APIs where the scsi_execute* users are trying to use a SCSI
interface where the DMA values are also used (LLDs use DMA flags, ULDs
use a mix because they convert between block and SCSI, and scsi-ml uses
both as well).
Mike Christie Nov. 10, 2022, 7:26 p.m. UTC | #6
On 11/10/22 12:40 PM, Mike Christie wrote:
> On 11/10/22 5:15 AM, John Garry wrote:
>> On 04/11/2022 23:18, Mike Christie wrote:
>>> -         req_flags_t rq_flags, int *resid)
>>> +int __scsi_exec_req(const struct scsi_exec_args *args)
>>
>> nit: I would expect a req to be passed based on the name, like blk_execute_rq()
>>
> 
> We have scsi_exeucute_req now which works like scsi_exec_req. I carried it over
> because it seemed nice that it reflects we are executing a request vs something
> like a TMF. I don't care either way if people have a preference.
> 
> 
>>>   {
>>>       struct request *req;
>>>       struct scsi_cmnd *scmd;
>>>       int ret;
>>>   -    req = scsi_alloc_request(sdev->request_queue,
>>> -            data_direction == DMA_TO_DEVICE ?
>>> -            REQ_OP_DRV_OUT : REQ_OP_DRV_IN,
>>> -            rq_flags & RQF_PM ? BLK_MQ_REQ_PM : 0);
>>> +    req = scsi_alloc_request(args->sdev->request_queue,
>>> +                 args->data_dir == DMA_TO_DEVICE ?
>>> +                 REQ_OP_DRV_OUT : REQ_OP_DRV_IN,
>>
>> Did you ever consider just putting the scsi_alloc_request() opf arg in struct scsi_exec_args (rather than data_dir), i.e. have the caller evaluate? We already do it in other callers to scsi_alloc_request().
> 
> I did, but this part of the patches just convert us to the args struct
> use. I tried to not change the types to keep the patchset down.
> 
> I'll change the types in another patchset,

Oh wait, I could probably handle your comments in this set if we
wanted to.

scsi_execute could already take the block layer flags, so we already
are doing that type of thing so when I was thinking the scsi_execute
interface should be less block layer'y I was wrong. So, I could convert
the users to use the REQ_OP_DRV instead of DMA values when I do the args
conversion since we normally do just pass it in the directly (vs libata
where we do some extra work).

One thing that is weird to me is that scsi_execute_req is the more scsi'ish
interface. I would have thought since it took the req flag since it has the
req naming in it.

I don't care either way.
Bart Van Assche Nov. 10, 2022, 8:47 p.m. UTC | #7
On 11/10/22 11:26, Mike Christie wrote:
> One thing that is weird to me is that scsi_execute_req is the more scsi'ish
> interface. I would have thought since it took the req flag since it has the
> req naming in it.
> 
> I don't care either way.

Although this is not a strong argument, it is an argument: passing a DMA 
direction is slightly more descriptive than passing REQ_OP_DRV_* since 
the information that no data is passed would be lost if the DMA 
direction argument would be changed into a REQ_OP_DRV_* argument.

Bart.
John Garry Nov. 11, 2022, 12:07 p.m. UTC | #8
On 10/11/2022 19:26, Mike Christie wrote:
>> I'll change the types in another patchset,
> Oh wait, I could probably handle your comments in this set if we
> wanted to.

hmmm...maybe it's better to be done in another series, as you might get 
bogged down here with other comments ...

> scsi_execute could already take the block layer flags, so we already
> are doing that type of thing so when I was thinking the scsi_execute
> interface should be less block layer'y I was wrong. So, I could convert
> the users to use the REQ_OP_DRV instead of DMA values when I do the args
> conversion since we normally do just pass it in the directly (vs libata
> where we do some extra work).

Further to that, I do wonder why we even need a separate dma_dir flag at 
all.

We already pass a blk_opt_t arg in flags, so why have a separate conduit 
to set REQ_OP_DRV_{IN/OUT}? A couple of other observations:
a. why do we manually set req->cmd_flags from flags instead of passing 
flags directly to scsi_alloc_request()
b. why pass a req_flags_t instead of a blk_mq_req_flags_t - as I see, 
rq_flags arg is only ever set to RQF_PM or 0, so no need for a conversion.

> 
> One thing that is weird to me is that scsi_execute_req is the more scsi'ish
> interface. I would have thought since it took the req flag since it has the
> req naming in it.
> 
> I don't care either way.

Thanks,
John
Christoph Hellwig Nov. 15, 2022, 8:13 a.m. UTC | #9
On Fri, Nov 11, 2022 at 12:07:26PM +0000, John Garry wrote:
> We already pass a blk_opt_t arg in flags, so why have a separate conduit to 
> set REQ_OP_DRV_{IN/OUT}? A couple of other observations:
> a. why do we manually set req->cmd_flags from flags instead of passing 
> flags directly to scsi_alloc_request()
> b. why pass a req_flags_t instead of a blk_mq_req_flags_t - as I see, 
> rq_flags arg is only ever set to RQF_PM or 0, so no need for a conversion.

Mostly historic I guess.  But to me the fact that the blk_opf_t is
passed is a good argument for only passing that and not an extra
DMA direction.

rq_flags is a mess - the flags to the request allocator are different
from those at runtime, and the PM case needs both.  We'll eventually
need to clean this up in the block layer, but this is not the time.
Christoph Hellwig Nov. 15, 2022, 8:25 a.m. UTC | #10
On Fri, Nov 04, 2022 at 06:18:55PM -0500, Mike Christie wrote:
> This begins to move the SCSI execution functions to use a struct for
> passing in args. This patch adds the new struct, converts the low level
> helpers and then adds a new helper the next patches will convert the rest
> of the code to.
> 
> Signed-off-by: Mike Christie <michael.christie@oracle.com>
> Reviewed-by: Martin Wilck <mwilck@suse.com>
> Reviewed-by: Bart Van Assche <bvanassche@acm.org>
> ---
>  drivers/scsi/scsi_lib.c    | 69 +++++++++++++++-----------------------
>  include/scsi/scsi_device.h | 69 ++++++++++++++++++++++++++++++--------
>  2 files changed, 82 insertions(+), 56 deletions(-)
> 
> diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
> index fc1560981a03..f832befb50b0 100644
> --- a/drivers/scsi/scsi_lib.c
> +++ b/drivers/scsi/scsi_lib.c
> @@ -185,55 +185,39 @@ void scsi_queue_insert(struct scsi_cmnd *cmd, int reason)
>  	__scsi_queue_insert(cmd, reason, true);
>  }
>  
> -
>  /**
> - * __scsi_execute - insert request and wait for the result
> - * @sdev:	scsi device
> - * @cmd:	scsi command
> - * @data_direction: data direction
> - * @buffer:	data buffer
> - * @bufflen:	len of buffer
> - * @sense:	optional sense buffer
> - * @sshdr:	optional decoded sense header
> - * @timeout:	request timeout in HZ
> - * @retries:	number of times to retry request
> - * @flags:	flags for ->cmd_flags
> - * @rq_flags:	flags for ->rq_flags
> - * @resid:	optional residual length
> + * __scsi_exec_req - insert request and wait for the result
> + * @scsi_exec_args: See struct definition for description of each field
>   *
>   * Returns the scsi_cmnd result field if a command was executed, or a negative
>   * Linux error code if we didn't get that far.
>   */
> -int __scsi_execute(struct scsi_device *sdev, const unsigned char *cmd,
> -		 int data_direction, void *buffer, unsigned bufflen,
> -		 unsigned char *sense, struct scsi_sense_hdr *sshdr,
> -		 int timeout, int retries, blk_opf_t flags,
> -		 req_flags_t rq_flags, int *resid)
> +int __scsi_exec_req(const struct scsi_exec_args *args)

I find the struct for everyhing a somewhat confusing calling convention.
So I'd pass the required arguments directly, and stuff all the optional
bits into the struct.  Based on the previous discussion maybe
something like:

int __scsi_execute_cmd(struct scsi_device *sdev, const unsigned char *cmd,
		blk_opf_t opf, void *buffer, unsigned int bufflen,
		int timeout, int retries,
		const struct scsi_exec_args *args)

which would be a nice replacement for all the existing scsi_execute*
interfaces.
diff mbox series

Patch

diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index fc1560981a03..f832befb50b0 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -185,55 +185,39 @@  void scsi_queue_insert(struct scsi_cmnd *cmd, int reason)
 	__scsi_queue_insert(cmd, reason, true);
 }
 
-
 /**
- * __scsi_execute - insert request and wait for the result
- * @sdev:	scsi device
- * @cmd:	scsi command
- * @data_direction: data direction
- * @buffer:	data buffer
- * @bufflen:	len of buffer
- * @sense:	optional sense buffer
- * @sshdr:	optional decoded sense header
- * @timeout:	request timeout in HZ
- * @retries:	number of times to retry request
- * @flags:	flags for ->cmd_flags
- * @rq_flags:	flags for ->rq_flags
- * @resid:	optional residual length
+ * __scsi_exec_req - insert request and wait for the result
+ * @scsi_exec_args: See struct definition for description of each field
  *
  * Returns the scsi_cmnd result field if a command was executed, or a negative
  * Linux error code if we didn't get that far.
  */
-int __scsi_execute(struct scsi_device *sdev, const unsigned char *cmd,
-		 int data_direction, void *buffer, unsigned bufflen,
-		 unsigned char *sense, struct scsi_sense_hdr *sshdr,
-		 int timeout, int retries, blk_opf_t flags,
-		 req_flags_t rq_flags, int *resid)
+int __scsi_exec_req(const struct scsi_exec_args *args)
 {
 	struct request *req;
 	struct scsi_cmnd *scmd;
 	int ret;
 
-	req = scsi_alloc_request(sdev->request_queue,
-			data_direction == DMA_TO_DEVICE ?
-			REQ_OP_DRV_OUT : REQ_OP_DRV_IN,
-			rq_flags & RQF_PM ? BLK_MQ_REQ_PM : 0);
+	req = scsi_alloc_request(args->sdev->request_queue,
+				 args->data_dir == DMA_TO_DEVICE ?
+				 REQ_OP_DRV_OUT : REQ_OP_DRV_IN,
+				 args->req_flags & RQF_PM ? BLK_MQ_REQ_PM : 0);
 	if (IS_ERR(req))
 		return PTR_ERR(req);
 
-	if (bufflen) {
-		ret = blk_rq_map_kern(sdev->request_queue, req,
-				      buffer, bufflen, GFP_NOIO);
+	if (args->buf) {
+		ret = blk_rq_map_kern(args->sdev->request_queue, req, args->buf,
+				      args->buf_len, GFP_NOIO);
 		if (ret)
 			goto out;
 	}
 	scmd = blk_mq_rq_to_pdu(req);
-	scmd->cmd_len = COMMAND_SIZE(cmd[0]);
-	memcpy(scmd->cmnd, cmd, scmd->cmd_len);
-	scmd->allowed = retries;
-	req->timeout = timeout;
-	req->cmd_flags |= flags;
-	req->rq_flags |= rq_flags | RQF_QUIET;
+	scmd->cmd_len = COMMAND_SIZE(args->cmd[0]);
+	memcpy(scmd->cmnd, args->cmd, scmd->cmd_len);
+	scmd->allowed = args->retries;
+	req->timeout = args->timeout;
+	req->cmd_flags |= args->op_flags;
+	req->rq_flags |= args->req_flags | RQF_QUIET;
 
 	/*
 	 * head injection *required* here otherwise quiesce won't work
@@ -246,23 +230,24 @@  int __scsi_execute(struct scsi_device *sdev, const unsigned char *cmd,
 	 * is invalid.  Prevent the garbage from being misinterpreted
 	 * and prevent security leaks by zeroing out the excess data.
 	 */
-	if (unlikely(scmd->resid_len > 0 && scmd->resid_len <= bufflen))
-		memset(buffer + bufflen - scmd->resid_len, 0, scmd->resid_len);
-
-	if (resid)
-		*resid = scmd->resid_len;
-	if (sense && scmd->sense_len)
-		memcpy(sense, scmd->sense_buffer, SCSI_SENSE_BUFFERSIZE);
-	if (sshdr)
+	if (unlikely(scmd->resid_len > 0 && scmd->resid_len <= args->buf_len))
+		memset(args->buf + args->buf_len - scmd->resid_len, 0,
+		       scmd->resid_len);
+
+	if (args->resid)
+		*args->resid = scmd->resid_len;
+	if (args->sense && scmd->sense_len)
+		memcpy(args->sense, scmd->sense_buffer, SCSI_SENSE_BUFFERSIZE);
+	if (args->sshdr)
 		scsi_normalize_sense(scmd->sense_buffer, scmd->sense_len,
-				     sshdr);
+				     args->sshdr);
 	ret = scmd->result;
  out:
 	blk_mq_free_request(req);
 
 	return ret;
 }
-EXPORT_SYMBOL(__scsi_execute);
+EXPORT_SYMBOL(__scsi_exec_req);
 
 /*
  * Wake up the error handler if necessary. Avoid as follows that the error
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index 24bdbf7999ab..cd3957b80acd 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -454,28 +454,69 @@  extern const char *scsi_device_state_name(enum scsi_device_state);
 extern int scsi_is_sdev_device(const struct device *);
 extern int scsi_is_target_device(const struct device *);
 extern void scsi_sanitize_inquiry_string(unsigned char *s, int len);
-extern int __scsi_execute(struct scsi_device *sdev, const unsigned char *cmd,
-			int data_direction, void *buffer, unsigned bufflen,
-			unsigned char *sense, struct scsi_sense_hdr *sshdr,
-			int timeout, int retries, blk_opf_t flags,
-			req_flags_t rq_flags, int *resid);
+
+struct scsi_exec_args {
+	struct scsi_device *sdev;	/* scsi device */
+	const unsigned char *cmd;	/* scsi command */
+	int data_dir;			/* DMA direction */
+	void *buf;			/* data buffer */
+	unsigned int buf_len;		/* len of buffer */
+	unsigned char *sense;		/* optional sense buffer */
+	unsigned int sense_len;		/* optional sense buffer len */
+	struct scsi_sense_hdr *sshdr;	/* optional decoded sense header */
+	int timeout;			/* request timeout in HZ */
+	int retries;			/* number of times to retry request */
+	blk_opf_t op_flags;		/* flags for ->cmd_flags */
+	req_flags_t req_flags;		/* flags for ->rq_flags */
+	int *resid;			/* optional residual length */
+};
+
+extern int __scsi_exec_req(const struct scsi_exec_args *args);
+/* Make sure any sense buffer is the correct size. */
+#define scsi_exec_req(args)						\
+({									\
+	BUILD_BUG_ON(args.sense &&					\
+		     args.sense_len != SCSI_SENSE_BUFFERSIZE);		\
+	__scsi_exec_req(&args);					\
+})
+
 /* Make sure any sense buffer is the correct size. */
-#define scsi_execute(sdev, cmd, data_direction, buffer, bufflen, sense,	\
-		     sshdr, timeout, retries, flags, rq_flags, resid)	\
+#define scsi_execute(_sdev, _cmd, _data_dir, _buffer, _bufflen, _sense,	\
+		     _sshdr, _timeout, _retries, _flags, _rq_flags,	\
+		     _resid)						\
 ({									\
-	BUILD_BUG_ON((sense) != NULL &&					\
-		     sizeof(sense) != SCSI_SENSE_BUFFERSIZE);		\
-	__scsi_execute(sdev, cmd, data_direction, buffer, bufflen,	\
-		       sense, sshdr, timeout, retries, flags, rq_flags,	\
-		       resid);						\
+	BUILD_BUG_ON((_sense) != NULL &&				\
+		     sizeof(_sense) != SCSI_SENSE_BUFFERSIZE);		\
+	__scsi_exec_req(&((struct scsi_exec_args) {			\
+				.sdev = _sdev,				\
+				.cmd = _cmd,				\
+				.data_dir = _data_dir,			\
+				.buf = _buffer,				\
+				.buf_len = _bufflen,			\
+				.sense = _sense,			\
+				.sshdr = _sshdr,			\
+				.timeout = _timeout,			\
+				.retries = _retries,			\
+				.op_flags = _flags,			\
+				.req_flags = _rq_flags,			\
+				.resid = _resid, }));			\
 })
+
 static inline int scsi_execute_req(struct scsi_device *sdev,
 	const unsigned char *cmd, int data_direction, void *buffer,
 	unsigned bufflen, struct scsi_sense_hdr *sshdr, int timeout,
 	int retries, int *resid)
 {
-	return scsi_execute(sdev, cmd, data_direction, buffer,
-		bufflen, NULL, sshdr, timeout, retries,  0, 0, resid);
+	return __scsi_exec_req(&(struct scsi_exec_args) {
+					.sdev = sdev,
+					.cmd = cmd,
+					.data_dir = data_direction,
+					.buf = buffer,
+					.buf_len = bufflen,
+					.sshdr = sshdr,
+					.timeout = timeout,
+					.retries = retries,
+					.resid = resid });
 }
 extern void sdev_disable_disk_events(struct scsi_device *sdev);
 extern void sdev_enable_disk_events(struct scsi_device *sdev);