blk-mq: improve bio merge for none scheduler
Subsystems (2)
v5 cover blk-mq: improve bio merge for none scheduler
Hi,
Patch 1 ~ 2 uses q->queue_depth as hint for setting up scheduler queue depth.
Patch 3 ~ 8 improve bio merge via hash table in sw queue, which makes bio merge more efficient than current approch in which only the last 8 requests in sw queue are checked.
Also this way has been used in block legacy path for long time, and blk-mq scheduler uses hash table to do bio merge too.
V5:
- splitted from previous patchset of 'blk-mq-sched: improve SCSI-MQ performance V4'
Ming Lei (8):
blk-mq-sched: introduce blk_mq_sched_queue_depth()
blk-mq-sched: use q->queue_depth as hint for q->nr_requests
block: introduce rqhash helpers
block: move actual bio merge code into __elv_merge
block: add check on elevator for supporting bio merge via hashtable
from blk-mq sw queue
block: introduce .last_merge and .hash to blk_mq_ctx
blk-mq-sched: refactor blk_mq_sched_try_merge()
blk-mq: improve bio merge from blk-mq sw queue
block/blk-mq-sched.c | 75 +++++++++++++++--------------------------- block/blk-mq-sched.h | 23 +++++++++++++ block/blk-mq.c | 55 ++++++++++++++++++++++++++++--- block/blk-mq.h | 5 +++ block/blk-settings.c | 2 ++ block/blk.h | 55 +++++++++++++++++++++++++++++++ block/elevator.c | 93 +++++++++++++++++++++++++++++++--------------------- 7 files changed, 216 insertions(+), 92 deletions(-)
Signature · 1 line
2.9.5
PATCH v5 1/8 blk-mq-sched: introduce blk_mq_sched_queue_depth()
The following patch will use one hint to figure out default queue depth for scheduler queue, so introduce the helper of blk_mq_sched_queue_depth() for this purpose.
--- block/blk-mq-sched.c | 8 +------- block/blk-mq-sched.h | 11 +++++++++++ 2 files changed, 12 insertions(+), 7 deletions(-)
block/blk-mq-sched.c +1 -7
@@ -588,13 +588,7 @@ int blk_mq_init_sched(struct request_queue *q, struct elevator_type *e)return 0 ; } -/* -* Default to double of smaller one between hw queue_depth and 128, -* since we don't split into sync/async like the old code did. -* Additionally, this is a per-hw queue depth. -*/ - q->nr_requests = 2 * min_t(unsigned int, q->tag_set->queue_depth, -BLKDEV_MAX_RQ ); + q->nr_requests =blk_mq_sched_queue_depth (q);queue_for_each_hw_ctx (q, hctx, i) { ret =blk_mq_sched_alloc_tags (q, hctx, i);
block/blk-mq-sched.h +11 -0
@@ -96,4 +96,15 @@ static inline bool blk_mq_sched_needs_restart(struct blk_mq_hw_ctx *hctx)return test_bit (BLK_MQ_S_SCHED_RESTART , &hctx->state); } +static inline unsigned blk_mq_sched_queue_depth (struct request_queue *q) +{ +/* +* Default to double of smaller one between hw queue_depth and 128, +* since we don't split into sync/async like the old code did. +* Additionally, this is a per-hw queue depth. +*/ + return 2 * min_t(unsigned int, q->tag_set->queue_depth, +BLKDEV_MAX_RQ ); +} +#endif
Signature · 1 line
2.9.5
PATCH v5 2/8 blk-mq-sched: use q->queue_depth as hint for q->nr_requests
In reply to Ming LeiSCSI sets q->queue_depth from shost->cmd_per_lun, and q->queue_depth is per request_queue and more related to scheduler queue compared with hw queue depth, which can be shared by queues, such as TAG_SHARED.
This patch tries to use q->queue_depth as hint for computing q->nr_requests, which should be more effective than current way.
--- block/blk-mq-sched.h | 18 +++++++++++++++--- block/blk-mq.c | 27 +++++++++++++++++++++++++-- block/blk-mq.h | 1 + block/blk-settings.c | 2 ++ 4 files changed, 43 insertions(+), 5 deletions(-)
block/blk-mq-sched.h +15 -3
@@ -99,12 +99,24 @@ static inline bool blk_mq_sched_needs_restart(struct blk_mq_hw_ctx *hctx)static inline unsigned blk_mq_sched_queue_depth (struct request_queue *q) {/* -* Default to double of smaller one between hw queue_depth and 128, +* q->queue_depth is more close to scheduler queue, so use it +* as hint for computing scheduler queue depth if it is valid +*/ +unsigned q_depth = q->queue_depth ?: q->tag_set->queue_depth; + +/* +* Default to double of smaller one between queue depth and 128, * since we don't split into sync/async like the old code did. * Additionally, this is a per-hw queue depth. */ - return 2 * min_t(unsigned int, q->tag_set->queue_depth, -BLKDEV_MAX_RQ ); + q_depth = 2 * min_t(unsigned int, q_depth, BLKDEV_MAX_RQ); + +/* +* when queue depth of driver is too small, we set queue depth +* of scheduler queue as 128 which is the default setting of +* block legacy code. +*/ +return max_t (unsigned, q_depth,BLKDEV_MAX_RQ ); }#endif
block/blk-mq.c +25 -2
@@ -2694,7 +2694,9 @@ void blk_mq_free_tag_set(struct blk_mq_tag_set *set) }EXPORT_SYMBOL (blk_mq_free_tag_set); -int blk_mq_update_nr_requests (struct request_queue *q,unsigned int nr) +static int __blk_mq_update_nr_requests (struct request_queue *q, +bool sched_only, +unsigned int nr) {struct blk_mq_tag_set *set = q->tag_set;struct blk_mq_hw_ctx *hctx;
@@ -2713,7 +2715,7 @@ int blk_mq_update_nr_requests(struct request_queue *q, unsigned int nr)* If we're using an MQ scheduler, just update the scheduler * queue depth. This is similar to what the old code would do. */ -if (!hctx->sched_tags) { +if (!sched_only && !hctx->sched_tags) { ret =blk_mq_tag_update_depth (hctx, &hctx->tags,min (nr, set->queue_depth), false);
@@ -2733,6 +2735,27 @@ int blk_mq_update_nr_requests(struct request_queue *q, unsigned int nr)return ret; } +int blk_mq_update_nr_requests (struct request_queue *q,unsigned int nr) +{ +return __blk_mq_update_nr_requests (q, false, nr); +} + +/* +* When drivers update q->queue_depth, this API is called so that +* we can use this queue depth as hint for adjusting scheduler +* queue depth. +*/ +int blk_mq_update_sched_queue_depth (struct request_queue *q) +{ +unsigned nr; + +if (!q->mq_ops || !q->elevator) +return 0 ; + + nr =blk_mq_sched_queue_depth (q); +return __blk_mq_update_nr_requests (q, true, nr); +} +static void __blk_mq_update_nr_hw_queues (struct blk_mq_tag_set *set,int nr_hw_queues) {
block/blk-mq.h +1 -0
@@ -37,6 +37,7 @@ bool blk_mq_get_driver_tag(struct request *rq, struct blk_mq_hw_ctx **hctx, bool wait);struct request *blk_mq_dequeue_from_ctx (struct blk_mq_hw_ctx *hctx,struct blk_mq_ctx *start); +int blk_mq_update_sched_queue_depth (struct request_queue *q);/* * Internal helpers for allocating/freeing the request map
block/blk-settings.c +2 -0
@@ -878,6 +878,8 @@ void blk_set_queue_depth(struct request_queue *q, unsigned int depth) { q->queue_depth = depth;wbt_set_queue_depth (q->rq_wb, depth); + +WARN_ON (blk_mq_update_sched_queue_depth (q)); }EXPORT_SYMBOL (blk_set_queue_depth);
Signature · 1 line
2.9.5
PATCH v5 3/8 block: introduce rqhash helpers
In reply to Ming LeiWe need this helpers for supporting to use hashtable to improve bio merge from sw queue in the following patches.
No functional change.
--- block/blk.h | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ block/elevator.c | 36 +++++++----------------------------- 2 files changed, 59 insertions(+), 29 deletions(-)
block/blk.h +52 -0
@@ -146,6 +146,58 @@ static inline void blk_clear_rq_complete(struct request *rq)*/ #define ELV_ON_HASH (rq) ((rq)->rq_flags & RQF_HASHED) +/* +* Merge hash stuff. +*/ +#define rq_hash_key (rq) (blk_rq_pos(rq) + blk_rq_sectors(rq)) + +#define bucket (head, key) &((head)[hash_min((key), ELV_HASH_BITS)]) + +static inline void __rqhash_del (struct request *rq) +{ +hash_del (&rq->hash); + rq->rq_flags &= ~RQF_HASHED ; +} + +static inline void rqhash_del (struct request *rq) +{ +if (ELV_ON_HASH (rq)) +__rqhash_del (rq); +} + +static inline void rqhash_add (struct hlist_head *hash,struct request *rq) +{ +BUG_ON (ELV_ON_HASH (rq)); +hlist_add_head (&rq->hash,bucket (hash,rq_hash_key (rq))); + rq->rq_flags |=RQF_HASHED ; +} + +static inline void rqhash_reposition (struct hlist_head *hash,struct request *rq) +{ +__rqhash_del (rq); +rqhash_add (hash, rq); +} + +static inline struct request *rqhash_find (struct hlist_head *hash,sector_t offset) +{ +struct hlist_node *next; +struct request *rq =NULL ; + +hlist_for_each_entry_safe (rq, next,bucket (hash, offset), hash) { +BUG_ON (!ELV_ON_HASH (rq)); + +if (unlikely (!rq_mergeable (rq))) { +__rqhash_del (rq); +continue ; + } + +if (rq_hash_key (rq) == offset) +return rq; + } + +return NULL ; +} +void blk_insert_flush (struct request *rq);static inline struct request *__elv_next_request (struct request_queue *q)
block/elevator.c +7 -29
@@ -47,11 +47,6 @@ static DEFINE_SPINLOCK(elv_list_lock);static LIST_HEAD (elv_list);/* -* Merge hash stuff. -*/ -#define rq_hash_key (rq) (blk_rq_pos(rq) + blk_rq_sectors(rq)) - -/* * Query io scheduler to see if the current process issuing bio may be * merged with rq. */
@@ -268,14 +263,12 @@ EXPORT_SYMBOL(elevator_exit);static inline void __elv_rqhash_del (struct request *rq) { -hash_del (&rq->hash); - rq->rq_flags &= ~RQF_HASHED ; +__rqhash_del (rq); }void elv_rqhash_del (struct request_queue *q,struct request *rq) { -if (ELV_ON_HASH (rq)) -__elv_rqhash_del (rq); +rqhash_del (rq); }EXPORT_SYMBOL_GPL (elv_rqhash_del);
@@ -283,37 +276,22 @@ void elv_rqhash_add(struct request_queue *q, struct request *rq) {struct elevator_queue *e = q->elevator; -BUG_ON (ELV_ON_HASH (rq)); -hash_add (e->hash, &rq->hash,rq_hash_key (rq)); - rq->rq_flags |=RQF_HASHED ; +rqhash_add (e->hash, rq); }EXPORT_SYMBOL_GPL (elv_rqhash_add);void elv_rqhash_reposition (struct request_queue *q,struct request *rq) { -__elv_rqhash_del (rq); -elv_rqhash_add (q, rq); +struct elevator_queue *e = q->elevator; + +rqhash_reposition (e->hash, rq); }struct request *elv_rqhash_find (struct request_queue *q,sector_t offset) {struct elevator_queue *e = q->elevator; -struct hlist_node *next; -struct request *rq; - -hash_for_each_possible_safe (e->hash, rq, next, hash, offset) { -BUG_ON (!ELV_ON_HASH (rq)); -if (unlikely (!rq_mergeable (rq))) { -__elv_rqhash_del (rq); -continue ; - } - -if (rq_hash_key (rq) == offset) -return rq; - } - -return NULL ; +return rqhash_find (e->hash, offset); }/*
Signature · 1 line
2.9.5
Re: [PATCH V5 3/8] block: introduce rqhash helpers
Can yu get rid of the elv_rqhash in favour of these new ones instead of having that tinty indirection?
Also the new helper should have some prefix - either keep elv_ or add blk_.
PATCH v5 4/8 block: move actual bio merge code into __elv_merge
In reply to Ming LeiSo that we can reuse __elv_merge() to merge bio into requests from sw queue in the following patches.
No functional change.
--- block/elevator.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-)
block/elevator.c +13 -6
@@ -409,8 +409,9 @@ void elv_dispatch_add_tail(struct request_queue *q, struct request *rq) }EXPORT_SYMBOL (elv_dispatch_add_tail); -enum elv_merge elv_merge (struct request_queue *q,struct request **req, -struct bio *bio) +static enum elv_merge __elv_merge (struct request_queue *q, +struct request **req,struct bio *bio, +struct hlist_head *hash,struct request *last_merge) {struct elevator_queue *e = q->elevator;struct request *__rq;
@@ -427,11 +428,11 @@ enum elv_merge elv_merge(struct request_queue *q, struct request **req,/* * First try one-hit cache. */ -if (q->last_merge &&elv_bio_merge_ok (q->last_merge, bio)) { -enum elv_merge ret =blk_try_merge (q->last_merge, bio); +if (last_merge &&elv_bio_merge_ok (last_merge, bio)) { +enum elv_merge ret =blk_try_merge (last_merge, bio);if (ret !=ELEVATOR_NO_MERGE ) { - *req = q->last_merge; + *req = last_merge;return ret; } }
@@ -442,7 +443,7 @@ enum elv_merge elv_merge(struct request_queue *q, struct request **req,/* * See if our hash lookup can find a potential backmerge. */ - __rq =elv_rqhash_find (q, bio->bi_iter.bi_sector); + __rq =rqhash_find (hash, bio->bi_iter.bi_sector);if (__rq &&elv_bio_merge_ok (__rq, bio)) { *req = __rq;return ELEVATOR_BACK_MERGE ;
@@ -456,6 +457,12 @@ enum elv_merge elv_merge(struct request_queue *q, struct request **req,return ELEVATOR_NO_MERGE ; } +enum elv_merge elv_merge (struct request_queue *q,struct request **req, +struct bio *bio) +{ +return __elv_merge (q, req, bio, q->elevator->hash, q->last_merge); +} +/* * Attempt to do an insertion back merge. Only check for the case where * we can append 'rq' to an existing request, so we can throw 'rq' away
Signature · 1 line
2.9.5
Re: [PATCH V5 4/8] block: move actual bio merge code into __elv_merge
Quoted 4 lines · Ming Lei
So that we can reuse
__elv_merge()to merge bio into requests from sw queue in the following patches.No functional change.
There are very few callers of elv_merge, just update the function with the new parameters instead of introducing __elv_merge.
PATCH v5 5/8 block: add check on elevator for supporting bio merge via hashtable from blk-mq sw queue
In reply to Ming Leiblk_mq_sched_try_merge() will be reused in following patches to support bio merge to blk-mq sw queue, so add checkes to related functions which are called from blk_mq_sched_try_merge().
--- block/elevator.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
block/elevator.c +16 -0
@@ -71,6 +71,10 @@ bool elv_bio_merge_ok(struct request *rq, struct bio *bio)if (!blk_rq_merge_ok (rq, bio))return false; +/* We need to support to merge bio from sw queue */ +if (!rq->q->elevator) +return true; +if (!elv_iosched_allow_bio_merge (rq, bio))return false;
@@ -449,6 +453,10 @@ static enum elv_merge __elv_merge(struct request_queue *q,return ELEVATOR_BACK_MERGE ; } +/* no elevator when merging bio to blk-mq sw queue */ +if (!e) +return ELEVATOR_NO_MERGE ; +if (e->uses_mq && e->type->ops.mq.request_merge)return e->type->ops.mq.request_merge (q, req, bio);else if (!e->uses_mq && e->type->ops.sq.elevator_merge_fn)
@@ -711,6 +719,10 @@ struct request *elv_latter_request(struct request_queue *q, struct request *rq) {struct elevator_queue *e = q->elevator; +/* no elevator when merging bio to blk-mq sw queue */ +if (!e) +return NULL ; +if (e->uses_mq && e->type->ops.mq.next_request)return e->type->ops.mq.next_request (q, rq);else if (!e->uses_mq && e->type->ops.sq.elevator_latter_req_fn)
@@ -723,6 +735,10 @@ struct request *elv_former_request(struct request_queue *q, struct request *rq) {struct elevator_queue *e = q->elevator; +/* no elevator when merging bio to blk-mq sw queue */ +if (!e) +return NULL ; +if (e->uses_mq && e->type->ops.mq.former_request)return e->type->ops.mq.former_request (q, rq);if (!e->uses_mq && e->type->ops.sq.elevator_former_req_fn)
Signature · 1 line
2.9.5
Re: [PATCH V5 5/8] block: add check on elevator for supporting bio merge via hashtable from blk-mq sw queue
Quoted 3 lines · Ming Lei
blk_mq_sched_try_merge()will be reused in following patches to support bio merge to blk-mq sw queue, so add checkes to related functions which are called fromblk_mq_sched_try_merge().
The changes themselves look ok, but don't make sense on their own, I think this needs to be merged with the code tor actually introduce the bio merge.
PATCH v5 6/8 block: introduce .last_merge and .hash to blk_mq_ctx
In reply to Ming LeiPrepare for supporting bio merge to sw queue if no blk-mq io scheduler is taken.
--- block/blk-mq.h | 4 ++++ block/blk.h | 3 +++ block/elevator.c | 22 +++++++++++++++++++--- 3 files changed, 26 insertions(+), 3 deletions(-)
block/blk-mq.h +4 -0
@@ -18,6 +18,10 @@ struct blk_mq_ctx {unsigned long rq_dispatched[2 ];unsigned long rq_merged; +/* bio merge via request hash table */ +struct request *last_merge; +DECLARE_HASHTABLE (hash,ELV_HASH_BITS ); +/* incremented at completion time */ unsigned long ____cacheline_aligned_in_smp rq_completed[2 ];
block/blk.h +3 -0
@@ -198,6 +198,9 @@ static inline struct request *rqhash_find(struct hlist_head *hash, sector_t offsreturn NULL ; } +enum elv_merge elv_merge_ctx (struct request_queue *q,struct request **req, +struct bio *bio,struct blk_mq_ctx *ctx); +void blk_insert_flush (struct request *rq);static inline struct request *__elv_next_request (struct request_queue *q)
block/elevator.c +19 -3
@@ -471,6 +471,13 @@ enum elv_merge elv_merge(struct request_queue *q, struct request **req,return __elv_merge (q, req, bio, q->elevator->hash, q->last_merge); } +enum elv_merge elv_merge_ctx (struct request_queue *q,struct request **req, +struct bio *bio,struct blk_mq_ctx *ctx) +{ +WARN_ON_ONCE (!q->mq_ops); +return __elv_merge (q, req, bio, ctx->hash, ctx->last_merge); +} +/* * Attempt to do an insertion back merge. Only check for the case where * we can append 'rq' to an existing request, so we can throw 'rq' away
@@ -516,16 +523,25 @@ void elv_merged_request(struct request_queue *q, struct request *rq, enum elv_merge type) {struct elevator_queue *e = q->elevator; +struct hlist_head *hash = e->hash; + +/* we do bio merge on blk-mq sw queue */ +if (q->mq_ops && !e) { + rq->mq_ctx->last_merge = rq; + hash = rq->mq_ctx->hash; + goto reposition; + } + + q->last_merge = rq;if (e->uses_mq && e->type->ops.mq.request_merged) e->type->ops.mq.request_merged (q, rq, type);else if (!e->uses_mq && e->type->ops.sq.elevator_merged_fn) e->type->ops.sq.elevator_merged_fn (q, rq, type); + reposition:if (type ==ELEVATOR_BACK_MERGE ) -elv_rqhash_reposition (q, rq); - - q->last_merge = rq; +rqhash_reposition (hash, rq); } void elv_merge_requests(struct request_queue *q, struct request *rq,
Signature · 1 line
2.9.5
Re: [PATCH V5 6/8] block: introduce .last_merge and .hash to blk_mq_ctx
block/elevator.c enum elv_merge elv_merge(struct request_queue *q, struct request **req, · line 479
Quoted 6 lines
block/elevator.c +6
-0+enum elv_merge elv_merge_ctx (struct request_queue *q,struct request **req, +struct bio *bio,struct blk_mq_ctx *ctx) +{ +WARN_ON_ONCE (!q->mq_ops); +return __elv_merge (q, req, bio, ctx->hash, ctx->last_merge); +}
This function only has a single caller, just open code it.
PATCH v5 7/8 blk-mq-sched: refactor blk_mq_sched_try_merge()
In reply to Ming LeiThis patch introduces one function __blk_mq_try_merge() which will be resued for bio merge to sw queue in the following patch.
No functional change.
--- block/blk-mq-sched.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-)
block/blk-mq-sched.c +13 -5
@@ -222,12 +222,11 @@ void blk_mq_sched_dispatch_requests(struct blk_mq_hw_ctx *hctx) } } -bool blk_mq_sched_try_merge (struct request_queue *q,struct bio *bio, -struct request **merged_request) +static bool __blk_mq_try_merge (struct request_queue *q, +struct bio *bio,struct request **merged_request, +struct request *rq,enum elv_merge type) { -struct request *rq; - -switch (elv_merge (q, &rq, bio)) { +switch (type) {case ELEVATOR_BACK_MERGE :if (!blk_mq_sched_allow_merge (q, rq, bio))return false;
@@ -250,6 +249,15 @@ bool blk_mq_sched_try_merge(struct request_queue *q, struct bio *bio,return false; } } + +bool blk_mq_sched_try_merge (struct request_queue *q,struct bio *bio, +struct request **merged_request) +{ +struct request *rq; +enum elv_merge type =elv_merge (q, &rq, bio); + +return __blk_mq_try_merge (q, bio, merged_request, rq, type); +}EXPORT_SYMBOL_GPL (blk_mq_sched_try_merge);/*
Signature · 1 line
2.9.5
Re: [PATCH V5 7/8] blk-mq-sched: refactor blk_mq_sched_try_merge()
block/blk-mq-sched.c bool blk_mq_sched_try_merge(struct request_queue *q, struct bio *bio, · line 261
Quoted 9 lines
block/blk-mq-sched.c +8
-0+bool blk_mq_sched_try_merge (struct request_queue *q,struct bio *bio, +struct request **merged_request) +{ +struct request *rq; +enum elv_merge type =elv_merge (q, &rq, bio); + +return __blk_mq_try_merge (q, bio, merged_request, rq, type); +}EXPORT_SYMBOL_GPL (blk_mq_sched_try_merge);
This only has two callers, just open code it and rename __blk_mq_try_merge to blk_mq_try_merge.
PATCH v5 8/8 blk-mq: improve bio merge from blk-mq sw queue
In reply to Ming LeiThis patch uses hash table to do bio merge from sw queue, then we can align to blk-mq scheduler/block legacy's way for bio merge.
Turns out bio merge via hash table is more efficient than simple merge on the last 8 requests in sw queue. On SCSI SRP, it is observed ~10% IOPS is increased in sequential IO test with this patch.
It is also one step forward to real 'none' scheduler, in which way the blk-mq scheduler framework can be more clean.
--- block/blk-mq-sched.c | 49 ++++++++++++------------------------------------- block/blk-mq.c | 28 +++++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 40 deletions(-)
block/blk-mq-sched.c +12 -37
@@ -260,50 +260,25 @@ bool blk_mq_sched_try_merge(struct request_queue *q, struct bio *bio, }EXPORT_SYMBOL_GPL (blk_mq_sched_try_merge); -/* -* Reverse check our software queue for entries that we could potentially -* merge with. Currently includes a hand-wavy stop count of 8, to not spend -* too much time checking for merges. -*/ -static bool blk_mq_attempt_merge (struct request_queue *q, +static bool blk_mq_ctx_try_merge (struct request_queue *q,struct blk_mq_ctx *ctx,struct bio *bio) { -struct request *rq; -int checked =8 ; +struct request *rq, *free =NULL ; +enum elv_merge type; +bool merged;lockdep_assert_held (&ctx->lock); -list_for_each_entry_reverse (rq, &ctx->rq_list, queuelist) { -bool merged = false; - -if (!checked--) -break ; - -if (!blk_rq_merge_ok (rq, bio)) -continue ; + type =elv_merge_ctx (q, &rq, bio, ctx); + merged =__blk_mq_try_merge (q, bio, &free, rq, type); -switch (blk_try_merge (rq, bio)) { -case ELEVATOR_BACK_MERGE : -if (blk_mq_sched_allow_merge (q, rq, bio)) - merged =bio_attempt_back_merge (q, rq, bio); -break ; -case ELEVATOR_FRONT_MERGE : -if (blk_mq_sched_allow_merge (q, rq, bio)) - merged =bio_attempt_front_merge (q, rq, bio); -break ; -case ELEVATOR_DISCARD_MERGE : - merged =bio_attempt_discard_merge (q, rq, bio); -break ; -default : -continue ; - } +if (free) +blk_mq_free_request (free); -if (merged) - ctx->rq_merged++; -return merged; - } +if (merged) + ctx->rq_merged++; -return false; +return merged; }bool __blk_mq_sched_bio_merge (struct request_queue *q,struct bio *bio)
@@ -321,7 +296,7 @@ bool __blk_mq_sched_bio_merge(struct request_queue *q, struct bio *bio)if (hctx->flags &BLK_MQ_F_SHOULD_MERGE ) {/* default per sw-queue merge */ spin_lock (&ctx->lock); - ret =blk_mq_attempt_merge (q, ctx, bio); + ret =blk_mq_ctx_try_merge (q, ctx, bio);spin_unlock (&ctx->lock); }
block/blk-mq.c +25 -3
@@ -849,6 +849,18 @@ static void blk_mq_timeout_work(struct work_struct *work)blk_queue_exit (q); } +static void blk_mq_ctx_remove_rq_list (struct blk_mq_ctx *ctx, +struct list_head *head) +{ +struct request *rq; + +lockdep_assert_held (&ctx->lock); + +list_for_each_entry (rq, head, queuelist) +rqhash_del (rq); + ctx->last_merge =NULL ; +} +struct flush_busy_ctx_data {struct blk_mq_hw_ctx *hctx;struct list_head *list;
@@ -863,6 +875,7 @@ static bool flush_busy_ctx(struct sbitmap *sb, unsigned int bitnr, void *data)sbitmap_clear_bit (sb, bitnr);spin_lock (&ctx->lock);list_splice_tail_init (&ctx->rq_list, flush_data->list); +blk_mq_ctx_remove_rq_list (ctx, flush_data->list);spin_unlock (&ctx->lock);return true; }
@@ -892,17 +905,23 @@ static bool dispatch_rq_from_ctx(struct sbitmap *sb, unsigned int bitnr, void *dstruct dispatch_rq_data *dispatch_data = data;struct blk_mq_hw_ctx *hctx = dispatch_data->hctx;struct blk_mq_ctx *ctx = hctx->ctxs[bitnr]; +struct request *rq =NULL ;spin_lock (&ctx->lock);if (unlikely (!list_empty (&ctx->rq_list))) { - dispatch_data->rq =list_entry_rq (ctx->rq_list.next); -list_del_init (&dispatch_data->rq->queuelist); + rq =list_entry_rq (ctx->rq_list.next); +list_del_init (&rq->queuelist); +rqhash_del (rq);if (list_empty (&ctx->rq_list))sbitmap_clear_bit (sb, bitnr); } +if (ctx->last_merge == rq) + ctx->last_merge =NULL ;spin_unlock (&ctx->lock); -return !dispatch_data->rq; + dispatch_data->rq = rq; + +return !rq; } struct request *blk_mq_dequeue_from_ctx(struct blk_mq_hw_ctx *hctx,
@@ -1433,6 +1452,8 @@ static inline void __blk_mq_insert_req_list(struct blk_mq_hw_ctx *hctx,list_add (&rq->queuelist, &ctx->rq_list); else list_add_tail(&rq->queuelist, &ctx->rq_list); + +rqhash_add (ctx->hash, rq); } void __blk_mq_insert_request(struct blk_mq_hw_ctx *hctx, struct request *rq,
@@ -1968,6 +1989,7 @@ static int blk_mq_hctx_notify_dead(unsigned int cpu, struct hlist_node *node)spin_lock (&ctx->lock);if (!list_empty (&ctx->rq_list)) {list_splice_init (&ctx->rq_list, &tmp); +blk_mq_ctx_remove_rq_list (ctx, &tmp);blk_mq_hctx_clear_pending (hctx, ctx); }spin_unlock (&ctx->lock);
Signature · 1 line
2.9.5
Re: [PATCH V5 8/8] blk-mq: improve bio merge from blk-mq sw queue
This looks generally good to me, but I really worry about the impact on very high iops devices. Did you try this e.g. for random reads from unallocated blocks on an enterprise NVMe SSD?
Re: [PATCH V5 8/8] blk-mq: improve bio merge from blk-mq sw queue
Quoted 3 lines · Christoph Hellwig
This looks generally good to me, but I really worry about the impact on very high iops devices. Did you try this e.g. for random reads from unallocated blocks on an enterprise NVMe SSD?
Looks no such impact, please see the following data
in the fio test(libaio, direct, bs=4k, 64jobs, randread, none scheduler)
[root@storageqe-62 results]# ../parse_fio 4.14.0-rc2.no_blk_mq_perf+-nvme-64jobs-mq-none.log 4.14.0-rc2.BLK_MQ_PERF_V5+-nvme-64jobs-mq-none.log
---------------------------------------
IOPS(K) | NONE | NONE
---------------------------------------
randread | 650.98 | 653.15
---------------------------------------
OR:
If you worry about this impact, can we simply disable merge on NVMe for none scheduler? It is basically impossible to merge NVMe's request/bio when none is used, but it can be doable in case of kyber scheduler.
Signature · 1 line
Ming