* [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
* [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 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
* 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.