summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJens Axboe <axboe@kernel.dk>2021-10-09 13:10:39 -0600
committerJens Axboe <axboe@kernel.dk>2021-10-18 06:17:35 -0600
commit349302da83529539040d2516de1deec1e09f491c (patch)
tree3d8049f24e9c7f2c32ef7ae88322d6828d50af59
parent9672b0d43782047b1825a96bafee1b6aefa35bc2 (diff)
block: improve batched tag allocation
Add a blk_mq_get_tags() helper, which uses the new sbitmap API for allocating a batch of tags all at once. This both simplifies the block code for batched allocation, and it is also more efficient than just doing repeated calls into __sbitmap_queue_get(). This reduces the sbitmap overhead in peak runs from ~3% to ~1% and yields a performanc increase from 6.6M IOPS to 6.8M IOPS for a single CPU core. Signed-off-by: Jens Axboe <axboe@kernel.dk>
-rw-r--r--block/blk-mq-tag.c15
-rw-r--r--block/blk-mq-tag.h2
-rw-r--r--block/blk-mq.c87
3 files changed, 73 insertions, 31 deletions
diff --git a/block/blk-mq-tag.c b/block/blk-mq-tag.c
index 72a2724a4eee..c43b97201161 100644
--- a/block/blk-mq-tag.c
+++ b/block/blk-mq-tag.c
@@ -86,6 +86,21 @@ static int __blk_mq_get_tag(struct blk_mq_alloc_data *data,
return __sbitmap_queue_get(bt);
}
+unsigned long blk_mq_get_tags(struct blk_mq_alloc_data *data, int nr_tags,
+ unsigned int *offset)
+{
+ struct blk_mq_tags *tags = blk_mq_tags_from_data(data);
+ struct sbitmap_queue *bt = &tags->bitmap_tags;
+ unsigned long ret;
+
+ if (data->shallow_depth ||data->flags & BLK_MQ_REQ_RESERVED ||
+ data->hctx->flags & BLK_MQ_F_TAG_QUEUE_SHARED)
+ return 0;
+ ret = __sbitmap_queue_get_batch(bt, nr_tags, offset);
+ *offset += tags->nr_reserved_tags;
+ return ret;
+}
+
unsigned int blk_mq_get_tag(struct blk_mq_alloc_data *data)
{
struct blk_mq_tags *tags = blk_mq_tags_from_data(data);
diff --git a/block/blk-mq-tag.h b/block/blk-mq-tag.h
index d8ce89fa1686..71c2f7d8e9b7 100644
--- a/block/blk-mq-tag.h
+++ b/block/blk-mq-tag.h
@@ -38,6 +38,8 @@ extern int blk_mq_init_bitmaps(struct sbitmap_queue *bitmap_tags,
int node, int alloc_policy);
extern unsigned int blk_mq_get_tag(struct blk_mq_alloc_data *data);
+unsigned long blk_mq_get_tags(struct blk_mq_alloc_data *data, int nr_tags,
+ unsigned int *offset);
extern void blk_mq_put_tag(struct blk_mq_tags *tags, struct blk_mq_ctx *ctx,
unsigned int tag);
extern int blk_mq_tag_update_depth(struct blk_mq_hw_ctx *hctx,
diff --git a/block/blk-mq.c b/block/blk-mq.c
index 568773de9a7f..97b911866de1 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -354,6 +354,38 @@ static struct request *blk_mq_rq_ctx_init(struct blk_mq_alloc_data *data,
return rq;
}
+static inline struct request *
+__blk_mq_alloc_requests_batch(struct blk_mq_alloc_data *data,
+ u64 alloc_time_ns)
+{
+ unsigned int tag, tag_offset;
+ struct request *rq;
+ unsigned long tags;
+ int i, nr = 0;
+
+ tags = blk_mq_get_tags(data, data->nr_tags, &tag_offset);
+ if (unlikely(!tags))
+ return NULL;
+
+ for (i = 0; tags; i++) {
+ if (!(tags & (1UL << i)))
+ continue;
+ tag = tag_offset + i;
+ tags &= ~(1UL << i);
+ rq = blk_mq_rq_ctx_init(data, tag, alloc_time_ns);
+ rq->rq_next = *data->cached_rq;
+ *data->cached_rq = rq;
+ }
+ data->nr_tags -= nr;
+
+ if (!data->cached_rq)
+ return NULL;
+
+ rq = *data->cached_rq;
+ *data->cached_rq = rq->rq_next;
+ return rq;
+}
+
static struct request *__blk_mq_alloc_requests(struct blk_mq_alloc_data *data)
{
struct request_queue *q = data->q;
@@ -389,42 +421,35 @@ retry:
blk_mq_tag_busy(data->hctx);
/*
+ * Try batched alloc if we want more than 1 tag.
+ */
+ if (data->nr_tags > 1) {
+ rq = __blk_mq_alloc_requests_batch(data, alloc_time_ns);
+ if (rq)
+ return rq;
+ data->nr_tags = 1;
+ }
+
+ /*
* Waiting allocations only fail because of an inactive hctx. In that
* case just retry the hctx assignment and tag allocation as CPU hotplug
* should have migrated us to an online CPU by now.
*/
- do {
- tag = blk_mq_get_tag(data);
- if (tag == BLK_MQ_NO_TAG) {
- if (data->flags & BLK_MQ_REQ_NOWAIT)
- break;
- /*
- * Give up the CPU and sleep for a random short time to
- * ensure that thread using a realtime scheduling class
- * are migrated off the CPU, and thus off the hctx that
- * is going away.
- */
- msleep(3);
- goto retry;
- }
-
- rq = blk_mq_rq_ctx_init(data, tag, alloc_time_ns);
- if (!--data->nr_tags || e ||
- (data->hctx->flags & BLK_MQ_F_TAG_QUEUE_SHARED))
- return rq;
-
- /* link into the cached list */
- rq->rq_next = *data->cached_rq;
- *data->cached_rq = rq;
- data->flags |= BLK_MQ_REQ_NOWAIT;
- } while (1);
-
- if (!data->cached_rq)
- return NULL;
+ tag = blk_mq_get_tag(data);
+ if (tag == BLK_MQ_NO_TAG) {
+ if (data->flags & BLK_MQ_REQ_NOWAIT)
+ return NULL;
+ /*
+ * Give up the CPU and sleep for a random short time to
+ * ensure that thread using a realtime scheduling class
+ * are migrated off the CPU, and thus off the hctx that
+ * is going away.
+ */
+ msleep(3);
+ goto retry;
+ }
- rq = *data->cached_rq;
- *data->cached_rq = rq->rq_next;
- return rq;
+ return blk_mq_rq_ctx_init(data, tag, alloc_time_ns);
}
struct request *blk_mq_alloc_request(struct request_queue *q, unsigned int op,