Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

blk-mq: fix potential deadlock while nr_requests grown

Allocate and free sched_tags while queue is freezed can deadlock[1],
this is a long term problem, hence allocate memory before freezing
queue and free memory after queue is unfreezed.

[1] https://lore.kernel.org/all/0659ea8d-a463-47c8-9180-43c719e106eb@linux.ibm.com/
Fixes: e3a2b3f931f5 ("blk-mq: allow changing of queue depth through sysfs")

Signed-off-by: Yu Kuai <yukuai3@huawei.com>
Reviewed-by: Nilay Shroff <nilay@linux.ibm.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>

authored by

Yu Kuai and committed by
Jens Axboe
b8643372 6293e336

+34 -22
+9 -13
block/blk-mq.c
··· 4926 4926 } 4927 4927 EXPORT_SYMBOL(blk_mq_free_tag_set); 4928 4928 4929 - int blk_mq_update_nr_requests(struct request_queue *q, unsigned int nr) 4929 + struct elevator_tags *blk_mq_update_nr_requests(struct request_queue *q, 4930 + struct elevator_tags *et, 4931 + unsigned int nr) 4930 4932 { 4931 4933 struct blk_mq_tag_set *set = q->tag_set; 4934 + struct elevator_tags *old_et = NULL; 4932 4935 struct blk_mq_hw_ctx *hctx; 4933 - int ret = 0; 4934 4936 unsigned long i; 4935 4937 4936 4938 blk_mq_quiesce_queue(q); ··· 4967 4965 } 4968 4966 } else { 4969 4967 /* Non-shared sched tags, and tags grow */ 4970 - queue_for_each_hw_ctx(q, hctx, i) { 4971 - if (!hctx->sched_tags) 4972 - continue; 4973 - ret = blk_mq_tag_update_depth(hctx, &hctx->sched_tags, 4974 - nr); 4975 - if (ret) 4976 - goto out; 4977 - } 4968 + queue_for_each_hw_ctx(q, hctx, i) 4969 + hctx->sched_tags = et->tags[i]; 4970 + old_et = q->elevator->et; 4971 + q->elevator->et = et; 4978 4972 } 4979 4973 4980 4974 q->nr_requests = nr; 4981 4975 if (q->elevator && q->elevator->type->ops.depth_updated) 4982 4976 q->elevator->type->ops.depth_updated(q); 4983 4977 4984 - out: 4985 4978 blk_mq_unquiesce_queue(q); 4986 - 4987 - return ret; 4979 + return old_et; 4988 4980 } 4989 4981 4990 4982 /*
+4 -1
block/blk-mq.h
··· 6 6 #include "blk-stat.h" 7 7 8 8 struct blk_mq_tag_set; 9 + struct elevator_tags; 9 10 10 11 struct blk_mq_ctxs { 11 12 struct kobject kobj; ··· 46 45 int blk_mq_poll(struct request_queue *q, blk_qc_t cookie, struct io_comp_batch *iob, 47 46 unsigned int flags); 48 47 void blk_mq_exit_queue(struct request_queue *q); 49 - int blk_mq_update_nr_requests(struct request_queue *q, unsigned int nr); 48 + struct elevator_tags *blk_mq_update_nr_requests(struct request_queue *q, 49 + struct elevator_tags *tags, 50 + unsigned int nr); 50 51 void blk_mq_wake_waiters(struct request_queue *q); 51 52 bool blk_mq_dispatch_rq_list(struct blk_mq_hw_ctx *hctx, struct list_head *, 52 53 bool);
+21 -8
block/blk-sysfs.c
··· 64 64 static ssize_t 65 65 queue_requests_store(struct gendisk *disk, const char *page, size_t count) 66 66 { 67 - unsigned long nr; 68 - int ret, err; 69 - unsigned int memflags; 70 67 struct request_queue *q = disk->queue; 71 68 struct blk_mq_tag_set *set = q->tag_set; 69 + struct elevator_tags *et = NULL; 70 + unsigned int memflags; 71 + unsigned long nr; 72 + int ret; 72 73 73 74 ret = queue_var_store(&nr, page, count); 74 75 if (ret < 0) ··· 100 99 goto unlock; 101 100 } 102 101 102 + if (!blk_mq_is_shared_tags(set->flags) && q->elevator && 103 + nr > q->elevator->et->nr_requests) { 104 + /* 105 + * Tags will grow, allocate memory before freezing queue to 106 + * prevent deadlock. 107 + */ 108 + et = blk_mq_alloc_sched_tags(set, q->nr_hw_queues, nr); 109 + if (!et) { 110 + ret = -ENOMEM; 111 + goto unlock; 112 + } 113 + } 114 + 103 115 memflags = blk_mq_freeze_queue(q); 104 116 mutex_lock(&q->elevator_lock); 105 - 106 - err = blk_mq_update_nr_requests(disk->queue, nr); 107 - if (err) 108 - ret = err; 109 - 117 + et = blk_mq_update_nr_requests(q, et, nr); 110 118 mutex_unlock(&q->elevator_lock); 111 119 blk_mq_unfreeze_queue(q, memflags); 120 + 121 + if (et) 122 + blk_mq_free_sched_tags(et, set); 112 123 113 124 unlock: 114 125 up_write(&set->update_nr_hwq_lock);