Skip to content

Commit 4e5cc99

Browse files
Ming Leiaxboe
authored andcommitted
blk-mq: manage hctx map via xarray
First code becomes more clean by switching to xarray from plain array. Second use-after-free on q->queue_hw_ctx can be fixed because queue_for_each_hw_ctx() may be run when updating nr_hw_queues is in-progress. With this patch, q->hctx_table is defined as xarray, and this structure will share same lifetime with request queue, so queue_for_each_hw_ctx() can use q->hctx_table to lookup hctx reliably. Reported-by: Yu Kuai <yukuai3@huawei.com> Signed-off-by: Ming Lei <ming.lei@redhat.com> Reviewed-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Christoph Hellwig <hch@lst.de> Link: https://lore.kernel.org/r/20220308073219.91173-7-ming.lei@redhat.com [axboe: fix blk_mq_hw_ctx forward declaration] Signed-off-by: Jens Axboe <axboe@kernel.dk>
1 parent 4f48120 commit 4e5cc99

File tree

6 files changed

+30
-43
lines changed

6 files changed

+30
-43
lines changed

block/blk-mq-debugfs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
#include <linux/seq_file.h>
88

9+
struct blk_mq_hw_ctx;
10+
911
struct blk_mq_debugfs_attr {
1012
const char *name;
1113
umode_t mode;

block/blk-mq-tag.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ void blk_mq_queue_tag_busy_iter(struct request_queue *q, busy_tag_iter_fn *fn,
498498
void *priv)
499499
{
500500
/*
501-
* __blk_mq_update_nr_hw_queues() updates nr_hw_queues and queue_hw_ctx
501+
* __blk_mq_update_nr_hw_queues() updates nr_hw_queues and hctx_table
502502
* while the queue is frozen. So we can use q_usage_counter to avoid
503503
* racing with it.
504504
*/

block/blk-mq.c

Lines changed: 24 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ static int blk_mq_poll_stats_bkt(const struct request *rq)
7171
static inline struct blk_mq_hw_ctx *blk_qc_to_hctx(struct request_queue *q,
7272
blk_qc_t qc)
7373
{
74-
return q->queue_hw_ctx[(qc & ~BLK_QC_T_INTERNAL) >> BLK_QC_T_SHIFT];
74+
return xa_load(&q->hctx_table,
75+
(qc & ~BLK_QC_T_INTERNAL) >> BLK_QC_T_SHIFT);
7576
}
7677

7778
static inline struct request *blk_qc_to_rq(struct blk_mq_hw_ctx *hctx,
@@ -573,7 +574,7 @@ struct request *blk_mq_alloc_request_hctx(struct request_queue *q,
573574
* If not tell the caller that it should skip this queue.
574575
*/
575576
ret = -EXDEV;
576-
data.hctx = q->queue_hw_ctx[hctx_idx];
577+
data.hctx = xa_load(&q->hctx_table, hctx_idx);
577578
if (!blk_mq_hw_queue_mapped(data.hctx))
578579
goto out_queue_exit;
579580
cpu = cpumask_first_and(data.hctx->cpumask, cpu_online_mask);
@@ -3437,6 +3438,8 @@ static void blk_mq_exit_hctx(struct request_queue *q,
34373438

34383439
blk_mq_remove_cpuhp(hctx);
34393440

3441+
xa_erase(&q->hctx_table, hctx_idx);
3442+
34403443
spin_lock(&q->unused_hctx_lock);
34413444
list_add(&hctx->hctx_list, &q->unused_hctx_list);
34423445
spin_unlock(&q->unused_hctx_lock);
@@ -3476,8 +3479,15 @@ static int blk_mq_init_hctx(struct request_queue *q,
34763479
if (blk_mq_init_request(set, hctx->fq->flush_rq, hctx_idx,
34773480
hctx->numa_node))
34783481
goto exit_hctx;
3482+
3483+
if (xa_insert(&q->hctx_table, hctx_idx, hctx, GFP_KERNEL))
3484+
goto exit_flush_rq;
3485+
34793486
return 0;
34803487

3488+
exit_flush_rq:
3489+
if (set->ops->exit_request)
3490+
set->ops->exit_request(set, hctx->fq->flush_rq, hctx_idx);
34813491
exit_hctx:
34823492
if (set->ops->exit_hctx)
34833493
set->ops->exit_hctx(hctx, hctx_idx);
@@ -3856,7 +3866,7 @@ void blk_mq_release(struct request_queue *q)
38563866
kobject_put(&hctx->kobj);
38573867
}
38583868

3859-
kfree(q->queue_hw_ctx);
3869+
xa_destroy(&q->hctx_table);
38603870

38613871
/*
38623872
* release .mq_kobj and sw queue's kobject now because
@@ -3945,46 +3955,28 @@ static struct blk_mq_hw_ctx *blk_mq_alloc_and_init_hctx(
39453955
static void blk_mq_realloc_hw_ctxs(struct blk_mq_tag_set *set,
39463956
struct request_queue *q)
39473957
{
3948-
int i, j, end;
3949-
struct blk_mq_hw_ctx **hctxs = q->queue_hw_ctx;
3950-
3951-
if (q->nr_hw_queues < set->nr_hw_queues) {
3952-
struct blk_mq_hw_ctx **new_hctxs;
3953-
3954-
new_hctxs = kcalloc_node(set->nr_hw_queues,
3955-
sizeof(*new_hctxs), GFP_KERNEL,
3956-
set->numa_node);
3957-
if (!new_hctxs)
3958-
return;
3959-
if (hctxs)
3960-
memcpy(new_hctxs, hctxs, q->nr_hw_queues *
3961-
sizeof(*hctxs));
3962-
q->queue_hw_ctx = new_hctxs;
3963-
kfree(hctxs);
3964-
hctxs = new_hctxs;
3965-
}
3958+
struct blk_mq_hw_ctx *hctx;
3959+
unsigned long i, j;
39663960

39673961
/* protect against switching io scheduler */
39683962
mutex_lock(&q->sysfs_lock);
39693963
for (i = 0; i < set->nr_hw_queues; i++) {
39703964
int old_node;
39713965
int node = blk_mq_get_hctx_node(set, i);
3972-
struct blk_mq_hw_ctx *old_hctx = hctxs[i];
3966+
struct blk_mq_hw_ctx *old_hctx = xa_load(&q->hctx_table, i);
39733967

39743968
if (old_hctx) {
39753969
old_node = old_hctx->numa_node;
39763970
blk_mq_exit_hctx(q, set, old_hctx, i);
39773971
}
39783972

3979-
hctxs[i] = blk_mq_alloc_and_init_hctx(set, q, i, node);
3980-
if (!hctxs[i]) {
3973+
if (!blk_mq_alloc_and_init_hctx(set, q, i, node)) {
39813974
if (!old_hctx)
39823975
break;
39833976
pr_warn("Allocate new hctx on node %d fails, fallback to previous one on node %d\n",
39843977
node, old_node);
3985-
hctxs[i] = blk_mq_alloc_and_init_hctx(set, q, i,
3986-
old_node);
3987-
WARN_ON_ONCE(!hctxs[i]);
3978+
hctx = blk_mq_alloc_and_init_hctx(set, q, i, old_node);
3979+
WARN_ON_ONCE(!hctx);
39883980
}
39893981
}
39903982
/*
@@ -3993,21 +3985,13 @@ static void blk_mq_realloc_hw_ctxs(struct blk_mq_tag_set *set,
39933985
*/
39943986
if (i != set->nr_hw_queues) {
39953987
j = q->nr_hw_queues;
3996-
end = i;
39973988
} else {
39983989
j = i;
3999-
end = q->nr_hw_queues;
40003990
q->nr_hw_queues = set->nr_hw_queues;
40013991
}
40023992

4003-
for (; j < end; j++) {
4004-
struct blk_mq_hw_ctx *hctx = hctxs[j];
4005-
4006-
if (hctx) {
4007-
blk_mq_exit_hctx(q, set, hctx, j);
4008-
hctxs[j] = NULL;
4009-
}
4010-
}
3993+
xa_for_each_start(&q->hctx_table, j, hctx, j)
3994+
blk_mq_exit_hctx(q, set, hctx, j);
40113995
mutex_unlock(&q->sysfs_lock);
40123996
}
40133997

@@ -4046,6 +4030,8 @@ int blk_mq_init_allocated_queue(struct blk_mq_tag_set *set,
40464030
INIT_LIST_HEAD(&q->unused_hctx_list);
40474031
spin_lock_init(&q->unused_hctx_lock);
40484032

4033+
xa_init(&q->hctx_table);
4034+
40494035
blk_mq_realloc_hw_ctxs(set, q);
40504036
if (!q->nr_hw_queues)
40514037
goto err_hctxs;
@@ -4075,7 +4061,7 @@ int blk_mq_init_allocated_queue(struct blk_mq_tag_set *set,
40754061
return 0;
40764062

40774063
err_hctxs:
4078-
kfree(q->queue_hw_ctx);
4064+
xa_destroy(&q->hctx_table);
40794065
q->nr_hw_queues = 0;
40804066
blk_mq_sysfs_deinit(q);
40814067
err_poll:

block/blk-mq.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ static inline struct blk_mq_hw_ctx *blk_mq_map_queue_type(struct request_queue *
8383
enum hctx_type type,
8484
unsigned int cpu)
8585
{
86-
return q->queue_hw_ctx[q->tag_set->map[type].mq_map[cpu]];
86+
return xa_load(&q->hctx_table, q->tag_set->map[type].mq_map[cpu]);
8787
}
8888

8989
static inline enum hctx_type blk_mq_get_hctx_type(unsigned int flags)

include/linux/blk-mq.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -917,8 +917,7 @@ static inline void *blk_mq_rq_to_pdu(struct request *rq)
917917
}
918918

919919
#define queue_for_each_hw_ctx(q, hctx, i) \
920-
for ((i) = 0; (i) < (q)->nr_hw_queues && \
921-
({ hctx = (q)->queue_hw_ctx[i]; 1; }); (i)++)
920+
xa_for_each(&(q)->hctx_table, (i), (hctx))
922921

923922
#define hctx_for_each_ctx(hctx, ctx, i) \
924923
for ((i) = 0; (i) < (hctx)->nr_ctx && \

include/linux/blkdev.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ struct request_queue {
355355
unsigned int queue_depth;
356356

357357
/* hw dispatch queues */
358-
struct blk_mq_hw_ctx **queue_hw_ctx;
358+
struct xarray hctx_table;
359359
unsigned int nr_hw_queues;
360360

361361
/*

0 commit comments

Comments
 (0)