Fix hungtask when nbd_config_put and sanity check for first_minor
Subsystems (2)
v4 cover Fix hungtask when nbd_config_put and sanity check for first_minor
This patchset include two patchsets as follows:
1. Fix hungtask when nbd_config_put
https://patchwork.kernel.org/project/linux-block/list/?series=573381
2. nbd: fix sanity check for first_minor
https://lore.kernel.org/linux-block/[email protected]/
I have consulted with Yu Kuai, and his modification has also been confirmed by him.
Ye Bin (2):
nbd: Fix incorrect error handle when first_minor is illegal in
nbd_dev_add
nbd: Fix hungtask when nbd_config_put
Yu Kuai (2):
nbd: fix max value for 'first_minor'
nbd: fix possible overflow for 'first_minor' in nbd_dev_add()
drivers/block/nbd.c | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-)
Signature · 1 line
2.31.1
PATCH v4 1/4 nbd: fix max value for 'first_minor'
From: Yu Kuai <[email protected]>
commit b1a811633f73 ("block: nbd: add sanity check for first_minor") checks that 'first_minor' should not be greater than 0xff, which is wrong. Whitout the commit, the details that when user pass 0x100000, it ends up create sysfs dir "/sys/block/43:0" are as follows:
nbd_dev_add
disk->first_minor = index << part_shift
-> default part_shift is 5, first_minor is 0x2000000 device_add_disk
ddev->devt = MKDEV(disk->major, disk->first_minor)
-> (0x2b << 20) | (0x2000000) = 0x2b00000
device_add
device_create_sys_dev_entry
format_dev_t
sprintf(buffer, "%u:%u", MAJOR(dev), MINOR(dev));
-> got 43:0
sysfs_create_link -> /sys/block/43:0By the way, with the wrong fix, when part_shift is the default value, only 8 ndb devices can be created since 8 << 5 is greater than 0xff.
Since the max bits for 'first_minor' should be the same as what MKDEV() does, which is 20. Change the upper bound of 'first_minor' from 0xff to 0xfffff.
--- drivers/block/nbd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/block/nbd.c +3 -3
@@ -1803,11 +1803,11 @@ static struct nbd_device *nbd_dev_add(int index, unsigned int refs) disk->major =NBD_MAJOR ;/* Too big first_minor can cause duplicate creation of -* sysfs files/links, since first_minor will be truncated to -* byte in __device_add_disk(). +* sysfs files/links, since MKDEV() expect that the max bits of +* first_minor is 20. */ disk->first_minor = index << part_shift; -if (disk->first_minor >0xff ) { +if (disk->first_minor >MINORMASK ) { err = -EINVAL ; goto out_free_idr; }
Signature · 1 line
2.31.1
PATCH v4 2/4 nbd: fix possible overflow for 'first_minor' in nbd_dev_add()
In reply to Ye BinFrom: Yu Kuai <[email protected]>
If 'part_shift' is not zero, then 'index << part_shift' might overflow to a value that is not greater than '0xfffff', then sysfs might complains about duplicate creation.
--- drivers/block/nbd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/block/nbd.c +3 -3
@@ -1803,11 +1803,11 @@ static struct nbd_device *nbd_dev_add(int index, unsigned int refs) disk->major =NBD_MAJOR ;/* Too big first_minor can cause duplicate creation of -* sysfs files/links, since MKDEV() expect that the max bits of -* first_minor is 20. +* sysfs files/links, since index << part_shift might overflow, or +* MKDEV() expect that the max bits of first_minor is 20. */ disk->first_minor = index << part_shift; -if (disk->first_minor >MINORMASK ) { +if (disk->first_minor < index || disk->first_minor >MINORMASK ) { err = -EINVAL ; goto out_free_idr; }
Signature · 1 line
2.31.1
PATCH v4 3/4 nbd: Fix incorrect error handle when first_minor is illegal in nbd_dev_add
In reply to Ye BinIf first_minor is illegal will goto out_free_idr label, this will miss cleanup disk.
--- drivers/block/nbd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/block/nbd.c +1 -1
@@ -1809,7 +1809,7 @@ static struct nbd_device *nbd_dev_add(int index, unsigned int refs) disk->first_minor = index << part_shift;if (disk->first_minor < index || disk->first_minor >MINORMASK ) { err = -EINVAL ; - goto out_free_idr; + goto out_err_disk; } disk->minors =1 << part_shift;
Signature · 1 line
2.31.1
PATCH v4 4/4 nbd: Fix hungtask when nbd_config_put
In reply to Ye BinI got follow issue:
[ 247.381177] INFO: task kworker/u10:0:47 blocked for more than 120 seconds. [ 247.382644] Not tainted 4.19.90-dirty #140 [ 247.383502] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. [ 247.385027] Call Trace: [ 247.388384] schedule+0xb8/0x3c0 [ 247.388966] schedule_timeout+0x2b4/0x380 [ 247.392815] wait_for_completion+0x367/0x510 [ 247.397713] flush_workqueue+0x32b/0x1340 [ 247.402700] drain_workqueue+0xda/0x3c0 [ 247.403442] destroy_workqueue+0x7b/0x690 [ 247.405014] nbd_config_put.cold+0x2f9/0x5b6 [ 247.405823] recv_work+0x1fd/0x2b0 [ 247.406485] process_one_work+0x70b/0x1610 [ 247.407262] worker_thread+0x5a9/0x1060 [ 247.408699] kthread+0x35e/0x430 [ 247.410918] ret_from_fork+0x1f/0x30
We can reproduce issue as follows:
1. Inject memory fault in nbd_start_device
-1244,10 +1248,18 @@ static int nbd_start_device(struct nbd_device *nbd)
nbd_dev_dbg_init(nbd);
for (i = 0; i < num_connections; i++) {
struct recv_thread_args *args;-
- args = kzalloc(sizeof(*args), GFP_KERNEL);
+
+ if (i == 1) {
+ args = NULL;
+ printk("%s: inject malloc error\n", __func__);
+ }
+ else
+ args = kzalloc(sizeof(*args), GFP_KERNEL);
2. Inject delay in recv_work
-757,6 +760,8 @@ static void recv_work(struct work_struct *work)
blk_mq_complete_request(blk_mq_rq_from_pdu(cmd));
}
+ printk("%s: comm=%s pid=%d\n", __func__, current->comm, current->pid);
+ mdelay(5 * 1000);
nbd_config_put(nbd);
atomic_dec(&config->recv_threads);
wake_up(&config->recv_wq);3. Create nbd server
nbd-server 8000 /tmp/disk
4. Create nbd client
nbd-client localhost 8000 /dev/nbd1
Then will trigger above issue.
Reason is when add delay in recv_work, lead to release the last reference of 'nbd->config_refs'. nbd_config_put will call flush_workqueue to make all work finish. Obviously, it will lead to deadloop.
To solve this issue, according to Josef's suggestion move 'recv_work' init from start device to nbd_dev_add, then destroy 'recv_work'when nbd device teardown.
--- drivers/block/nbd.c | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-)
drivers/block/nbd.c +16 -20
@@ -260,7 +260,7 @@ static void nbd_dev_remove(struct nbd_device *nbd)mutex_lock (&nbd_index_mutex);idr_remove (&nbd_index_idr, nbd->index);mutex_unlock (&nbd_index_mutex); - +destroy_workqueue (nbd->recv_workq);kfree (nbd); }
@@ -1314,10 +1314,6 @@ static void nbd_config_put(struct nbd_device *nbd)kfree (nbd->config); nbd->config =NULL ; -if (nbd->recv_workq) -destroy_workqueue (nbd->recv_workq); - nbd->recv_workq =NULL ; - nbd->tag_set.timeout =0 ; nbd->disk->queue->limits.discard_granularity =0 ; nbd->disk->queue->limits.discard_alignment =0 ;
@@ -1346,14 +1342,6 @@ static int nbd_start_device(struct nbd_device *nbd)return -EINVAL ; } - nbd->recv_workq =alloc_workqueue ("knbd%d-recv" , -WQ_MEM_RECLAIM |WQ_HIGHPRI | -WQ_UNBOUND ,0 , nbd->index); -if (!nbd->recv_workq) { -dev_err (disk_to_dev (nbd->disk),"Could not allocate knbd recv work queue.\n" ); -return -ENOMEM ; - } -blk_mq_update_nr_hw_queues (&nbd->tag_set, config->num_connections); nbd->pid =task_pid_nr (current);
@@ -1779,6 +1767,15 @@ static struct nbd_device *nbd_dev_add(int index, unsigned int refs) } nbd->disk = disk; + nbd->recv_workq =alloc_workqueue ("nbd%d-recv" , +WQ_MEM_RECLAIM |WQ_HIGHPRI | +WQ_UNBOUND ,0 , nbd->index); +if (!nbd->recv_workq) { +dev_err (disk_to_dev (nbd->disk),"Could not allocate knbd recv work queue.\n" ); + err = -ENOMEM ; + goto out_err_disk; + } +/* * Tell the block layer that we are not a rotational device */
@@ -1809,7 +1806,7 @@ static struct nbd_device *nbd_dev_add(int index, unsigned int refs) disk->first_minor = index << part_shift;if (disk->first_minor < index || disk->first_minor >MINORMASK ) { err = -EINVAL ; - goto out_err_disk; + goto out_free_work; } disk->minors =1 << part_shift;
@@ -1818,7 +1815,7 @@ static struct nbd_device *nbd_dev_add(int index, unsigned int refs)sprintf (disk->disk_name,"nbd%d" , index); err =add_disk (disk);if (err) - goto out_err_disk; + goto out_free_work;/* * Now publish the device.
@@ -1827,6 +1824,8 @@ static struct nbd_device *nbd_dev_add(int index, unsigned int refs) nbd_total_devices++;return nbd; +out_free_work: +destroy_workqueue (nbd->recv_workq); out_err_disk:blk_cleanup_disk (disk); out_free_idr:
@@ -2082,13 +2081,10 @@ static void nbd_disconnect_and_put(struct nbd_device *nbd)nbd_disconnect (nbd);sock_shutdown (nbd);/* -* Make sure recv thread has finished, so it does not drop the last -* config ref and try to destroy the workqueue from inside the work -* queue. And this also ensure that we can safely call nbd_clear_que() +* Make sure recv thread has finished, we can safely call nbd_clear_que() * to cancel the inflight I/Os. */ -if (nbd->recv_workq) -flush_workqueue (nbd->recv_workq); +flush_workqueue (nbd->recv_workq);nbd_clear_que (nbd); nbd->task_setup =NULL ;mutex_unlock (&nbd->config_lock);
Signature · 1 line
2.31.1
Quoted 13 lines · Ye Bin
This patchset include two patchsets as follows:
1. Fix hungtask whennbd_config_put
https://patchwork.kernel.org/project/linux-block/list/?series=573381
2. nbd: fix sanity check forfirst_minor
https://lore.kernel.org/linux-block/[email protected]/I have consulted with Yu Kuai, and his modification has also been confirmed by him.
Ye Bin (2):
nbd: Fix incorrect error handle whenfirst_minoris illegal in
nbd_dev_add
nbd: Fix hungtask whennbd_config_put
Quoted 7 lines
Yu Kuai (2):
nbd: fix max value for 'first_minor'
nbd: fix possible overflow for 'first_minor' innbd_dev_add()drivers/block/nbd.c | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-)
Perfect, thanks guys, you can add
to the series,
Josef
Quoted 9 lines · Ye Bin
This patchset include two patchsets as follows:
1. Fix hungtask whennbd_config_put
https://patchwork.kernel.org/project/linux-block/list/?series=573381
2. nbd: fix sanity check forfirst_minor
https://lore.kernel.org/linux-block/[email protected]/I have consulted with Yu Kuai, and his modification has also been confirmed by him.
[...]
Applied, thanks!
[1/4] nbd: fix max value for 'first_minor'
commit: e4c4871a73944353ea23e319de27ef73ce546623
[2/4] nbd: fix possible overflow for 'first_minor' in nbd_dev_add()
commit: 940c264984fd1457918393c49674f6b39ee16506
[3/4] nbd: Fix incorrect error handle when first_minor is illegal in nbd_dev_add
commit: 69beb62ff0d1723a750eebe1c4d01da573d7cd19
[4/4] nbd: Fix hungtask when nbd_config_put
commit: e2daec488c57069a4a431d5b752f50294c4bf273Best regards,
Signature · 1 line
Jens Axboe