Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1485499 > unrolled thread
| Started by | Omar Sandoval <osandov@osandov.com> |
|---|---|
| First post | 2016-09-17 10:30 +0200 |
| Last post | 2016-09-17 10:40 +0200 |
| Articles | 5 — 1 participant |
Back to article view | Back to linux.kernel
[PATCH v4 0/6] blk-mq: generalization and bug fixes for tag allocation Omar Sandoval <osandov@osandov.com> - 2016-09-17 10:30 +0200
[PATCH v4 2/6] sbitmap: allocate wait queues on a specific node Omar Sandoval <osandov@osandov.com> - 2016-09-17 10:30 +0200
[PATCH v4 4/6] sbitmap: push alloc policy into sbitmap_queue Omar Sandoval <osandov@osandov.com> - 2016-09-17 10:30 +0200
[PATCH v4 6/6] sbitmap: re-initialize allocation hints after resize Omar Sandoval <osandov@osandov.com> - 2016-09-17 10:30 +0200
[PATCH v4 3/6] sbitmap: push per-cpu last_tag into sbitmap_queue Omar Sandoval <osandov@osandov.com> - 2016-09-17 10:40 +0200
| From | Omar Sandoval <osandov@osandov.com> |
|---|---|
| Date | 2016-09-17 10:30 +0200 |
| Subject | [PATCH v4 0/6] blk-mq: generalization and bug fixes for tag allocation |
| Message-ID | <sijax-b3-3@gated-at.bofh.it> |
From: Omar Sandoval <osandov@fb.com> This is v4 of the patch series to turn blk-mq's scalable tag allocation into a generic library, fixing a few bugs along the way. This is both cleanup and preparation for coming blk-mq work. v1 is here [1], v2 is here [2], v3 is here [3]. Changes in v4: - Added patch 6 to fix a bug in blk-mq where shrinking the tag set doesn't stop allocations from happening to high tags. - Changed the interface for raw `sbitmap_get()` to pass the allocation hint as a value, not a pointer. This pushes the hint updating out into `sbitmap_queue_get()` where it makes more sense. Changes in v3: - Renamed scale_bitmap to sbitmap Changes in v2: - Return -EINVAL instead of BUG_ON() if an invalid shift is passed to the initialization functions. - Rename last_cache to alloc_hint. - Split the wait queue allocation change into a separate patch. Patch 1 should now be a no-op. - Add patches 3 and 4 to make the API cleaner by pushing some context from the blk-mq data structures into the common scale_bitmap code. - Add patch 5 to randomize the allocation hint on initialization like Applies to v4.8-rc6. Thanks! 1: http://marc.info/?l=linux-block&m=147251402805405&w=2 2: http://marc.info/?l=linux-block&m=147329198222482&w=2 3: http://marc.info/?l=linux-block&m=147344663202718&w=2 Omar Sandoval (6): blk-mq: abstract tag allocation out into sbitmap library sbitmap: allocate wait queues on a specific node sbitmap: push per-cpu last_tag into sbitmap_queue sbitmap: push alloc policy into sbitmap_queue sbitmap: randomize initial alloc_hint values sbitmap: re-initialize allocation hints after resize MAINTAINERS | 1 + block/Kconfig | 1 + block/blk-mq-tag.c | 503 +++++++++++------------------------------------- block/blk-mq-tag.h | 42 ++-- block/blk-mq.c | 114 ++++------- block/blk-mq.h | 11 -- include/linux/blk-mq.h | 9 +- include/linux/sbitmap.h | 373 +++++++++++++++++++++++++++++++++++ lib/Kconfig | 3 + lib/Makefile | 2 + lib/sbitmap.c | 347 +++++++++++++++++++++++++++++++++ 11 files changed, 889 insertions(+), 517 deletions(-) create mode 100644 include/linux/sbitmap.h create mode 100644 lib/sbitmap.c -- 2.9.3
[toc] | [next] | [standalone]
| From | Omar Sandoval <osandov@osandov.com> |
|---|---|
| Date | 2016-09-17 10:30 +0200 |
| Subject | [PATCH v4 2/6] sbitmap: allocate wait queues on a specific node |
| Message-ID | <sijay-b3-11@gated-at.bofh.it> |
| In reply to | #1485499 |
From: Omar Sandoval <osandov@fb.com>
The original bt_alloc() we converted from was using kzalloc(), not
kzalloc_node(), to allocate the wait queues. This was probably an
oversight, so fix it for sbitmap_queue_init_node().
Signed-off-by: Omar Sandoval <osandov@fb.com>
---
lib/sbitmap.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/sbitmap.c b/lib/sbitmap.c
index dfc084a..4d8e97e 100644
--- a/lib/sbitmap.c
+++ b/lib/sbitmap.c
@@ -208,7 +208,7 @@ int sbitmap_queue_init_node(struct sbitmap_queue *sbq, unsigned int depth,
sbq->wake_batch = sbq_calc_wake_batch(depth);
atomic_set(&sbq->wake_index, 0);
- sbq->ws = kzalloc(SBQ_WAIT_QUEUES * sizeof(*sbq->ws), flags);
+ sbq->ws = kzalloc_node(SBQ_WAIT_QUEUES * sizeof(*sbq->ws), flags, node);
if (!sbq->ws) {
sbitmap_free(&sbq->sb);
return -ENOMEM;
--
2.9.3
[toc] | [prev] | [next] | [standalone]
| From | Omar Sandoval <osandov@osandov.com> |
|---|---|
| Date | 2016-09-17 10:30 +0200 |
| Subject | [PATCH v4 4/6] sbitmap: push alloc policy into sbitmap_queue |
| Message-ID | <sijay-b3-19@gated-at.bofh.it> |
| In reply to | #1485499 |
From: Omar Sandoval <osandov@fb.com>
Again, there's no point in passing this in every time. Make it part of
struct sbitmap_queue and clean up the API.
Signed-off-by: Omar Sandoval <osandov@fb.com>
---
block/blk-mq-tag.c | 33 +++++++++++++++------------------
block/blk-mq-tag.h | 1 -
include/linux/sbitmap.h | 19 +++++++++++--------
lib/sbitmap.c | 14 ++++++++------
4 files changed, 34 insertions(+), 33 deletions(-)
diff --git a/block/blk-mq-tag.c b/block/blk-mq-tag.c
index c9a22db..e1c2bed 100644
--- a/block/blk-mq-tag.c
+++ b/block/blk-mq-tag.c
@@ -91,14 +91,11 @@ static inline bool hctx_may_queue(struct blk_mq_hw_ctx *hctx,
return atomic_read(&hctx->nr_active) < depth;
}
-#define BT_ALLOC_RR(tags) (tags->alloc_policy == BLK_TAG_ALLOC_RR)
-
-static int __bt_get(struct blk_mq_hw_ctx *hctx, struct sbitmap_queue *bt,
- struct blk_mq_tags *tags)
+static int __bt_get(struct blk_mq_hw_ctx *hctx, struct sbitmap_queue *bt)
{
if (!hctx_may_queue(hctx, bt))
return -1;
- return __sbitmap_queue_get(bt, BT_ALLOC_RR(tags));
+ return __sbitmap_queue_get(bt);
}
static int bt_get(struct blk_mq_alloc_data *data, struct sbitmap_queue *bt,
@@ -108,7 +105,7 @@ static int bt_get(struct blk_mq_alloc_data *data, struct sbitmap_queue *bt,
DEFINE_WAIT(wait);
int tag;
- tag = __bt_get(hctx, bt, tags);
+ tag = __bt_get(hctx, bt);
if (tag != -1)
return tag;
@@ -119,7 +116,7 @@ static int bt_get(struct blk_mq_alloc_data *data, struct sbitmap_queue *bt,
do {
prepare_to_wait(&ws->wait, &wait, TASK_UNINTERRUPTIBLE);
- tag = __bt_get(hctx, bt, tags);
+ tag = __bt_get(hctx, bt);
if (tag != -1)
break;
@@ -136,7 +133,7 @@ static int bt_get(struct blk_mq_alloc_data *data, struct sbitmap_queue *bt,
* Retry tag allocation after running the hardware queue,
* as running the queue may also have found completions.
*/
- tag = __bt_get(hctx, bt, tags);
+ tag = __bt_get(hctx, bt);
if (tag != -1)
break;
@@ -206,12 +203,10 @@ void blk_mq_put_tag(struct blk_mq_hw_ctx *hctx, struct blk_mq_ctx *ctx,
const int real_tag = tag - tags->nr_reserved_tags;
BUG_ON(real_tag >= tags->nr_tags);
- sbitmap_queue_clear(&tags->bitmap_tags, real_tag,
- BT_ALLOC_RR(tags), ctx->cpu);
+ sbitmap_queue_clear(&tags->bitmap_tags, real_tag, ctx->cpu);
} else {
BUG_ON(tag >= tags->nr_reserved_tags);
- sbitmap_queue_clear(&tags->breserved_tags, tag,
- BT_ALLOC_RR(tags), ctx->cpu);
+ sbitmap_queue_clear(&tags->breserved_tags, tag, ctx->cpu);
}
}
@@ -363,21 +358,23 @@ static unsigned int bt_unused_tags(const struct sbitmap_queue *bt)
return bt->sb.depth - sbitmap_weight(&bt->sb);
}
-static int bt_alloc(struct sbitmap_queue *bt, unsigned int depth, int node)
+static int bt_alloc(struct sbitmap_queue *bt, unsigned int depth,
+ bool round_robin, int node)
{
- return sbitmap_queue_init_node(bt, depth, -1, GFP_KERNEL, node);
+ return sbitmap_queue_init_node(bt, depth, -1, round_robin, GFP_KERNEL,
+ node);
}
static struct blk_mq_tags *blk_mq_init_bitmap_tags(struct blk_mq_tags *tags,
int node, int alloc_policy)
{
unsigned int depth = tags->nr_tags - tags->nr_reserved_tags;
+ bool round_robin = alloc_policy == BLK_TAG_ALLOC_RR;
- tags->alloc_policy = alloc_policy;
-
- if (bt_alloc(&tags->bitmap_tags, depth, node))
+ if (bt_alloc(&tags->bitmap_tags, depth, round_robin, node))
goto free_tags;
- if (bt_alloc(&tags->breserved_tags, tags->nr_reserved_tags, node))
+ if (bt_alloc(&tags->breserved_tags, tags->nr_reserved_tags, round_robin,
+ node))
goto free_bitmap_tags;
return tags;
diff --git a/block/blk-mq-tag.h b/block/blk-mq-tag.h
index 2b1d52e..f90b850 100644
--- a/block/blk-mq-tag.h
+++ b/block/blk-mq-tag.h
@@ -18,7 +18,6 @@ struct blk_mq_tags {
struct request **rqs;
struct list_head page_list;
- int alloc_policy;
cpumask_var_t cpumask;
};
diff --git a/include/linux/sbitmap.h b/include/linux/sbitmap.h
index 6745545..f017fd6 100644
--- a/include/linux/sbitmap.h
+++ b/include/linux/sbitmap.h
@@ -122,6 +122,11 @@ struct sbitmap_queue {
* @ws: Wait queues.
*/
struct sbq_wait_state *ws;
+
+ /**
+ * @round_robin: Allocate bits in strict round-robin order.
+ */
+ bool round_robin;
};
/**
@@ -259,13 +264,14 @@ unsigned int sbitmap_weight(const struct sbitmap *sb);
* @sbq: Bitmap queue to initialize.
* @depth: See sbitmap_init_node().
* @shift: See sbitmap_init_node().
+ * @round_robin: See sbitmap_get().
* @flags: Allocation flags.
* @node: Memory node to allocate on.
*
* Return: Zero on success or negative errno on failure.
*/
int sbitmap_queue_init_node(struct sbitmap_queue *sbq, unsigned int depth,
- int shift, gfp_t flags, int node);
+ int shift, bool round_robin, gfp_t flags, int node);
/**
* sbitmap_queue_free() - Free memory used by a &struct sbitmap_queue.
@@ -294,29 +300,27 @@ void sbitmap_queue_resize(struct sbitmap_queue *sbq, unsigned int depth);
* __sbitmap_queue_get() - Try to allocate a free bit from a &struct
* sbitmap_queue with preemption already disabled.
* @sbq: Bitmap queue to allocate from.
- * @round_robin: See sbitmap_get().
*
* Return: Non-negative allocated bit number if successful, -1 otherwise.
*/
-int __sbitmap_queue_get(struct sbitmap_queue *sbq, bool round_robin);
+int __sbitmap_queue_get(struct sbitmap_queue *sbq);
/**
* sbitmap_queue_get() - Try to allocate a free bit from a &struct
* sbitmap_queue.
* @sbq: Bitmap queue to allocate from.
- * @round_robin: See sbitmap_get().
* @cpu: Output parameter; will contain the CPU we ran on (e.g., to be passed to
* sbitmap_queue_clear()).
*
* Return: Non-negative allocated bit number if successful, -1 otherwise.
*/
-static inline int sbitmap_queue_get(struct sbitmap_queue *sbq, bool round_robin,
+static inline int sbitmap_queue_get(struct sbitmap_queue *sbq,
unsigned int *cpu)
{
int nr;
*cpu = get_cpu();
- nr = __sbitmap_queue_get(sbq, round_robin);
+ nr = __sbitmap_queue_get(sbq);
put_cpu();
return nr;
}
@@ -326,11 +330,10 @@ static inline int sbitmap_queue_get(struct sbitmap_queue *sbq, bool round_robin,
* &struct sbitmap_queue.
* @sbq: Bitmap to free from.
* @nr: Bit number to free.
- * @round_robin: See sbitmap_get().
* @cpu: CPU the bit was allocated on.
*/
void sbitmap_queue_clear(struct sbitmap_queue *sbq, unsigned int nr,
- bool round_robin, unsigned int cpu);
+ unsigned int cpu);
static inline int sbq_index_inc(int index)
{
diff --git a/lib/sbitmap.c b/lib/sbitmap.c
index 1651ad9d..be55f74 100644
--- a/lib/sbitmap.c
+++ b/lib/sbitmap.c
@@ -196,7 +196,7 @@ static unsigned int sbq_calc_wake_batch(unsigned int depth)
}
int sbitmap_queue_init_node(struct sbitmap_queue *sbq, unsigned int depth,
- int shift, gfp_t flags, int node)
+ int shift, bool round_robin, gfp_t flags, int node)
{
int ret;
int i;
@@ -225,6 +225,8 @@ int sbitmap_queue_init_node(struct sbitmap_queue *sbq, unsigned int depth,
init_waitqueue_head(&sbq->ws[i].wait);
atomic_set(&sbq->ws[i].wait_cnt, sbq->wake_batch);
}
+
+ sbq->round_robin = round_robin;
return 0;
}
EXPORT_SYMBOL_GPL(sbitmap_queue_init_node);
@@ -236,18 +238,18 @@ void sbitmap_queue_resize(struct sbitmap_queue *sbq, unsigned int depth)
}
EXPORT_SYMBOL_GPL(sbitmap_queue_resize);
-int __sbitmap_queue_get(struct sbitmap_queue *sbq, bool round_robin)
+int __sbitmap_queue_get(struct sbitmap_queue *sbq)
{
unsigned int hint;
int nr;
hint = this_cpu_read(*sbq->alloc_hint);
- nr = sbitmap_get(&sbq->sb, hint, round_robin);
+ nr = sbitmap_get(&sbq->sb, hint, sbq->round_robin);
if (nr == -1) {
/* If the map is full, a hint won't do us much good. */
this_cpu_write(*sbq->alloc_hint, 0);
- } else if (nr == hint || unlikely(round_robin)) {
+ } else if (nr == hint || unlikely(sbq->round_robin)) {
/* Only update the hint if we used it. */
hint = nr + 1;
if (hint >= sbq->sb.depth - 1)
@@ -304,11 +306,11 @@ static void sbq_wake_up(struct sbitmap_queue *sbq)
}
void sbitmap_queue_clear(struct sbitmap_queue *sbq, unsigned int nr,
- bool round_robin, unsigned int cpu)
+ unsigned int cpu)
{
sbitmap_clear_bit(&sbq->sb, nr);
sbq_wake_up(sbq);
- if (likely(!round_robin))
+ if (likely(!sbq->round_robin))
*per_cpu_ptr(sbq->alloc_hint, cpu) = nr;
}
EXPORT_SYMBOL_GPL(sbitmap_queue_clear);
--
2.9.3
[toc] | [prev] | [next] | [standalone]
| From | Omar Sandoval <osandov@osandov.com> |
|---|---|
| Date | 2016-09-17 10:30 +0200 |
| Subject | [PATCH v4 6/6] sbitmap: re-initialize allocation hints after resize |
| Message-ID | <sijay-b3-23@gated-at.bofh.it> |
| In reply to | #1485499 |
From: Omar Sandoval <osandov@fb.com>
After a struct sbitmap_queue is resized smaller, the allocation hints
may still be set to bits beyond the new depth of the bitmap. This means
that, for example, if the number of blk-mq tags is reduced through
sysfs, more requests than the nominal queue depth may be in flight.
It's tempting to fix this at resize time by doing a one-time
reinitialization of the hints, but this can race with
__sbitmap_queue_get() updating the hint. Instead, check the hint before
we use it. This caused no measurable performance difference in my
synthetic benchmarks.
Signed-off-by: Omar Sandoval <osandov@fb.com>
---
lib/sbitmap.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/lib/sbitmap.c b/lib/sbitmap.c
index 928b82a..f736c52 100644
--- a/lib/sbitmap.c
+++ b/lib/sbitmap.c
@@ -246,10 +246,15 @@ EXPORT_SYMBOL_GPL(sbitmap_queue_resize);
int __sbitmap_queue_get(struct sbitmap_queue *sbq)
{
- unsigned int hint;
+ unsigned int hint, depth;
int nr;
hint = this_cpu_read(*sbq->alloc_hint);
+ depth = READ_ONCE(sbq->sb.depth);
+ if (unlikely(hint >= depth)) {
+ hint = depth ? prandom_u32() % depth : 0;
+ this_cpu_write(*sbq->alloc_hint, hint);
+ }
nr = sbitmap_get(&sbq->sb, hint, sbq->round_robin);
if (nr == -1) {
@@ -258,7 +263,7 @@ int __sbitmap_queue_get(struct sbitmap_queue *sbq)
} else if (nr == hint || unlikely(sbq->round_robin)) {
/* Only update the hint if we used it. */
hint = nr + 1;
- if (hint >= sbq->sb.depth - 1)
+ if (hint >= depth - 1)
hint = 0;
this_cpu_write(*sbq->alloc_hint, hint);
}
--
2.9.3
[toc] | [prev] | [next] | [standalone]
| From | Omar Sandoval <osandov@osandov.com> |
|---|---|
| Date | 2016-09-17 10:40 +0200 |
| Subject | [PATCH v4 3/6] sbitmap: push per-cpu last_tag into sbitmap_queue |
| Message-ID | <sijkd-ei-17@gated-at.bofh.it> |
| In reply to | #1485499 |
From: Omar Sandoval <osandov@fb.com>
Allocating your own per-cpu allocation hint separately makes for an
awkward API. Instead, allocate the per-cpu hint as part of the struct
sbitmap_queue. There's no point for a struct sbitmap_queue without the
cache, but you can still use a bare struct sbitmap.
Signed-off-by: Omar Sandoval <osandov@fb.com>
---
block/blk-mq-tag.c | 53 ++++++++++++++++---------------------------------
block/blk-mq-tag.h | 3 ++-
block/blk-mq.c | 2 +-
block/blk-mq.h | 2 --
include/linux/sbitmap.h | 45 ++++++++++++++++++++++++++++++++++++++++-
lib/sbitmap.c | 35 +++++++++++++++++++++++++++++++-
6 files changed, 98 insertions(+), 42 deletions(-)
diff --git a/block/blk-mq-tag.c b/block/blk-mq-tag.c
index 2cbdecd..c9a22db 100644
--- a/block/blk-mq-tag.c
+++ b/block/blk-mq-tag.c
@@ -94,39 +94,21 @@ static inline bool hctx_may_queue(struct blk_mq_hw_ctx *hctx,
#define BT_ALLOC_RR(tags) (tags->alloc_policy == BLK_TAG_ALLOC_RR)
static int __bt_get(struct blk_mq_hw_ctx *hctx, struct sbitmap_queue *bt,
- unsigned int *tag_cache, struct blk_mq_tags *tags)
+ struct blk_mq_tags *tags)
{
- unsigned int last_tag;
- int tag;
-
if (!hctx_may_queue(hctx, bt))
return -1;
-
- last_tag = *tag_cache;
- tag = sbitmap_get(&bt->sb, last_tag, BT_ALLOC_RR(tags));
-
- if (tag == -1) {
- *tag_cache = 0;
- } else if (tag == last_tag || unlikely(BT_ALLOC_RR(tags))) {
- last_tag = tag + 1;
- if (last_tag >= bt->sb.depth - 1)
- last_tag = 0;
- *tag_cache = last_tag;
- }
-
- return tag;
+ return __sbitmap_queue_get(bt, BT_ALLOC_RR(tags));
}
-static int bt_get(struct blk_mq_alloc_data *data,
- struct sbitmap_queue *bt,
- struct blk_mq_hw_ctx *hctx,
- unsigned int *last_tag, struct blk_mq_tags *tags)
+static int bt_get(struct blk_mq_alloc_data *data, struct sbitmap_queue *bt,
+ struct blk_mq_hw_ctx *hctx, struct blk_mq_tags *tags)
{
struct sbq_wait_state *ws;
DEFINE_WAIT(wait);
int tag;
- tag = __bt_get(hctx, bt, last_tag, tags);
+ tag = __bt_get(hctx, bt, tags);
if (tag != -1)
return tag;
@@ -137,7 +119,7 @@ static int bt_get(struct blk_mq_alloc_data *data,
do {
prepare_to_wait(&ws->wait, &wait, TASK_UNINTERRUPTIBLE);
- tag = __bt_get(hctx, bt, last_tag, tags);
+ tag = __bt_get(hctx, bt, tags);
if (tag != -1)
break;
@@ -154,7 +136,7 @@ static int bt_get(struct blk_mq_alloc_data *data,
* Retry tag allocation after running the hardware queue,
* as running the queue may also have found completions.
*/
- tag = __bt_get(hctx, bt, last_tag, tags);
+ tag = __bt_get(hctx, bt, tags);
if (tag != -1)
break;
@@ -168,7 +150,6 @@ static int bt_get(struct blk_mq_alloc_data *data,
if (data->flags & BLK_MQ_REQ_RESERVED) {
bt = &data->hctx->tags->breserved_tags;
} else {
- last_tag = &data->ctx->last_tag;
hctx = data->hctx;
bt = &hctx->tags->bitmap_tags;
}
@@ -185,7 +166,7 @@ static unsigned int __blk_mq_get_tag(struct blk_mq_alloc_data *data)
int tag;
tag = bt_get(data, &data->hctx->tags->bitmap_tags, data->hctx,
- &data->ctx->last_tag, data->hctx->tags);
+ data->hctx->tags);
if (tag >= 0)
return tag + data->hctx->tags->nr_reserved_tags;
@@ -194,15 +175,15 @@ static unsigned int __blk_mq_get_tag(struct blk_mq_alloc_data *data)
static unsigned int __blk_mq_get_reserved_tag(struct blk_mq_alloc_data *data)
{
- int tag, zero = 0;
+ int tag;
if (unlikely(!data->hctx->tags->nr_reserved_tags)) {
WARN_ON_ONCE(1);
return BLK_MQ_TAG_FAIL;
}
- tag = bt_get(data, &data->hctx->tags->breserved_tags, NULL, &zero,
- data->hctx->tags);
+ tag = bt_get(data, &data->hctx->tags->breserved_tags, NULL,
+ data->hctx->tags);
if (tag < 0)
return BLK_MQ_TAG_FAIL;
@@ -216,8 +197,8 @@ unsigned int blk_mq_get_tag(struct blk_mq_alloc_data *data)
return __blk_mq_get_tag(data);
}
-void blk_mq_put_tag(struct blk_mq_hw_ctx *hctx, unsigned int tag,
- unsigned int *last_tag)
+void blk_mq_put_tag(struct blk_mq_hw_ctx *hctx, struct blk_mq_ctx *ctx,
+ unsigned int tag)
{
struct blk_mq_tags *tags = hctx->tags;
@@ -225,12 +206,12 @@ void blk_mq_put_tag(struct blk_mq_hw_ctx *hctx, unsigned int tag,
const int real_tag = tag - tags->nr_reserved_tags;
BUG_ON(real_tag >= tags->nr_tags);
- sbitmap_queue_clear(&tags->bitmap_tags, real_tag);
- if (likely(tags->alloc_policy == BLK_TAG_ALLOC_FIFO))
- *last_tag = real_tag;
+ sbitmap_queue_clear(&tags->bitmap_tags, real_tag,
+ BT_ALLOC_RR(tags), ctx->cpu);
} else {
BUG_ON(tag >= tags->nr_reserved_tags);
- sbitmap_queue_clear(&tags->breserved_tags, tag);
+ sbitmap_queue_clear(&tags->breserved_tags, tag,
+ BT_ALLOC_RR(tags), ctx->cpu);
}
}
diff --git a/block/blk-mq-tag.h b/block/blk-mq-tag.h
index 3215c08..2b1d52e 100644
--- a/block/blk-mq-tag.h
+++ b/block/blk-mq-tag.h
@@ -27,7 +27,8 @@ extern struct blk_mq_tags *blk_mq_init_tags(unsigned int nr_tags, unsigned int r
extern void blk_mq_free_tags(struct blk_mq_tags *tags);
extern unsigned int blk_mq_get_tag(struct blk_mq_alloc_data *data);
-extern void blk_mq_put_tag(struct blk_mq_hw_ctx *hctx, unsigned int tag, unsigned int *last_tag);
+extern void blk_mq_put_tag(struct blk_mq_hw_ctx *hctx, struct blk_mq_ctx *ctx,
+ unsigned int tag);
extern bool blk_mq_has_free_tags(struct blk_mq_tags *tags);
extern ssize_t blk_mq_tag_sysfs_show(struct blk_mq_tags *tags, char *page);
extern void blk_mq_tag_init_last_tag(struct blk_mq_tags *tags, unsigned int *last_tag);
diff --git a/block/blk-mq.c b/block/blk-mq.c
index 9dbe37f..004728f 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -302,7 +302,7 @@ static void __blk_mq_free_request(struct blk_mq_hw_ctx *hctx,
rq->cmd_flags = 0;
clear_bit(REQ_ATOM_STARTED, &rq->atomic_flags);
- blk_mq_put_tag(hctx, tag, &ctx->last_tag);
+ blk_mq_put_tag(hctx, ctx, tag);
blk_queue_exit(q);
}
diff --git a/block/blk-mq.h b/block/blk-mq.h
index 71831f9..9b15d2e 100644
--- a/block/blk-mq.h
+++ b/block/blk-mq.h
@@ -12,8 +12,6 @@ struct blk_mq_ctx {
unsigned int cpu;
unsigned int index_hw;
- unsigned int last_tag ____cacheline_aligned_in_smp;
-
/* incremented at dispatch time */
unsigned long rq_dispatched[2];
unsigned long rq_merged;
diff --git a/include/linux/sbitmap.h b/include/linux/sbitmap.h
index 1a3b836..6745545 100644
--- a/include/linux/sbitmap.h
+++ b/include/linux/sbitmap.h
@@ -99,6 +99,14 @@ struct sbitmap_queue {
*/
struct sbitmap sb;
+ /*
+ * @alloc_hint: Cache of last successfully allocated or freed bit.
+ *
+ * This is per-cpu, which allows multiple users to stick to different
+ * cachelines until the map is exhausted.
+ */
+ unsigned int __percpu *alloc_hint;
+
/**
* @wake_batch: Number of bits which must be freed before we wake up any
* waiters.
@@ -267,6 +275,7 @@ int sbitmap_queue_init_node(struct sbitmap_queue *sbq, unsigned int depth,
static inline void sbitmap_queue_free(struct sbitmap_queue *sbq)
{
kfree(sbq->ws);
+ free_percpu(sbq->alloc_hint);
sbitmap_free(&sbq->sb);
}
@@ -282,12 +291,46 @@ static inline void sbitmap_queue_free(struct sbitmap_queue *sbq)
void sbitmap_queue_resize(struct sbitmap_queue *sbq, unsigned int depth);
/**
+ * __sbitmap_queue_get() - Try to allocate a free bit from a &struct
+ * sbitmap_queue with preemption already disabled.
+ * @sbq: Bitmap queue to allocate from.
+ * @round_robin: See sbitmap_get().
+ *
+ * Return: Non-negative allocated bit number if successful, -1 otherwise.
+ */
+int __sbitmap_queue_get(struct sbitmap_queue *sbq, bool round_robin);
+
+/**
+ * sbitmap_queue_get() - Try to allocate a free bit from a &struct
+ * sbitmap_queue.
+ * @sbq: Bitmap queue to allocate from.
+ * @round_robin: See sbitmap_get().
+ * @cpu: Output parameter; will contain the CPU we ran on (e.g., to be passed to
+ * sbitmap_queue_clear()).
+ *
+ * Return: Non-negative allocated bit number if successful, -1 otherwise.
+ */
+static inline int sbitmap_queue_get(struct sbitmap_queue *sbq, bool round_robin,
+ unsigned int *cpu)
+{
+ int nr;
+
+ *cpu = get_cpu();
+ nr = __sbitmap_queue_get(sbq, round_robin);
+ put_cpu();
+ return nr;
+}
+
+/**
* sbitmap_queue_clear() - Free an allocated bit and wake up waiters on a
* &struct sbitmap_queue.
* @sbq: Bitmap to free from.
* @nr: Bit number to free.
+ * @round_robin: See sbitmap_get().
+ * @cpu: CPU the bit was allocated on.
*/
-void sbitmap_queue_clear(struct sbitmap_queue *sbq, unsigned int nr);
+void sbitmap_queue_clear(struct sbitmap_queue *sbq, unsigned int nr,
+ bool round_robin, unsigned int cpu);
static inline int sbq_index_inc(int index)
{
diff --git a/lib/sbitmap.c b/lib/sbitmap.c
index 4d8e97e..1651ad9d 100644
--- a/lib/sbitmap.c
+++ b/lib/sbitmap.c
@@ -205,11 +205,18 @@ int sbitmap_queue_init_node(struct sbitmap_queue *sbq, unsigned int depth,
if (ret)
return ret;
+ sbq->alloc_hint = alloc_percpu_gfp(unsigned int, flags);
+ if (!sbq->alloc_hint) {
+ sbitmap_free(&sbq->sb);
+ return -ENOMEM;
+ }
+
sbq->wake_batch = sbq_calc_wake_batch(depth);
atomic_set(&sbq->wake_index, 0);
sbq->ws = kzalloc_node(SBQ_WAIT_QUEUES * sizeof(*sbq->ws), flags, node);
if (!sbq->ws) {
+ free_percpu(sbq->alloc_hint);
sbitmap_free(&sbq->sb);
return -ENOMEM;
}
@@ -229,6 +236,29 @@ void sbitmap_queue_resize(struct sbitmap_queue *sbq, unsigned int depth)
}
EXPORT_SYMBOL_GPL(sbitmap_queue_resize);
+int __sbitmap_queue_get(struct sbitmap_queue *sbq, bool round_robin)
+{
+ unsigned int hint;
+ int nr;
+
+ hint = this_cpu_read(*sbq->alloc_hint);
+ nr = sbitmap_get(&sbq->sb, hint, round_robin);
+
+ if (nr == -1) {
+ /* If the map is full, a hint won't do us much good. */
+ this_cpu_write(*sbq->alloc_hint, 0);
+ } else if (nr == hint || unlikely(round_robin)) {
+ /* Only update the hint if we used it. */
+ hint = nr + 1;
+ if (hint >= sbq->sb.depth - 1)
+ hint = 0;
+ this_cpu_write(*sbq->alloc_hint, hint);
+ }
+
+ return nr;
+}
+EXPORT_SYMBOL_GPL(__sbitmap_queue_get);
+
static struct sbq_wait_state *sbq_wake_ptr(struct sbitmap_queue *sbq)
{
int i, wake_index;
@@ -273,10 +303,13 @@ static void sbq_wake_up(struct sbitmap_queue *sbq)
}
}
-void sbitmap_queue_clear(struct sbitmap_queue *sbq, unsigned int nr)
+void sbitmap_queue_clear(struct sbitmap_queue *sbq, unsigned int nr,
+ bool round_robin, unsigned int cpu)
{
sbitmap_clear_bit(&sbq->sb, nr);
sbq_wake_up(sbq);
+ if (likely(!round_robin))
+ *per_cpu_ptr(sbq->alloc_hint, cpu) = nr;
}
EXPORT_SYMBOL_GPL(sbitmap_queue_clear);
--
2.9.3
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web