Skip to content

Commit

Permalink
block: free sched's request pool in blk_cleanup_queue
Browse files Browse the repository at this point in the history
In theory, IO scheduler belongs to request queue, and the request pool
of sched tags belongs to the request queue too.

However, the current tags allocation interfaces are re-used for both
driver tags and sched tags, and driver tags is definitely host wide,
and doesn't belong to any request queue, same with its request pool.
So we need tagset instance for freeing request of sched tags.

Meantime, blk_mq_free_tag_set() often follows blk_cleanup_queue() in case
of non-BLK_MQ_F_TAG_SHARED, this way requires that request pool of sched
tags to be freed before calling blk_mq_free_tag_set().

Commit 47cdee2 ("block: move blk_exit_queue into __blk_release_queue")
moves blk_exit_queue into __blk_release_queue for simplying the fast
path in generic_make_request(), then causes oops during freeing requests
of sched tags in __blk_release_queue().

Fix the above issue by move freeing request pool of sched tags into
blk_cleanup_queue(), this way is safe becasue queue has been frozen and no any
in-queue requests at that time. Freeing sched tags has to be kept in queue's
release handler becasue there might be un-completed dispatch activity
which might refer to sched tags.

Cc: Bart Van Assche <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Fixes: 47cdee2 ("block: move blk_exit_queue into __blk_release_queue")
Tested-by: Yi Zhang <[email protected]>
Reported-by: kernel test robot <[email protected]>
Signed-off-by: Ming Lei <[email protected]>
Signed-off-by: Jens Axboe <[email protected]>
  • Loading branch information
Ming Lei authored and axboe committed Jun 7, 2019
1 parent cf1db7f commit c3e2219
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 6 deletions.
13 changes: 13 additions & 0 deletions block/blk-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,19 @@ void blk_cleanup_queue(struct request_queue *q)
if (queue_is_mq(q))
blk_mq_exit_queue(q);

/*
* In theory, request pool of sched_tags belongs to request queue.
* However, the current implementation requires tag_set for freeing
* requests, so free the pool now.
*
* Queue has become frozen, there can't be any in-queue requests, so
* it is safe to free requests now.
*/
mutex_lock(&q->sysfs_lock);
if (q->elevator)
blk_mq_sched_free_requests(q);
mutex_unlock(&q->sysfs_lock);

percpu_ref_exit(&q->q_usage_counter);

/* @q is and will stay empty, shutdown and put */
Expand Down
30 changes: 27 additions & 3 deletions block/blk-mq-sched.c
Original file line number Diff line number Diff line change
Expand Up @@ -475,14 +475,18 @@ static int blk_mq_sched_alloc_tags(struct request_queue *q,
return ret;
}

/* called in queue's release handler, tagset has gone away */
static void blk_mq_sched_tags_teardown(struct request_queue *q)
{
struct blk_mq_tag_set *set = q->tag_set;
struct blk_mq_hw_ctx *hctx;
int i;

queue_for_each_hw_ctx(q, hctx, i)
blk_mq_sched_free_tags(set, hctx, i);
queue_for_each_hw_ctx(q, hctx, i) {
if (hctx->sched_tags) {
blk_mq_free_rq_map(hctx->sched_tags);
hctx->sched_tags = NULL;
}
}
}

int blk_mq_init_sched(struct request_queue *q, struct elevator_type *e)
Expand Down Expand Up @@ -523,6 +527,7 @@ int blk_mq_init_sched(struct request_queue *q, struct elevator_type *e)
ret = e->ops.init_hctx(hctx, i);
if (ret) {
eq = q->elevator;
blk_mq_sched_free_requests(q);
blk_mq_exit_sched(q, eq);
kobject_put(&eq->kobj);
return ret;
Expand All @@ -534,11 +539,30 @@ int blk_mq_init_sched(struct request_queue *q, struct elevator_type *e)
return 0;

err:
blk_mq_sched_free_requests(q);
blk_mq_sched_tags_teardown(q);
q->elevator = NULL;
return ret;
}

/*
* called in either blk_queue_cleanup or elevator_switch, tagset
* is required for freeing requests
*/
void blk_mq_sched_free_requests(struct request_queue *q)
{
struct blk_mq_hw_ctx *hctx;
int i;

lockdep_assert_held(&q->sysfs_lock);
WARN_ON(!q->elevator);

queue_for_each_hw_ctx(q, hctx, i) {
if (hctx->sched_tags)
blk_mq_free_rqs(q->tag_set, hctx->sched_tags, i);
}
}

void blk_mq_exit_sched(struct request_queue *q, struct elevator_queue *e)
{
struct blk_mq_hw_ctx *hctx;
Expand Down
1 change: 1 addition & 0 deletions block/blk-mq-sched.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ void blk_mq_sched_dispatch_requests(struct blk_mq_hw_ctx *hctx);

int blk_mq_init_sched(struct request_queue *q, struct elevator_type *e);
void blk_mq_exit_sched(struct request_queue *q, struct elevator_queue *e);
void blk_mq_sched_free_requests(struct request_queue *q);

static inline bool
blk_mq_sched_bio_merge(struct request_queue *q, struct bio *bio)
Expand Down
2 changes: 1 addition & 1 deletion block/blk-sysfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -850,7 +850,7 @@ static void blk_exit_queue(struct request_queue *q)
*/
if (q->elevator) {
ioc_clear_queue(q);
elevator_exit(q, q->elevator);
__elevator_exit(q, q->elevator);
q->elevator = NULL;
}

Expand Down
10 changes: 9 additions & 1 deletion block/blk.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <linux/blk-mq.h>
#include <xen/xen.h>
#include "blk-mq.h"
#include "blk-mq-sched.h"

/* Max future timer expiry for timeouts */
#define BLK_MAX_TIMEOUT (5 * HZ)
Expand Down Expand Up @@ -176,10 +177,17 @@ void blk_insert_flush(struct request *rq);
int elevator_init_mq(struct request_queue *q);
int elevator_switch_mq(struct request_queue *q,
struct elevator_type *new_e);
void elevator_exit(struct request_queue *, struct elevator_queue *);
void __elevator_exit(struct request_queue *, struct elevator_queue *);
int elv_register_queue(struct request_queue *q);
void elv_unregister_queue(struct request_queue *q);

static inline void elevator_exit(struct request_queue *q,
struct elevator_queue *e)
{
blk_mq_sched_free_requests(q);
__elevator_exit(q, e);
}

struct hd_struct *__disk_get_part(struct gendisk *disk, int partno);

#ifdef CONFIG_FAIL_IO_TIMEOUT
Expand Down
2 changes: 1 addition & 1 deletion block/elevator.c
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ static void elevator_release(struct kobject *kobj)
kfree(e);
}

void elevator_exit(struct request_queue *q, struct elevator_queue *e)
void __elevator_exit(struct request_queue *q, struct elevator_queue *e)
{
mutex_lock(&e->sysfs_lock);
if (e->type->ops.exit_sched)
Expand Down

0 comments on commit c3e2219

Please sign in to comment.