[08/19] block: Introduce request_queue.initialize_rq_fn()
diff mbox

Message ID 20170525184327.23570-9-bart.vanassche@sandisk.com
State New
Headers show

Commit Message

Bart Van Assche May 25, 2017, 6:43 p.m. UTC
Several block drivers need to initialize the driver-private data
after having called blk_get_request() and before .prep_rq_fn() is
called, e.g. when submitting a REQ_OP_SCSI_* request. Avoid that
that initialization code has to be repeated after every
blk_get_request() call by adding a new callback function to struct
request_queue.

Signed-off-by: Bart Van Assche <bart.vanassche@sandisk.com>
Reviewed-by: Hannes Reinecke <hare@suse.com>
Cc: Jens Axboe <axboe@fb.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Omar Sandoval <osandov@fb.com>
Cc: linux-block@vger.kernel.org
---
 block/blk-core.c       | 3 +++
 block/blk-mq.c         | 3 +++
 include/linux/blkdev.h | 4 ++++
 3 files changed, 10 insertions(+)

Comments

Christoph Hellwig May 26, 2017, 6:34 a.m. UTC | #1
On Thu, May 25, 2017 at 11:43:16AM -0700, Bart Van Assche wrote:
> Several block drivers need to initialize the driver-private data
> after having called blk_get_request() and before .prep_rq_fn() is
> called, e.g. when submitting a REQ_OP_SCSI_* request. Avoid that
> that initialization code has to be repeated after every
> blk_get_request() call by adding a new callback function to struct
> request_queue.

I still think we should do this at the end of blk_mq_alloc_request /
blk_old_get_request to exactly keep the old semantics and keep the
calls out of the hot path.
Bart Van Assche May 26, 2017, 11:56 p.m. UTC | #2
On Fri, 2017-05-26 at 08:34 +0200, Christoph Hellwig wrote:
> On Thu, May 25, 2017 at 11:43:16AM -0700, Bart Van Assche wrote:
> > Several block drivers need to initialize the driver-private data
> > after having called blk_get_request() and before .prep_rq_fn() is
> > called, e.g. when submitting a REQ_OP_SCSI_* request. Avoid that
> > that initialization code has to be repeated after every
> > blk_get_request() call by adding a new callback function to struct
> > request_queue.
> 
> I still think we should do this at the end of blk_mq_alloc_request /
> blk_old_get_request to exactly keep the old semantics and keep the
> calls out of the hot path.

Hello Christoph,

I have tried to move that call into blk_mq_alloc_request() but that
resulted in a kernel oops during boot due to scsi_add_cmd_to_list()
dereferencing scsi_cmnd.device and due to that pointer being invalid.
I think that pointer was invalid because moving the initialize_rq_fn()
call into blk_mq_alloc_request() caused request initialization to be
skipped for the following code path:
submit_bio()
-> generic_make_request()
  -> .make_request_fn == blk_mq_make_request()
    -> blk_mq_sched_get_request()
      -> __blk_mq_alloc_request()
        -> blk_mq_rq_ctx_init()

This is why I would like to keep the .initialize_rq_fn() call in
blk_mq_rq_ctx_init().

Bart.
Christoph Hellwig May 28, 2017, 8:34 a.m. UTC | #3
On Fri, May 26, 2017 at 11:56:30PM +0000, Bart Van Assche wrote:
> I have tried to move that call into blk_mq_alloc_request() but that
> resulted in a kernel oops during boot due to scsi_add_cmd_to_list()
> dereferencing scsi_cmnd.device and due to that pointer being invalid.
> I think that pointer was invalid because moving the initialize_rq_fn()
> call into blk_mq_alloc_request() caused request initialization to be
> skipped for the following code path:
> submit_bio()
> -> generic_make_request()
>   -> .make_request_fn == blk_mq_make_request()
>     -> blk_mq_sched_get_request()
>       -> __blk_mq_alloc_request()
>         -> blk_mq_rq_ctx_init()
> 
> This is why I would like to keep the .initialize_rq_fn() call in
> blk_mq_rq_ctx_init().

But we don't call scsi_req_init for this path either with the current
code.  So not having the call should be fine as long as you ensure
we still manually initialize everything for the non-passthrough path
in the later patches.  I'll keep an eye on that issue while reviewing
the remaining patches.
Christoph Hellwig May 28, 2017, 8:37 a.m. UTC | #4
Oh, and btw - for the mq case we don't want to use the function
pointer directly in the queue, but in blk_mq_ops, so that we have
all the mq methods in one place.
Bart Van Assche May 28, 2017, 4:12 p.m. UTC | #5
On Sun, 2017-05-28 at 10:34 +0200, hch@lst.de wrote:
> On Fri, May 26, 2017 at 11:56:30PM +0000, Bart Van Assche wrote:

