From: Sergei Heifetz <[email protected]> The virtio specification is not completely clear about seg_max and its relationship with queue_size. Some drivers (for example, the modern Linux kernel driver) rely on seg_max to set the maximum number of segments used in a request. If seg_max is set larger than queue_size, such a driver might overwhelm a virtqueue by trying to send more segments than it can handle. As a result, it either hangs or faults.
One might argue that it is the vhost-user server's responsibility to set a valid seg_max value. However, due to the issue described in the previous paragraph, this value should generally depend on queue_size. That's why it may be necessary to control it on QEMU's side. This patch adds the seg-max-adjust flag. A flag with the same name already exists for virtio-blk (and it exists to solve the same problem, except that here we have a vhost-user server to consult). If seg-max-adjust is set, the final seg_max is the minimum of the value provided by the vhost-user server and (queue_size - 2). It is not enabled by default. Signed-off-by: Sergei Heifetz <[email protected]> Reviewed-by: Michael S. Tsirkin <[email protected]> Signed-off-by: Michael S. Tsirkin <[email protected]> Message-ID: <[email protected]> --- include/hw/virtio/vhost-user-blk.h | 1 + hw/block/vhost-user-blk.c | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/include/hw/virtio/vhost-user-blk.h b/include/hw/virtio/vhost-user-blk.h index 1e41a2bcdf..dee848cfd8 100644 --- a/include/hw/virtio/vhost-user-blk.h +++ b/include/hw/virtio/vhost-user-blk.h @@ -34,6 +34,7 @@ struct VHostUserBlk { struct virtio_blk_config blkcfg; uint16_t num_queues; uint32_t queue_size; + bool seg_max_adjust; struct vhost_dev dev; struct vhost_inflight *inflight; VhostUserState vhost_user; diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c index b2e46eb3f9..2e5b3ae1b1 100644 --- a/hw/block/vhost-user-blk.c +++ b/hw/block/vhost-user-blk.c @@ -66,6 +66,12 @@ static void vhost_user_blk_update_config(VirtIODevice *vdev, uint8_t *config) /* Our num_queues overrides the device backend */ virtio_stw_p(vdev, &s->blkcfg.num_queues, s->num_queues); + if (s->seg_max_adjust) { + uint32_t seg_max = MIN(s->blkcfg.seg_max, s->queue_size - 2); + + virtio_stl_p(vdev, &s->blkcfg.seg_max, seg_max); + } + memcpy(config, &s->blkcfg, vdev->config_len); } @@ -489,6 +495,10 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp) error_setg(errp, "queue size must be non-zero"); return; } + if (s->queue_size < 4 && s->seg_max_adjust) { + error_setg(errp, "queue size must be >= 4 when seg-max-adjust is set"); + return; + } if (s->queue_size > VIRTQUEUE_MAX_SIZE) { error_setg(errp, "queue size must not exceed %d", VIRTQUEUE_MAX_SIZE); @@ -624,6 +634,8 @@ static const Property vhost_user_blk_properties[] = { DEFINE_PROP_UINT16("num-queues", VHostUserBlk, num_queues, VHOST_USER_BLK_AUTO_NUM_QUEUES), DEFINE_PROP_UINT32("queue-size", VHostUserBlk, queue_size, 128), + DEFINE_PROP_BOOL("seg-max-adjust", VHostUserBlk, seg_max_adjust, + false), DEFINE_PROP_BIT64("config-wce", VHostUserBlk, parent_obj.host_features, VIRTIO_BLK_F_CONFIG_WCE, true), DEFINE_PROP_BIT64("discard", VHostUserBlk, parent_obj.host_features, -- MST
