From: Yu Kuai yukuai3@huawei.com
hulk inclusion category: bugfix bugzilla: 177149 https://gitee.com/openeuler/kernel/issues/I4DDEL
-----------------------------------------------
If blk-throttle is enabled and io is issued before blk_throtl_register_queue() is done. Divide by zero crash will be triggered in tg_may_dispatch() because 'throtl_slice' is uninitialized.
Thus introduce a new flag QUEUE_FLAG_THROTL_INIT_DONE. It will be set after blk_throtl_register_queue() is done, and will be checked before applying any config.
Signed-off-by: Yu Kuai yukuai3@huawei.com Reviewed-by: Hou Tao houtao1@huawei.com
Signed-off-by: Chen Jun chenjun102@huawei.com --- block/blk-sysfs.c | 7 +++++++ block/blk-throttle.c | 37 ++++++++++++++++++++++++++++++++++++- include/linux/blkdev.h | 1 + 3 files changed, 44 insertions(+), 1 deletion(-)
diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index b513f1683af0..66765740902b 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -910,6 +910,9 @@ int blk_register_queue(struct gendisk *disk) blk_queue_flag_set(QUEUE_FLAG_REGISTERED, q); wbt_enable_default(q); blk_throtl_register_queue(q); + spin_lock_irq(&q->queue_lock); + blk_queue_flag_set(QUEUE_FLAG_THROTL_INIT_DONE, q); + spin_unlock_irq(&q->queue_lock);
/* Now everything is ready and send out KOBJ_ADD uevent */ kobject_uevent(&q->kobj, KOBJ_ADD); @@ -942,6 +945,10 @@ void blk_unregister_queue(struct gendisk *disk) if (!blk_queue_registered(q)) return;
+ spin_lock_irq(&q->queue_lock); + blk_queue_flag_clear(QUEUE_FLAG_THROTL_INIT_DONE, q); + spin_unlock_irq(&q->queue_lock); + /* * Since sysfs_remove_dir() prevents adding new directory entries * before removal of existing entries starts, protect against diff --git a/block/blk-throttle.c b/block/blk-throttle.c index b771c4299982..6c327893314e 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -11,6 +11,7 @@ #include <linux/bio.h> #include <linux/blktrace_api.h> #include <linux/blk-cgroup.h> +#include <linux/delay.h> #include "blk.h" #include "blk-cgroup-rwstat.h"
@@ -1445,6 +1446,31 @@ static void tg_conf_updated(struct throtl_grp *tg, bool global) } }
+static inline int throtl_check_init_done(struct request_queue *q) +{ + if (test_bit(QUEUE_FLAG_THROTL_INIT_DONE, &q->queue_flags)) + return 0; + + return blk_queue_dying(q) ? -ENODEV : -EBUSY; +} + +/* + * If throtl_check_init_done() return -EBUSY, we should retry after a short + * msleep(), since that throttle init will be completed in blk_register_queue() + * soon. + */ +static inline int throtl_restart_syscall_when_busy(int errno) +{ + int ret = errno; + + if (ret == -EBUSY) { + msleep(10); + ret = restart_syscall(); + } + + return ret; +} + static ssize_t tg_set_conf(struct kernfs_open_file *of, char *buf, size_t nbytes, loff_t off, bool is_u64) { @@ -1458,6 +1484,10 @@ static ssize_t tg_set_conf(struct kernfs_open_file *of, if (ret) return ret;
+ ret = throtl_check_init_done(ctx.disk->queue); + if (ret) + goto out_finish; + ret = -EINVAL; if (sscanf(ctx.body, "%llu", &v) != 1) goto out_finish; @@ -1475,6 +1505,7 @@ static ssize_t tg_set_conf(struct kernfs_open_file *of, ret = 0; out_finish: blkg_conf_finish(&ctx); + ret = throtl_restart_syscall_when_busy(ret); return ret ?: nbytes; }
@@ -1650,8 +1681,11 @@ static ssize_t tg_set_limit(struct kernfs_open_file *of, if (ret) return ret;
- tg = blkg_to_tg(ctx.blkg); + ret = throtl_check_init_done(ctx.disk->queue); + if (ret) + goto out_finish;
+ tg = blkg_to_tg(ctx.blkg); v[0] = tg->bps_conf[READ][index]; v[1] = tg->bps_conf[WRITE][index]; v[2] = tg->iops_conf[READ][index]; @@ -1747,6 +1781,7 @@ static ssize_t tg_set_limit(struct kernfs_open_file *of, ret = 0; out_finish: blkg_conf_finish(&ctx); + ret = throtl_restart_syscall_when_busy(ret); return ret ?: nbytes; }
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 00e71019f4f6..e8e2ab8a6742 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -593,6 +593,7 @@ struct request_queue { /* Keep blk_queue_flag_name[] in sync with the definitions below */ #define QUEUE_FLAG_STOPPED 0 /* queue is stopped */ #define QUEUE_FLAG_DYING 1 /* queue being torn down */ +#define QUEUE_FLAG_THROTL_INIT_DONE 2 /* io throttle can be online */ #define QUEUE_FLAG_NOMERGES 3 /* disable merge attempts */ #define QUEUE_FLAG_SAME_COMP 4 /* complete on same CPU-group */ #define QUEUE_FLAG_FAIL_IO 5 /* fake timeout */