* [PATCH hci-8.1 0/2] vhost-blk: switch from VHOST_SET_NWORKERS to the mainstream worker ioctls @ 2026-10-27 11:38 Alexander Ivanov 2026-10-27 11:38 ` [PATCH hci-8.1 1/2] vhost-blk: create kernel threads with " Alexander Ivanov 2026-10-27 11:38 ` [PATCH hci-8.1 2/2] linux-headers: drop VHOST_SET_NWORKERS Alexander Ivanov 0 siblings, 2 replies; 7+ messages in thread From: Alexander Ivanov @ 2026-10-27 11:38 UTC (permalink / raw) To: andrey.zhadchenko; +Cc: andrey.drobyshev, svt-core The kernel drops our VHOST_SET_NWORKERS ioctl in favour of the mainstream VHOST_NEW_WORKER/VHOST_ATTACH_VRING_WORKER, which vhost-blk now supports. Patch 1 makes vhost-blk create its num-threads workers with the mainstream ioctls, keeping the same worker layout, and fall back to a single thread on a kernel without them. Patch 2 drops VHOST_SET_NWORKERS from linux-headers. https://virtuozzo.atlassian.net/browse/VSTOR-107537 Alexander Ivanov (2): vhost-blk: create kernel threads with the mainstream worker ioctls linux-headers: drop VHOST_SET_NWORKERS hw/block/vhost-blk.c | 75 +++++++++++++++++++++++++++++++++++-- linux-headers/linux/vhost.h | 9 ----- 2 files changed, 71 insertions(+), 13 deletions(-) -- 2.53.0 ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH hci-8.1 1/2] vhost-blk: create kernel threads with the mainstream worker ioctls 2026-10-27 11:38 [PATCH hci-8.1 0/2] vhost-blk: switch from VHOST_SET_NWORKERS to the mainstream worker ioctls Alexander Ivanov @ 2026-10-27 11:38 ` Alexander Ivanov 2026-10-27 11:43 ` no-reply 2026-10-27 11:38 ` [PATCH hci-8.1 2/2] linux-headers: drop VHOST_SET_NWORKERS Alexander Ivanov 1 sibling, 1 reply; 7+ messages in thread From: Alexander Ivanov @ 2026-10-27 11:38 UTC (permalink / raw) To: andrey.zhadchenko; +Cc: andrey.drobyshev, svt-core num-threads was passed to our own VHOST_SET_NWORKERS kernel ioctl. The kernel is dropping it in favour of the mainstream worker ioctls, which vhost-blk now supports, so use those: create num-threads - 1 workers with VHOST_NEW_WORKER and attach them to the virtqueues round-robin with VHOST_ATTACH_VRING_WORKER, the default worker being the first one. This is the layout VHOST_SET_NWORKERS produced. VHOST_NEW_WORKER works only in vhost_task mode, while the kernel defaults to kthreads, so switch the device to vhost_task mode with VHOST_SET_FORK_FROM_OWNER before VHOST_SET_OWNER. VHOST_RESET_OWNER restores the default, hence this is done on every start. With num-threads=1 nothing changes: no worker ioctls, kthread mode. On a kernel without these ioctls warn and run a single thread rather than fail to start vhost-blk. https://virtuozzo.atlassian.net/browse/VSTOR-107537 Signed-off-by: Alexander Ivanov <alexander.ivanov@virtuozzo.com> --- hw/block/vhost-blk.c | 75 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 71 insertions(+), 4 deletions(-) diff --git a/hw/block/vhost-blk.c b/hw/block/vhost-blk.c index abddff02c6d..99fec9533a9 100644 --- a/hw/block/vhost-blk.c +++ b/hw/block/vhost-blk.c @@ -33,6 +33,51 @@ #include "migration/cpr.h" #include "system/runstate.h" +/* + * Spread the virtqueues round-robin over num-threads workers, the first one + * being the default worker that VHOST_SET_OWNER created. + */ +static int vhost_blk_set_workers(VHostBlk *s, int nworkers) +{ + uint32_t ids[VHOST_BLK_MAX_QUEUES]; + struct vhost_worker_state worker; + struct vhost_vring_worker vq_worker; + int ret, i; + + for (i = 1; i < nworkers; i++) { + memset(&worker, 0, sizeof(worker)); + if (ioctl(s->vhostfd, VHOST_NEW_WORKER, &worker)) { + ret = -errno; + if (ret == -ENOTTY && i == 1) { + warn_report_once("vhost-blk: kernel does not support worker " + "ioctls, using a single kernel thread"); + return 0; + } + error_report("vhost-blk: unable to create kernel thread: %s", + strerror(-ret)); + return ret; + } + ids[i] = worker.worker_id; + } + + for (i = 0; i < s->dev.nvqs; i++) { + if (i % nworkers == 0) { + continue; + } + memset(&vq_worker, 0, sizeof(vq_worker)); + vq_worker.index = i; + vq_worker.worker_id = ids[i % nworkers]; + if (ioctl(s->vhostfd, VHOST_ATTACH_VRING_WORKER, &vq_worker)) { + ret = -errno; + error_report("vhost-blk: unable to attach kernel thread to " + "queue %d: %s", i, strerror(-ret)); + return ret; + } + } + + return 0; +} + static int vhost_blk_start(VirtIODevice *vdev) { VHostBlk *s = VHOST_BLK(vdev); @@ -51,15 +96,37 @@ static int vhost_blk_start(VirtIODevice *vdev) return 0; } + nworkers = MIN(s->conf.num_threads, s->dev.nvqs); + if (nworkers > 1) { + /* + * VHOST_NEW_WORKER works only in vhost_task mode, and + * VHOST_RESET_OWNER brings the kthread default back. + */ + uint8_t fork_owner = VHOST_FORK_OWNER_TASK; + + if (ioctl(s->vhostfd, VHOST_SET_FORK_FROM_OWNER, &fork_owner)) { + ret = -errno; + if (ret != -ENOTTY) { + error_report("vhost-blk: unable to set vhost task mode: %s", + strerror(-ret)); + return ret; + } + warn_report_once("vhost-blk: kernel does not support vhost " + "tasks, using a single kernel thread"); + nworkers = 1; + } + } + if (ioctl(s->vhostfd, VHOST_SET_OWNER, NULL)) { error_report("vhost-blk: unable to set owner"); return -ENOSYS; } - nworkers = s->conf.num_threads; - if (nworkers != 1 && ioctl(s->vhostfd, VHOST_SET_NWORKERS, &nworkers)) { - error_report("vhost-blk: unable to set number of kernel threads"); - return -EINVAL; + if (nworkers > 1) { + ret = vhost_blk_set_workers(s, nworkers); + if (ret < 0) { + return ret; + } } if (s->conf.serial) { -- 2.53.0 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH hci-8.1 1/2] vhost-blk: create kernel threads with the mainstream worker ioctls 2026-10-27 11:38 ` [PATCH hci-8.1 1/2] vhost-blk: create kernel threads with " Alexander Ivanov @ 2026-10-27 11:43 ` no-reply 2026-10-27 12:11 ` Alexander Ivanov 0 siblings, 1 reply; 7+ messages in thread From: no-reply @ 2026-10-27 11:43 UTC (permalink / raw) To: andrey.zhadchenko, Alexander Ivanov; +Cc: andrey.drobyshev, svt-core Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: - [High] vhost-blk: ids[] stack overflow in vhost_blk_set_workers() for num-queues above 32 - [Medium] vhost-blk: ENOTTY from VHOST_SET_FORK_FROM_OWNER wrongly forces a single thread - [Low] vhost-blk: num-threads 0 and values above nvqs silently clamped in vhost_blk_start() -- commit 5f90746d5e0757eb9fa0a34f3f44664e746de67b Author: Alexander Ivanov <alexander.ivanov@virtuozzo.com> vhost-blk: create kernel threads with the mainstream worker ioctls This commit replaces the VHOST_SET_NWORKERS ioctl with VHOST_NEW_WORKER and VHOST_ATTACH_VRING_WORKER. It spreads the virtqueues round-robin over num-threads workers. It also switches the device to vhost_task mode with VHOST_SET_FORK_FROM_OWNER before VHOST_SET_OWNER. Link: https://virtuozzo.atlassian.net/browse/VSTOR-107537 > diff --git a/hw/block/vhost-blk.c b/hw/block/vhost-blk.c > index abddff02c6d92..99fec9533a9cc 100644 > --- a/hw/block/vhost-blk.c > +++ b/hw/block/vhost-blk.c [ ... ] > +static int vhost_blk_set_workers(VHostBlk *s, int nworkers) > +{ > + uint32_t ids[VHOST_BLK_MAX_QUEUES]; > + struct vhost_worker_state worker; > + struct vhost_vring_worker vq_worker; > + int ret, i; > + > + for (i = 1; i < nworkers; i++) { > + memset(&worker, 0, sizeof(worker)); > + if (ioctl(s->vhostfd, VHOST_NEW_WORKER, &worker)) { > + ret = -errno; > + if (ret == -ENOTTY && i == 1) { > + warn_report_once("vhost-blk: kernel does not support worker " > + "ioctls, using a single kernel thread"); > + return 0; > + } > + error_report("vhost-blk: unable to create kernel thread: %s", > + strerror(-ret)); > + return ret; > + } > + ids[i] = worker.worker_id; [Severity: High] Can this overflow ids[]? ids[] has VHOST_BLK_MAX_QUEUES (32) entries, but nworkers is bounded only by nvqs. In vhost_blk_start(): nworkers = MIN(s->conf.num_threads, s->dev.nvqs); vhost_blk_device_realize() and vhost_blk_pci_realize() clamp num-queues to VHOST_BLK_MAX_QUEUES only when it equals VHOST_BLK_AUTO_NUM_QUEUES: if (conf->num_queues == VHOST_BLK_AUTO_NUM_QUEUES) { conf->num_queues = MIN(virtio_pci_optimal_num_queues(0), VHOST_BLK_MAX_QUEUES); } An explicit value such as num-queues=64 is accepted and becomes s->dev.nvqs. With num-threads=64, nworkers is 64. The loop above then writes ids[i] past the end of the stack array for i >= 32. > + } > + > + for (i = 0; i < s->dev.nvqs; i++) { > + if (i % nworkers == 0) { > + continue; > + } > + memset(&vq_worker, 0, sizeof(vq_worker)); > + vq_worker.index = i; > + vq_worker.worker_id = ids[i % nworkers]; The same configuration also reads ids[i % nworkers] out of bounds when i % nworkers >= 32. Should realize reject num-queues above VHOST_BLK_MAX_QUEUES, or should ids[] be sized from nworkers or nvqs? [ ... ] > static int vhost_blk_start(VirtIODevice *vdev) > { [ ... ] > + nworkers = MIN(s->conf.num_threads, s->dev.nvqs); > + if (nworkers > 1) { [Severity: Low] Is it intended that num-threads=0 and values above nvqs are silently clamped here? vhost_blk_device_realize() validates num-queues but has no equivalent check for num-threads, and the property is an unconstrained uint16: DEFINE_PROP_UINT16("num-threads", VHostBlk, conf.num_threads, 1), With num-threads=0, nworkers becomes 0, the nworkers > 1 test is false, and no worker ioctls are issued. Before this patch the old condition was nworkers != 1, so a value of 0 reached VHOST_SET_NWORKERS. Would a realize-time check that rejects 0, or warns when num-threads exceeds num-queues, make the behaviour explicit? The commit message only says that num-threads=1 is unchanged. > + /* > + * VHOST_NEW_WORKER works only in vhost_task mode, and > + * VHOST_RESET_OWNER brings the kthread default back. > + */ > + uint8_t fork_owner = VHOST_FORK_OWNER_TASK; > + > + if (ioctl(s->vhostfd, VHOST_SET_FORK_FROM_OWNER, &fork_owner)) { > + ret = -errno; > + if (ret != -ENOTTY) { > + error_report("vhost-blk: unable to set vhost task mode: %s", > + strerror(-ret)); > + return ret; > + } > + warn_report_once("vhost-blk: kernel does not support vhost " > + "tasks, using a single kernel thread"); > + nworkers = 1; [Severity: Medium] Does this -ENOTTY fallback drop a working multi-thread configuration? linux-headers/linux/vhost.h documents VHOST_SET_FORK_FROM_OWNER as "Only available when CONFIG_VHOST_ENABLE_FORK_OWNER_CONTROL=y". It also documents VHOST_FORK_OWNER_TASK as the "(default value)". A kernel built without that option returns -ENOTTY here. According to the header, such a kernel runs in task mode, which VHOST_NEW_WORKER and VHOST_ATTACH_VRING_WORKER need. In that case nworkers = 1 discards a usable setup. The commit message says "the kernel defaults to kthreads", which contradicts the header. I could not check the kernel source for the target kernel, so which one is correct is unverified. vhost_blk_set_workers() already handles -ENOTTY from VHOST_NEW_WORKER when i == 1, with its own warning and single-thread fallback. Is the earlier -ENOTTY fallback needed, given that? > + } > + } > + [ ... ] -- Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261027113857.2928048-1-alexander.ivanov@virtuozzo.com?part=1 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH hci-8.1 1/2] vhost-blk: create kernel threads with the mainstream worker ioctls 2026-10-27 11:43 ` no-reply @ 2026-10-27 12:11 ` Alexander Ivanov 0 siblings, 0 replies; 7+ messages in thread From: Alexander Ivanov @ 2026-10-27 12:11 UTC (permalink / raw) To: no-reply, andrey.zhadchenko; +Cc: andrey.drobyshev, svt-core >> +static int vhost_blk_set_workers(VHostBlk *s, int nworkers) >> +{ >> + uint32_t ids[VHOST_BLK_MAX_QUEUES]; >> + struct vhost_worker_state worker; >> + struct vhost_vring_worker vq_worker; >> + int ret, i; >> + >> + for (i = 1; i < nworkers; i++) { >> + memset(&worker, 0, sizeof(worker)); >> + if (ioctl(s->vhostfd, VHOST_NEW_WORKER, &worker)) { >> + ret = -errno; >> + if (ret == -ENOTTY && i == 1) { >> + warn_report_once("vhost-blk: kernel does not support worker " >> + "ioctls, using a single kernel thread"); >> + return 0; >> + } >> + error_report("vhost-blk: unable to create kernel thread: %s", >> + strerror(-ret)); >> + return ret; >> + } >> + ids[i] = worker.worker_id; > [Severity: High] > Can this overflow ids[]? > > ids[] has VHOST_BLK_MAX_QUEUES (32) entries, but nworkers is bounded only > by nvqs. In vhost_blk_start(): > > nworkers = MIN(s->conf.num_threads, s->dev.nvqs); > > vhost_blk_device_realize() and vhost_blk_pci_realize() clamp num-queues to > VHOST_BLK_MAX_QUEUES only when it equals VHOST_BLK_AUTO_NUM_QUEUES: > > if (conf->num_queues == VHOST_BLK_AUTO_NUM_QUEUES) { > conf->num_queues = MIN(virtio_pci_optimal_num_queues(0), > VHOST_BLK_MAX_QUEUES); > } > > An explicit value such as num-queues=64 is accepted and becomes > s->dev.nvqs. With num-threads=64, nworkers is 64. > > The loop above then writes ids[i] past the end of the stack array for > i >= 32. Fixed. > >> + } >> + >> + for (i = 0; i < s->dev.nvqs; i++) { >> + if (i % nworkers == 0) { >> + continue; >> + } >> + memset(&vq_worker, 0, sizeof(vq_worker)); >> + vq_worker.index = i; >> + vq_worker.worker_id = ids[i % nworkers]; > The same configuration also reads ids[i % nworkers] out of bounds when > i % nworkers >= 32. > > Should realize reject num-queues above VHOST_BLK_MAX_QUEUES, or should > ids[] be sized from nworkers or nvqs? Fixed. > > [ ... ] > >> static int vhost_blk_start(VirtIODevice *vdev) >> { > [ ... ] > >> + nworkers = MIN(s->conf.num_threads, s->dev.nvqs); >> + if (nworkers > 1) { > [Severity: Low] > Is it intended that num-threads=0 and values above nvqs are silently > clamped here? > > vhost_blk_device_realize() validates num-queues but has no equivalent > check for num-threads, and the property is an unconstrained uint16: > > DEFINE_PROP_UINT16("num-threads", VHostBlk, conf.num_threads, 1), > > With num-threads=0, nworkers becomes 0, the nworkers > 1 test is false, > and no worker ioctls are issued. Before this patch the old condition was > nworkers != 1, so a value of 0 reached VHOST_SET_NWORKERS. > > Would a realize-time check that rejects 0, or warns when num-threads > exceeds num-queues, make the behaviour explicit? The commit message only > says that num-threads=1 is unchanged. No behaviour change: the old VHOST_SET_NWORKERS handler already clamped n to nvqs and created no extra workers for n=0, and the patch does the same on the QEMU side. >> + /* >> + * VHOST_NEW_WORKER works only in vhost_task mode, and >> + * VHOST_RESET_OWNER brings the kthread default back. >> + */ >> + uint8_t fork_owner = VHOST_FORK_OWNER_TASK; >> + >> + if (ioctl(s->vhostfd, VHOST_SET_FORK_FROM_OWNER, &fork_owner)) { >> + ret = -errno; >> + if (ret != -ENOTTY) { >> + error_report("vhost-blk: unable to set vhost task mode: %s", >> + strerror(-ret)); >> + return ret; >> + } >> + warn_report_once("vhost-blk: kernel does not support vhost " >> + "tasks, using a single kernel thread"); >> + nworkers = 1; > [Severity: Medium] > Does this -ENOTTY fallback drop a working multi-thread configuration? > > linux-headers/linux/vhost.h documents VHOST_SET_FORK_FROM_OWNER as "Only > available when CONFIG_VHOST_ENABLE_FORK_OWNER_CONTROL=y". It also > documents VHOST_FORK_OWNER_TASK as the "(default value)". > > A kernel built without that option returns -ENOTTY here. According to the > header, such a kernel runs in task mode, which VHOST_NEW_WORKER and > VHOST_ATTACH_VRING_WORKER need. In that case nworkers = 1 discards a > usable setup. > > The commit message says "the kernel defaults to kthreads", which > contradicts the header. I could not check the kernel source for the target > kernel, so which one is correct is unverified. > > vhost_blk_set_workers() already handles -ENOTTY from VHOST_NEW_WORKER when > i == 1, with its own warning and single-thread fallback. Is the earlier > -ENOTTY fallback needed, given that? Our kernel, unlike the mainline header, defaults to kthreads, so without CONFIG_VHOST_ENABLE_FORK_OWNER_CONTROL VHOST_NEW_WORKER fails with -EFAULT, which the vhost_blk_set_workers() fallback does not catch, so the early -ENOTTY fallback is needed (commit message reworded to say "unlike mainline"). ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH hci-8.1 2/2] linux-headers: drop VHOST_SET_NWORKERS 2026-10-27 11:38 [PATCH hci-8.1 0/2] vhost-blk: switch from VHOST_SET_NWORKERS to the mainstream worker ioctls Alexander Ivanov 2026-10-27 11:38 ` [PATCH hci-8.1 1/2] vhost-blk: create kernel threads with " Alexander Ivanov @ 2026-10-27 11:38 ` Alexander Ivanov 2026-10-27 11:42 ` no-reply 1 sibling, 1 reply; 7+ messages in thread From: Alexander Ivanov @ 2026-10-27 11:38 UTC (permalink / raw) To: andrey.zhadchenko; +Cc: andrey.drobyshev, svt-core vhost-blk no longer uses it, and the kernel drops the ioctl. https://virtuozzo.atlassian.net/browse/VSTOR-107537 Signed-off-by: Alexander Ivanov <alexander.ivanov@virtuozzo.com> --- linux-headers/linux/vhost.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/linux-headers/linux/vhost.h b/linux-headers/linux/vhost.h index baad06412d4..b6eb628d973 100644 --- a/linux-headers/linux/vhost.h +++ b/linux-headers/linux/vhost.h @@ -102,15 +102,6 @@ #define VHOST_GET_VRING_WORKER _IOWR(VHOST_VIRTIO, 0x16, \ struct vhost_vring_worker) -/* Set number of vhost workers - * Currently number of vhost workers can only be increased. - * All workers are freed upon reset. - * If the value is too big it is silently truncated to the maximum number of - * supported vhost workers - * Even if the error is returned it is possible that some workers were created - */ -#define VHOST_SET_NWORKERS _IOW(VHOST_VIRTIO, 0x1F, int) - /* The following ioctls use eventfd file descriptors to signal and poll * for events. */ -- 2.53.0 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH hci-8.1 2/2] linux-headers: drop VHOST_SET_NWORKERS 2026-10-27 11:38 ` [PATCH hci-8.1 2/2] linux-headers: drop VHOST_SET_NWORKERS Alexander Ivanov @ 2026-10-27 11:42 ` no-reply 2026-10-27 12:29 ` Alexander Ivanov 0 siblings, 1 reply; 7+ messages in thread From: no-reply @ 2026-10-27 11:42 UTC (permalink / raw) To: andrey.zhadchenko, Alexander Ivanov; +Cc: svt-core, andrey.drobyshev Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [Medium] vhost-blk: vhost_blk_start() leaks owner and workers on worker setup failure - [Medium] vhost-blk: ids[] stack overflow in vhost_blk_set_workers() when num-queues/num-threads > 32 -- commit c5f15a317fc4cac3e42d0133296a66fbe1a1ca7d Author: Alexander Ivanov <alexander.ivanov@virtuozzo.com> linux-headers: drop VHOST_SET_NWORKERS This commit removes the VHOST_SET_NWORKERS ioctl definition from linux-headers/linux/vhost.h, since vhost-blk no longer uses it and the kernel drops the ioctl. Link: https://virtuozzo.atlassian.net/browse/VSTOR-107537 > diff --git a/linux-headers/linux/vhost.h b/linux-headers/linux/vhost.h > index baad06412d451..b6eb628d97389 100644 > --- a/linux-headers/linux/vhost.h > +++ b/linux-headers/linux/vhost.h > @@ -102,15 +102,6 @@ > #define VHOST_GET_VRING_WORKER _IOWR(VHOST_VIRTIO, 0x16, \ > struct vhost_vring_worker) > > -/* Set number of vhost workers > - * Currently number of vhost workers can only be increased. > - * All workers are freed upon reset. > - * If the value is too big it is silently truncated to the maximum number of > - * supported vhost workers > - * Even if the error is returned it is possible that some workers were created > - */ > -#define VHOST_SET_NWORKERS _IOW(VHOST_VIRTIO, 0x1F, int) > - > /* The following ioctls use eventfd file descriptors to signal and poll > * for events. */ [Severity: Medium] The two comments below are not about the lines removed by this header change. They concern hw/block/vhost-blk.c as modified by the preceding commit "vhost-blk: create kernel threads with the mainstream worker ioctls", which added vhost_blk_set_workers(). This patch does not touch that code, and no later commit in the series fixes either point. Can vhost_blk_start() leak the vhost owner and workers when worker setup fails? hw/block/vhost-blk.c:vhost_blk_start() { ... if (ioctl(s->vhostfd, VHOST_SET_OWNER, NULL)) { error_report("vhost-blk: unable to set owner"); return -ENOSYS; } if (nworkers > 1) { ret = vhost_blk_set_workers(s, nworkers); if (ret < 0) { return ret; } } ... } If VHOST_NEW_WORKER fails in vhost_blk_set_workers() (for example with -ENOMEM, a thread limit, or -ENOTTY), or a mid-loop VHOST_ATTACH_VRING_WORKER fails, the error is returned with the owner set and some workers already created or attached. Neither the helper nor vhost_blk_start() issues VHOST_RESET_OWNER or VHOST_FREE_WORKER. s->vhost_started is still false at that point, so vhost_blk_stop() returns early: hw/block/vhost-blk.c:vhost_blk_stop() { if (!s->vhost_started) { return; } ... } A retry of vhost_blk_start() before a reset would then fail, because VHOST_SET_OWNER on an already owned device returns an error that is reported as -ENOSYS. The resources are not permanently leaked. vhost_blk_reset() issues VHOST_RESET_OWNER on guest device reset and on the PAUSED and FINISH_MIGRATE handlers, and closing vhostfd in vhost_dev_cleanup() at unrealize frees everything. The serial-failure path (return -EINVAL after VHOST_SET_OWNER) and the set_guest_notifiers failure path have the same missing-reset pattern. That pattern predates the preceding commit, as did the old VHOST_SET_NWORKERS failure return. [Severity: Medium] Can vhost_blk_set_workers() overflow the ids[] array on the stack? hw/block/vhost-blk.c:vhost_blk_set_workers() { uint32_t ids[VHOST_BLK_MAX_QUEUES]; ... for (i = 1; i < nworkers; i++) { ... ids[i] = worker.worker_id; } ... } ids[] has VHOST_BLK_MAX_QUEUES (32) entries, and the loop writes ids[i] for every i below nworkers with no bound check. The only limit on nworkers is in vhost_blk_start(): nworkers = MIN(s->conf.num_threads, s->dev.nvqs); Neither input is capped at 32. In vhost_blk_device_realize() the VHOST_BLK_MAX_QUEUES cap is applied only in the VHOST_BLK_AUTO_NUM_QUEUES branch: if (conf->num_queues == VHOST_BLK_AUTO_NUM_QUEUES) { conf->num_queues = MIN(virtio_pci_optimal_num_queues(0), VHOST_BLK_MAX_QUEUES); } An explicit num-queues above 32 is only checked for being non-zero and then flows into s->dev.nvqs. num-threads is a plain DEFINE_PROP_UINT16 in vhost_blk_properties with no upper bound. With an explicit num-queues=N (33 or more) and num-threads=M (33 or more), nworkers can exceed 32, and the writes to ids[i] go past the end of the array. The later attach loop reads ids[i % nworkers] and can also read out of bounds. This needs QEMU command line or QMP device_add access, so it is not reachable from the guest. Would it make sense to clamp nworkers, and reject num-queues above VHOST_BLK_MAX_QUEUES, in vhost_blk_device_realize() for explicit values as well? -- Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261027113857.2928048-1-alexander.ivanov@virtuozzo.com?part=2 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH hci-8.1 2/2] linux-headers: drop VHOST_SET_NWORKERS 2026-10-27 11:42 ` no-reply @ 2026-10-27 12:29 ` Alexander Ivanov 0 siblings, 0 replies; 7+ messages in thread From: Alexander Ivanov @ 2026-10-27 12:29 UTC (permalink / raw) To: no-reply, andrey.zhadchenko; +Cc: svt-core, andrey.drobyshev >> diff --git a/linux-headers/linux/vhost.h b/linux-headers/linux/vhost.h >> index baad06412d451..b6eb628d97389 100644 >> --- a/linux-headers/linux/vhost.h >> +++ b/linux-headers/linux/vhost.h >> @@ -102,15 +102,6 @@ >> #define VHOST_GET_VRING_WORKER _IOWR(VHOST_VIRTIO, 0x16, \ >> struct vhost_vring_worker) >> >> -/* Set number of vhost workers >> - * Currently number of vhost workers can only be increased. >> - * All workers are freed upon reset. >> - * If the value is too big it is silently truncated to the maximum number of >> - * supported vhost workers >> - * Even if the error is returned it is possible that some workers were created >> - */ >> -#define VHOST_SET_NWORKERS _IOW(VHOST_VIRTIO, 0x1F, int) >> - >> /* The following ioctls use eventfd file descriptors to signal and poll >> * for events. */ > [Severity: Medium] > The two comments below are not about the lines removed by this header > change. They concern hw/block/vhost-blk.c as modified by the preceding > commit "vhost-blk: create kernel threads with the mainstream worker > ioctls", which added vhost_blk_set_workers(). This patch does not touch > that code, and no later commit in the series fixes either point. > > Can vhost_blk_start() leak the vhost owner and workers when worker setup > fails? > > hw/block/vhost-blk.c:vhost_blk_start() { > ... > if (ioctl(s->vhostfd, VHOST_SET_OWNER, NULL)) { > error_report("vhost-blk: unable to set owner"); > return -ENOSYS; > } > > if (nworkers > 1) { > ret = vhost_blk_set_workers(s, nworkers); > if (ret < 0) { > return ret; > } > } > ... > } > > If VHOST_NEW_WORKER fails in vhost_blk_set_workers() (for example with > -ENOMEM, a thread limit, or -ENOTTY), or a mid-loop > VHOST_ATTACH_VRING_WORKER fails, the error is returned with the owner set > and some workers already created or attached. Neither the helper nor > vhost_blk_start() issues VHOST_RESET_OWNER or VHOST_FREE_WORKER. > > s->vhost_started is still false at that point, so vhost_blk_stop() returns > early: > > hw/block/vhost-blk.c:vhost_blk_stop() { > if (!s->vhost_started) { > return; > } > ... > } > > A retry of vhost_blk_start() before a reset would then fail, because > VHOST_SET_OWNER on an already owned device returns an error that is > reported as -ENOSYS. > > The resources are not permanently leaked. vhost_blk_reset() issues > VHOST_RESET_OWNER on guest device reset and on the PAUSED and > FINISH_MIGRATE handlers, and closing vhostfd in vhost_dev_cleanup() at > unrealize frees everything. > > The serial-failure path (return -EINVAL after VHOST_SET_OWNER) and the > set_guest_notifiers failure path have the same missing-reset pattern. > That pattern predates the preceding commit, as did the old > VHOST_SET_NWORKERS failure return. Correct, but this is not a regression: the old VHOST_SET_NWORKERS failure path behaved the same way, VHOST_RESET_OWNER on device reset or closing vhostfd frees everything, and resetting the owner on every start error path is a separate pre-existing issue I'd fix in its own patch. > [Severity: Medium] > Can vhost_blk_set_workers() overflow the ids[] array on the stack? > > hw/block/vhost-blk.c:vhost_blk_set_workers() { > uint32_t ids[VHOST_BLK_MAX_QUEUES]; > ... > for (i = 1; i < nworkers; i++) { > ... > ids[i] = worker.worker_id; > } > ... > } > > ids[] has VHOST_BLK_MAX_QUEUES (32) entries, and the loop writes ids[i] > for every i below nworkers with no bound check. The only limit on > nworkers is in vhost_blk_start(): > > nworkers = MIN(s->conf.num_threads, s->dev.nvqs); > > Neither input is capped at 32. In vhost_blk_device_realize() the > VHOST_BLK_MAX_QUEUES cap is applied only in the > VHOST_BLK_AUTO_NUM_QUEUES branch: > > if (conf->num_queues == VHOST_BLK_AUTO_NUM_QUEUES) { > conf->num_queues = MIN(virtio_pci_optimal_num_queues(0), > VHOST_BLK_MAX_QUEUES); > } > > An explicit num-queues above 32 is only checked for being non-zero and > then flows into s->dev.nvqs. num-threads is a plain DEFINE_PROP_UINT16 > in vhost_blk_properties with no upper bound. > > With an explicit num-queues=N (33 or more) and num-threads=M (33 or > more), nworkers can exceed 32, and the writes to ids[i] go past the end of > the array. The later attach loop reads ids[i % nworkers] and can also read > out of bounds. > > This needs QEMU command line or QMP device_add access, so it is not > reachable from the guest. Would it make sense to clamp nworkers, and > reject num-queues above VHOST_BLK_MAX_QUEUES, in > vhost_blk_device_realize() for explicit values as well? The clamp is already there: vhost_blk_set_workers() does nworkers = MIN(nworkers, VHOST_BLK_MAX_QUEUES) before the loop, so ids[] is only written and read below index 32. With more queues than that, the extra queues share the 32 workers round-robin. ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-10-27 12:29 UTC | newest] Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-10-27 11:38 [PATCH hci-8.1 0/2] vhost-blk: switch from VHOST_SET_NWORKERS to the mainstream worker ioctls Alexander Ivanov 2026-10-27 11:38 ` [PATCH hci-8.1 1/2] vhost-blk: create kernel threads with " Alexander Ivanov 2026-10-27 11:43 ` no-reply 2026-10-27 12:11 ` Alexander Ivanov 2026-10-27 11:38 ` [PATCH hci-8.1 2/2] linux-headers: drop VHOST_SET_NWORKERS Alexander Ivanov 2026-10-27 11:42 ` no-reply 2026-10-27 12:29 ` Alexander Ivanov
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.