> > I have tried to move that call into blk_mq_alloc_request() but that

> > resulted in a kernel oops during boot due to scsi_add_cmd_to_list()

> > dereferencing scsi_cmnd.device and due to that pointer being invalid.

> > I think that pointer was invalid because moving the initialize_rq_fn()

> > call into blk_mq_alloc_request() caused request initialization to be

> > skipped for the following code path:

> > submit_bio()

> > -> generic_make_request()

> >   -> .make_request_fn == blk_mq_make_request()

> >     -> blk_mq_sched_get_request()

> >       -> __blk_mq_alloc_request()

> >         -> blk_mq_rq_ctx_init()

> > 

> > This is why I would like to keep the .initialize_rq_fn() call in

> > blk_mq_rq_ctx_init().

> 

> But we don't call scsi_req_init for this path either with the current

> code.  So not having the call should be fine as long as you ensure

> we still manually initialize everything for the non-passthrough path

> in the later patches.  I'll keep an eye on that issue while reviewing

> the remaining patches.


Hello Christoph,

Do you see an alternative to introducing an .initialize_rq_fn() call in
blk_rq_init() / blk_mq_rq_ctx_init() to ensure that jiffies_at_alloc is
only set once and not every time a request is requeued?

Thanks,

Bart.
Bart Van Assche May 30, 2017, 5:54 p.m. UTC | #6
On Sun, 2017-05-28 at 10:37 +0200, Christoph Hellwig wrote:
> Oh, and btw - for the mq case we don't want to use the function
> pointer directly in the queue, but in blk_mq_ops, so that we have
> all the mq methods in one place.

Hello Christoph,

Are you sure of this? All other function pointers that apply to both blk-sq
and blk-mq occur in struct request, e.g. .make_request_fn(), .init_rq_fn()
and .exit_rq_fn(). If the .initialize_rq_fn() will be added to struct request
all I have to add to blk_get_request is the following:

        if (!IS_ERR(req) && q->initialize_rq_fn)
                q->initialize_rq_fn(req);

However, if for the mq case the .initialize_rq_fn() would be added to struct
blk_mq_ops then that code would look as follows:

        if (!IS_ERR(req))
                if (q->mq_ops)
                        q->mq_ops->initialize_rq_fn(req);
                else
                        q->initialize_rq_fn(req);

Personally I prefer the first alternative.

Bart.

Patch
diff mbox

diff --git a/block/blk-core.c b/block/blk-core.c
index 9416f6f495d4..fa453ed95973 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -126,6 +126,9 @@  void blk_rq_init(struct request_queue *q, struct request *rq)
 	rq->start_time = jiffies;
 	set_start_time_ns(rq);
 	rq->part = NULL;
+
+	if (q->initialize_rq_fn)
+		q->initialize_rq_fn(rq);
 }
 EXPORT_SYMBOL(blk_rq_init);
 
diff --git a/block/blk-mq.c b/block/blk-mq.c
index 9ae1d9ccf4df..a1620b36b95c 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -241,6 +241,9 @@  void blk_mq_rq_ctx_init(struct request_queue *q, struct blk_mq_ctx *ctx,
 	rq->end_io_data = NULL;
 	rq->next_rq = NULL;
 
+	if (q->initialize_rq_fn)
+		q->initialize_rq_fn(rq);
+
 	ctx->rq_dispatched[op_is_sync(op)]++;
 }
 EXPORT_SYMBOL_GPL(blk_mq_rq_ctx_init);
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 94fd2600584d..8a223a0c95d5 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -410,8 +410,12 @@  struct request_queue {
 	rq_timed_out_fn		*rq_timed_out_fn;
 	dma_drain_needed_fn	*dma_drain_needed;
 	lld_busy_fn		*lld_busy_fn;
+	/* Called just after a request is allocated */
 	init_rq_fn		*init_rq_fn;
+	/* Called just before a request is freed */
 	exit_rq_fn		*exit_rq_fn;
+	/* Called from inside blk_get_request() */
+	void (*initialize_rq_fn)(struct request *rq);
 
 	const struct blk_mq_ops	*mq_ops;