[PATCH -next 5/5] blk-iocost: fix sleeping in atomic context warnning in ioc_cost_model_write()

From: Yu Kuai
Date: Fri Oct 28 2022 - 05:49:53 EST


From: Yu Kuai <yukuai3@xxxxxxxxxx>

match_u64() is called inside ioc->lock, which causes smatch static
checker warnings:

block/blk-iocost.c:3407 ioc_cost_model_write() warn: sleeping in atomic context

Fix the problem by prase params before holding the lock.

Fixes: 2c0647988433 ("blk-iocost: don't release 'ioc->lock' while updating params")
Reported-by: Dan Carpenter <dan.carpenter@xxxxxxxxxx>
Signed-off-by: Yu Kuai <yukuai3@xxxxxxxxxx>
---
block/blk-iocost.c | 128 +++++++++++++++++++++++++++++----------------
1 file changed, 84 insertions(+), 44 deletions(-)

diff --git a/block/blk-iocost.c b/block/blk-iocost.c
index 27b41f3f1b07..62e18c2719cb 100644
--- a/block/blk-iocost.c
+++ b/block/blk-iocost.c
@@ -3403,43 +3403,23 @@ static const match_table_t i_lcoef_tokens = {
{ NR_I_LCOEFS, NULL },
};

-static ssize_t ioc_cost_model_write(struct kernfs_open_file *of, char *input,
- size_t nbytes, loff_t off)
-{
- struct block_device *bdev;
- struct request_queue *q;
- struct ioc *ioc;
+struct ioc_model_params {
u64 u[NR_I_LCOEFS];
bool user;
- char *p;
- int ret;
-
- bdev = blkcg_conf_open_bdev(&input);
- if (IS_ERR(bdev))
- return PTR_ERR(bdev);
-
- q = bdev_get_queue(bdev);
- if (!queue_is_mq(q)) {
- ret = -EPERM;
- goto out;
- }
-
- ioc = q_to_ioc(q);
- if (!ioc) {
- ret = blk_iocost_init(bdev->bd_disk);
- if (ret)
- goto out;
- ioc = q_to_ioc(q);
- }
+ bool set_u[NR_I_LCOEFS];
+ bool set_user;
+};

- blk_mq_freeze_queue(q);
- blk_mq_quiesce_queue(q);
+static struct ioc_model_params *ioc_model_parse_params(char *input)
+{
+ struct ioc_model_params *params;
+ char *p;
+ int ret = -EINVAL;

- spin_lock_irq(&ioc->lock);
- memcpy(u, ioc->params.i_lcoefs, sizeof(u));
- user = ioc->user_cost_model;
+ params = kzalloc(sizeof(*params), GFP_KERNEL);
+ if (!params)
+ return ERR_PTR(-ENOMEM);

- ret = -EINVAL;
while ((p = strsep(&input, " \t\n"))) {
substring_t args[MAX_OPT_ARGS];
char buf[32];
@@ -3454,48 +3434,108 @@ static ssize_t ioc_cost_model_write(struct kernfs_open_file *of, char *input,
case COST_CTRL:
match_strlcpy(buf, &args[0], sizeof(buf));
if (!strcmp(buf, "auto"))
- user = false;
+ params->user = false;
else if (!strcmp(buf, "user"))
- user = true;
+ params->user = true;
else
- goto out_unlock;
+ goto err;
+ params->set_user = true;
continue;
case COST_MODEL:
match_strlcpy(buf, &args[0], sizeof(buf));
if (strcmp(buf, "linear"))
- goto out_unlock;
+ goto err;
continue;
}

tok = match_token(p, i_lcoef_tokens, args);
if (tok == NR_I_LCOEFS)
- goto out_unlock;
+ goto err;

err = match_u64(&args[0], &v);
if (err) {
ret = err;
- goto out_unlock;
+ goto err;
}

- u[tok] = v;
- user = true;
+ params->u[tok] = v;
+ params->set_u[tok] = true;
+ params->user = true;
+ params->set_user = true;
}

- if (user) {
- memcpy(ioc->params.i_lcoefs, u, sizeof(u));
+ return params;
+
+err:
+ kfree(params);
+ return ERR_PTR(ret);
+}
+
+static void ioc_model_update_params(struct ioc *ioc,
+ struct ioc_model_params *params)
+{
+ int i;
+
+ if (!params->set_user)
+ params->user = ioc->user_cost_model;
+ if (params->user) {
+ for (i = 0; i < NR_I_LCOEFS; ++i)
+ if (params->set_u[i])
+ ioc->params.i_lcoefs[i] = params->u[i];
ioc->user_cost_model = true;
} else {
ioc->user_cost_model = false;
}
+}
+
+static ssize_t ioc_cost_model_write(struct kernfs_open_file *of, char *input,
+ size_t nbytes, loff_t off)
+{
+ struct block_device *bdev;
+ struct request_queue *q;
+ struct ioc *ioc;
+ struct ioc_model_params *params;
+ int ret;
+
+ bdev = blkcg_conf_open_bdev(&input);
+ if (IS_ERR(bdev))
+ return PTR_ERR(bdev);
+
+ q = bdev_get_queue(bdev);
+ if (!queue_is_mq(q)) {
+ ret = -EPERM;
+ goto out;
+ }

+ ioc = q_to_ioc(q);
+ if (!ioc) {
+ ret = blk_iocost_init(bdev->bd_disk);
+ if (ret)
+ goto out;
+ ioc = q_to_ioc(q);
+ }
+
+ params = ioc_model_parse_params(input);
+ if (IS_ERR(params)) {
+ ret = PTR_ERR(params);
+ goto out;
+ }
+
+ blk_mq_freeze_queue(q);
+ blk_mq_quiesce_queue(q);
+
+ spin_lock_irq(&ioc->lock);
+
+ ioc_model_update_params(ioc, params);
ioc_refresh_params(ioc, true);
- ret = nbytes;

-out_unlock:
spin_unlock_irq(&ioc->lock);
+
blk_mq_unquiesce_queue(q);
blk_mq_unfreeze_queue(q);

+ kfree(params);
+ ret = nbytes;
out:
blkdev_put_no_open(bdev);
return ret;
--
2.31.1