* [PATCH 0/2] virtio_blk: fit requests to virtqueues without indirect descriptors
@ 2026-11-01 16:53 Stjepan Poljak
2026-11-01 16:53 ` [PATCH 1/2] virtio_blk: fail the probe when a virtqueue is too small for a request Stjepan Poljak
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Stjepan Poljak @ 2026-11-01 16:53 UTC (permalink / raw)
To: Michael S. Tsirkin, Jason Wang, Jens Axboe
Cc: Paolo Bonzini, Stefan Hajnoczi, Eugenio Pérez, Xuan Zhuo,
virtualization, linux-block, linux-kernel
A virtio-blk device without VIRTIO_RING_F_INDIRECT_DESC needs one ring
descriptor per segment of a request, plus one for the header and one for
the status. virtio_blk takes max_segments from the device's seg_max
without checking that against the size of the virtqueues, so a device
that offers a seg_max above its queue size minus two gets requests that
can never be added to the ring. virtqueue_add_split() warns, returns
-ENOSPC, and the request waits for ring space forever. A virtqueue with
fewer than three descriptors cannot hold any request, and depending on
the queue depth the probe then either fails without saying why or hangs
on the first I/O.
I ran into this while writing the virtio-blk device of Hearth, a small
KVM-based VMM, with queue sizes and seg_max values like these:
virtqueue seg_max queue_depth before after
--------- ------- ----------- ------------------------ -------------------
128 126 default boots boots
128 127 default WARN, hangs loading init boots, 126 segments
128 254 default WARN, hangs loading init boots, 126 segments
1 - default -EINVAL, no reason given -EINVAL, message
1 - 128 WARN, hangs on first I/O -EINVAL, message
where the message is "virtqueue size 1 is too small for a request". A
virtqueue of 2 is rejected the same way, with either queue depth.
Patch 1 checks the virtqueue sizes in virtblk_probe(), before the queue
depth is derived from them, and fails the probe with a reason when one
is too small for a request, whatever the queue depth: the last two rows
and the virtqueue of 2. Patch 2 then clamps max_segments to the smallest
virtqueue minus two when indirect descriptors are not negotiated: the
seg_max rows.
The seg_max case also reproduces with QEMU, which with seg-max-adjust=off
offers seg_max 126 whatever the queue size:
-device virtio-blk-pci,drive=d0,indirect_desc=off,queue-size=64,seg-max-adjust=off
Before the series this warns and hangs loading init, after it the guest
boots with 62 segments. With QEMU's defaults nothing changes.
The seg_max cases were tested by booting a busybox guest that reads its
disk with O_DIRECT in 1 MiB blocks, the others by looking at the probe
messages, on v7.3-19954-gcbbddb6b08cc, x86_64.
These patches were written with the help of an AI assistant, as the
Assisted-by tags say. I reviewed and tested them, and I can answer for
every line.
Stjepan Poljak (2):
virtio_blk: fail the probe when a virtqueue is too small for a request
virtio_blk: fit max_segments to the virtqueue without indirect
descriptors
drivers/block/virtio_blk.c | 37 +++++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
--
2.53.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 1/2] virtio_blk: fail the probe when a virtqueue is too small for a request
2026-11-01 16:53 [PATCH 0/2] virtio_blk: fit requests to virtqueues without indirect descriptors Stjepan Poljak
@ 2026-11-01 16:53 ` Stjepan Poljak
2026-11-01 16:53 ` [PATCH 2/2] virtio_blk: fit max_segments to the virtqueue without indirect descriptors Stjepan Poljak
2026-11-02 12:59 ` [PATCH 0/2] virtio_blk: fit requests to virtqueues " Michael S. Tsirkin
2 siblings, 0 replies; 4+ messages in thread
From: Stjepan Poljak @ 2026-11-01 16:53 UTC (permalink / raw)
To: Michael S. Tsirkin, Jason Wang, Jens Axboe
Cc: Paolo Bonzini, Stefan Hajnoczi, Eugenio Pérez, Xuan Zhuo,
virtualization, linux-block, linux-kernel
Without VIRTIO_RING_F_INDIRECT_DESC a request takes at least three ring
descriptors: the header, one segment and the status. Nothing checks
that the virtqueues hold that many, and a smaller one fails in a way
that depends on the queue depth. With the default depth, num_free / 2,
a virtqueue of one descriptor gives a depth of 0, which
blk_mq_alloc_tag_set() refuses, and the probe fails without saying why:
virtio_blk virtio0: probe with driver virtio_blk failed with error -22
With a depth of at least one, from a virtqueue of two or from
virtio_blk.queue_depth, the probe succeeds and the first request never
fits: virtqueue_add_split() warns and returns -ENOSPC, and the hardware
queue is stopped to wait for descriptors that even an empty ring does
not have.
Check the virtqueue sizes right after the virtqueues are set up, before
the queue depth is derived from them, and fail the probe with a reason
when the smallest one cannot hold a request:
virtio_blk virtio0: virtqueue size 1 is too small for a request
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Stjepan Poljak <stjepan.poljak@gmail.com>
---
drivers/block/virtio_blk.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index c8bfbf8435ac..0a2af22f2365 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -1244,6 +1244,18 @@ static const struct blk_mq_ops virtio_mq_ops = {
static unsigned int virtblk_queue_depth;
module_param_named(queue_depth, virtblk_queue_depth, uint, 0444);
+/* The size of the smallest virtqueue, which every request has to fit. */
+static unsigned int virtblk_min_vring_size(struct virtio_blk *vblk)
+{
+ unsigned int vring_size = UINT_MAX;
+ int i;
+
+ for (i = 0; i < vblk->num_vqs; i++)
+ vring_size = min(vring_size,
+ virtqueue_get_vring_size(vblk->vqs[i].vq));
+ return vring_size;
+}
+
static int virtblk_read_limits(struct virtio_blk *vblk,
struct queue_limits *lim)
{
@@ -1474,6 +1486,21 @@ static int virtblk_probe(struct virtio_device *vdev)
if (err)
goto out_free_vblk;
+ /*
+ * Without indirect descriptors the smallest request, a header, one
+ * segment and the status, takes three descriptors. Check before the
+ * queue depth is derived from the ring, which would come out as 0
+ * for a ring of one and fail without saying why.
+ */
+ if (!virtio_has_feature(vdev, VIRTIO_RING_F_INDIRECT_DESC) &&
+ virtblk_min_vring_size(vblk) < 3) {
+ dev_err(&vdev->dev,
+ "virtqueue size %u is too small for a request\n",
+ virtblk_min_vring_size(vblk));
+ err = -EINVAL;
+ goto out_free_vq;
+ }
+
/* Default queue sizing is to fill the ring. */
if (!virtblk_queue_depth) {
queue_depth = vblk->vqs[0].vq->num_free;
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 2/2] virtio_blk: fit max_segments to the virtqueue without indirect descriptors
2026-11-01 16:53 [PATCH 0/2] virtio_blk: fit requests to virtqueues without indirect descriptors Stjepan Poljak
2026-11-01 16:53 ` [PATCH 1/2] virtio_blk: fail the probe when a virtqueue is too small for a request Stjepan Poljak
@ 2026-11-01 16:53 ` Stjepan Poljak
2026-11-02 12:59 ` [PATCH 0/2] virtio_blk: fit requests to virtqueues " Michael S. Tsirkin
2 siblings, 0 replies; 4+ messages in thread
From: Stjepan Poljak @ 2026-11-01 16:53 UTC (permalink / raw)
To: Michael S. Tsirkin, Jason Wang, Jens Axboe
Cc: Paolo Bonzini, Stefan Hajnoczi, Eugenio Pérez, Xuan Zhuo,
virtualization, linux-block, linux-kernel
virtblk_read_limits() takes max_segments from the device's seg_max and
only clamps it to VIRTIO_BLK_MAX_SG_ELEMS - 2, which fits the largest
virtqueue the spec allows, not the one the device has. Without
VIRTIO_RING_F_INDIRECT_DESC every segment takes a descriptor in the
ring, next to the header and the status. A device that offers a seg_max
above its queue size minus two thus lets the block layer build requests
that can never be added: virtqueue_add_split() warns that the request
is larger than the ring and returns -ENOSPC, and the hardware queue is
stopped to wait for descriptors that even an empty ring does not have.
The I/O hangs.
Seen with a device that offers seg_max 254 on a 128-entry queue without
indirect descriptors, where the boot hangs loading /sbin/init:
WARNING: drivers/virtio/virtio_ring.c:688 at virtqueue_add_split+0x6c0/0x850
...
virtqueue_add_sgs+0xd1/0x120
virtblk_add_req+0xe2/0x140
virtio_queue_rq+0x79/0x1a0
With seg_max 126 it boots, with 127 it hangs.
Without indirect descriptors, clamp max_segments to the size of the
smallest virtqueue minus two. The previous commit makes the probe fail
for a virtqueue smaller than three, so at least one segment is left.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Stjepan Poljak <stjepan.poljak@gmail.com>
---
drivers/block/virtio_blk.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 0a2af22f2365..5f05aaa079c2 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -1280,6 +1280,16 @@ static int virtblk_read_limits(struct virtio_blk *vblk,
/* Prevent integer overflows and honor max vq size */
sg_elems = min_t(u32, sg_elems, VIRTIO_BLK_MAX_SG_ELEMS - 2);
+ /*
+ * Without indirect descriptors a request takes one ring descriptor
+ * per segment, plus one for the header and one for the status, so
+ * it has to fit in the smallest of the virtqueues. A larger request
+ * would never fit and would wait for ring space forever.
+ * virtblk_probe() made sure every virtqueue holds at least three.
+ */
+ if (!virtio_has_feature(vdev, VIRTIO_RING_F_INDIRECT_DESC))
+ sg_elems = min(sg_elems, virtblk_min_vring_size(vblk) - 2);
+
/* We can handle whatever the host told us to handle. */
lim->max_segments = sg_elems;
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH 0/2] virtio_blk: fit requests to virtqueues without indirect descriptors
2026-11-01 16:53 [PATCH 0/2] virtio_blk: fit requests to virtqueues without indirect descriptors Stjepan Poljak
2026-11-01 16:53 ` [PATCH 1/2] virtio_blk: fail the probe when a virtqueue is too small for a request Stjepan Poljak
2026-11-01 16:53 ` [PATCH 2/2] virtio_blk: fit max_segments to the virtqueue without indirect descriptors Stjepan Poljak
@ 2026-11-02 12:59 ` Michael S. Tsirkin
2 siblings, 0 replies; 4+ messages in thread
From: Michael S. Tsirkin @ 2026-11-02 12:59 UTC (permalink / raw)
To: Stjepan Poljak
Cc: Jason Wang, Jens Axboe, Paolo Bonzini, Stefan Hajnoczi,
Eugenio Pérez, Xuan Zhuo, virtualization, linux-block,
linux-kernel
On Sun, Nov 01, 2026 at 05:53:05PM +0100, Stjepan Poljak wrote:
> A virtio-blk device without VIRTIO_RING_F_INDIRECT_DESC needs one ring
> descriptor per segment of a request, plus one for the header and one for
> the status. virtio_blk takes max_segments from the device's seg_max
> without checking that against the size of the virtqueues, so a device
> that offers a seg_max above its queue size minus two gets requests that
> can never be added to the ring. virtqueue_add_split() warns, returns
> -ENOSPC, and the request waits for ring space forever. A virtqueue with
> fewer than three descriptors cannot hold any request, and depending on
> the queue depth the probe then either fails without saying why or hangs
> on the first I/O.
Thanks, I applied
Message-ID: <20260918135120.1261150-1-sergiiushakov@google.com>
to fix this, as it seemed like a more comprehensive fix.
> I ran into this while writing the virtio-blk device of Hearth, a small
> KVM-based VMM, with queue sizes and seg_max values like these:
>
> virtqueue seg_max queue_depth before after
> --------- ------- ----------- ------------------------ -------------------
> 128 126 default boots boots
> 128 127 default WARN, hangs loading init boots, 126 segments
> 128 254 default WARN, hangs loading init boots, 126 segments
> 1 - default -EINVAL, no reason given -EINVAL, message
> 1 - 128 WARN, hangs on first I/O -EINVAL, message
>
> where the message is "virtqueue size 1 is too small for a request". A
> virtqueue of 2 is rejected the same way, with either queue depth.
>
> Patch 1 checks the virtqueue sizes in virtblk_probe(), before the queue
> depth is derived from them, and fails the probe with a reason when one
> is too small for a request, whatever the queue depth: the last two rows
> and the virtqueue of 2. Patch 2 then clamps max_segments to the smallest
> virtqueue minus two when indirect descriptors are not negotiated: the
> seg_max rows.
>
> The seg_max case also reproduces with QEMU, which with seg-max-adjust=off
> offers seg_max 126 whatever the queue size:
>
> -device virtio-blk-pci,drive=d0,indirect_desc=off,queue-size=64,seg-max-adjust=off
>
> Before the series this warns and hangs loading init, after it the guest
> boots with 62 segments. With QEMU's defaults nothing changes.
>
> The seg_max cases were tested by booting a busybox guest that reads its
> disk with O_DIRECT in 1 MiB blocks, the others by looking at the probe
> messages, on v7.3-19954-gcbbddb6b08cc, x86_64.
>
> These patches were written with the help of an AI assistant, as the
> Assisted-by tags say. I reviewed and tested them, and I can answer for
> every line.
>
> Stjepan Poljak (2):
> virtio_blk: fail the probe when a virtqueue is too small for a request
> virtio_blk: fit max_segments to the virtqueue without indirect
> descriptors
>
> drivers/block/virtio_blk.c | 37 +++++++++++++++++++++++++++++++++++++
> 1 file changed, 37 insertions(+)
>
> --
> 2.53.0
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-11-02 12:59 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-01 16:53 [PATCH 0/2] virtio_blk: fit requests to virtqueues without indirect descriptors Stjepan Poljak
2026-11-01 16:53 ` [PATCH 1/2] virtio_blk: fail the probe when a virtqueue is too small for a request Stjepan Poljak
2026-11-01 16:53 ` [PATCH 2/2] virtio_blk: fit max_segments to the virtqueue without indirect descriptors Stjepan Poljak
2026-11-02 12:59 ` [PATCH 0/2] virtio_blk: fit requests to virtqueues " Michael S. Tsirkin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox