Commit 1276e6c85d for qemu.org
commit 1276e6c85dcc108ff523f93f0fe4f73118394fa0
Author: Alexandr Moshkov <dtalexundeer@yandex-team.ru>
Date: Mon Aug 3 12:28:49 2026 +0500
vhost-user: add GET_VRING_BASE_SKIP_DRAIN message
VHOST_USER_PROTOCOL_F_GET_VRING_BASE_INFLIGHT was introduced to allow
the back-end to suspend in-flight I/O during GET_VRING_BASE instead of
draining it, enabling live migration of in-flight requests. However,
this behaviour is tied to the protocol feature itself - once negotiated,
there is no way for the front-end to tell the back-end to fall back to
the normal drain behaviour on a per-stop basis.
Introduce a separate message VHOST_USER_GET_VRING_BASE_SKIP_DRAIN
(id=45) guarded by a new protocol feature
VHOST_USER_PROTOCOL_F_GET_VRING_BASE_SKIP_DRAIN. The message is
identical to GET_VRING_BASE except that the back-end must immediately suspend all
in-flight I/O and record it in the inflight region. This way the
front-end has explicit per-call control: send GET_VRING_BASE for normal drain,
send GET_VRING_BASE_SKIP_DRAIN when immediate suspend is needed.
The new feature requires both
VHOST_USER_PROTOCOL_F_GET_VRING_BASE_INFLIGHT
and VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD to be negotiated.
Signed-off-by: Alexandr Moshkov <dtalexundeer@yandex-team.ru>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260803072853.2920007-3-dtalexundeer@yandex-team.ru>
diff --git a/docs/interop/vhost-user.rst b/docs/interop/vhost-user.rst
index fbfb9d01c6..c1e02ff7e9 100644
--- a/docs/interop/vhost-user.rst
+++ b/docs/interop/vhost-user.rst
@@ -457,6 +457,7 @@ replies, except for the following requests:
* ``VHOST_USER_GET_FEATURES``
* ``VHOST_USER_GET_PROTOCOL_FEATURES``
* ``VHOST_USER_GET_VRING_BASE``
+* ``VHOST_USER_GET_VRING_BASE_SKIP_DRAIN``
* ``VHOST_USER_SET_LOG_BASE`` (if ``VHOST_USER_PROTOCOL_F_LOG_SHMFD``)
* ``VHOST_USER_GET_INFLIGHT_FD`` (if ``VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD``)
@@ -533,7 +534,7 @@ Rings have two independent states: started/stopped, and enabled/disabled.
Each ring is initialized in a stopped and disabled state. Rings are started
with ``VHOST_USER_SET_VRING_KICK`` (or ``VHOST_USER_VRING_KICK`` if
``VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS`` is negotiated) and stopped with
-``VHOST_USER_GET_VRING_BASE``. A stopped ring enters the started state again
+``VHOST_USER_GET_VRING_BASE`` or ``VHOST_USER_GET_VRING_BASE_SKIP_DRAIN``. A stopped ring enters the started state again
with ``VHOST_USER_SET_VRING_KICK`` (or ``VHOST_USER_VRING_KICK`` if
``VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS`` is negotiated) and the back-end
resumes processing requests.
@@ -1159,6 +1160,7 @@ Protocol features
#define VHOST_USER_PROTOCOL_F_GET_VRING_BASE_INFLIGHT 20
#define VHOST_USER_PROTOCOL_F_GPA_ADDRESSES 21
#define VHOST_USER_PROTOCOL_F_SHMEM 22
+ #define VHOST_USER_PROTOCOL_F_GET_VRING_BASE_SKIP_DRAIN 23
Front-end message types
-----------------------
@@ -1355,17 +1357,11 @@ Front-end message types
set to 0.
By default, the back-end must complete all inflight I/O requests for the
- specified vring before stopping it.
-
- If the ``VHOST_USER_PROTOCOL_F_GET_VRING_BASE_INFLIGHT`` protocol
- feature has been negotiated, the back-end may suspend in-flight I/O
- requests and record them as described in :ref:`Inflight I/O tracking
- <inflight_io_tracking>` instead of completing them before stopping the vring.
- How to suspend an in-flight request depends on the implementation of the back-end
- but it typically can be done by aborting or cancelling the underlying I/O
- request. The ``VHOST_USER_PROTOCOL_F_GET_VRING_BASE_INFLIGHT``
- protocol feature must only be negotiated if
- ``VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD`` is also negotiated.
+ specified vring before stopping it. If the
+ ``VHOST_USER_PROTOCOL_F_GET_VRING_BASE_SKIP_DRAIN`` protocol feature has
+ been negotiated, the front-end may instead use
+ ``VHOST_USER_GET_VRING_BASE_SKIP_DRAIN`` to request the back-end to
+ suspend in-flight I/O immediately.
``VHOST_USER_SET_VRING_KICK``
:id: 12
@@ -1868,6 +1864,28 @@ Front-end message types
* The size may be 0 if the region is unused.
+``VHOST_USER_GET_VRING_BASE_SKIP_DRAIN``
+ :id: 45
+ :equivalent ioctl: N/A
+ :request payload: vring state description
+ :reply payload: vring descriptor index/indices
+
+ This message requires the ``VHOST_USER_PROTOCOL_F_GET_VRING_BASE_SKIP_DRAIN``
+ protocol feature to be negotiated.
+
+ Identical to ``VHOST_USER_GET_VRING_BASE`` except that the back-end
+ must not wait for inflight I/O requests to complete before stopping
+ the vring. Instead, the back-end must immediately suspend all
+ in-flight I/O requests and record them as described in
+ :ref:`Inflight I/O tracking <inflight_io_tracking>`. How to suspend
+ an in-flight request depends on the implementation of the back-end,
+ but it typically can be done by aborting or cancelling the underlying
+ I/O request.
+
+ The ``VHOST_USER_PROTOCOL_F_GET_VRING_BASE_SKIP_DRAIN`` protocol feature
+ must only be negotiated if both ``VHOST_USER_PROTOCOL_F_GET_VRING_BASE_INFLIGHT``
+ and ``VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD`` are also negotiated.
+
Back-end message types
----------------------
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index 2881cec72d..5fb5d61afb 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -101,6 +101,7 @@ typedef enum VhostUserRequest {
VHOST_USER_SET_DEVICE_STATE_FD = 42,
VHOST_USER_CHECK_DEVICE_STATE = 43,
VHOST_USER_GET_SHMEM_CONFIG = 44,
+ VHOST_USER_GET_VRING_BASE_SKIP_DRAIN = 45,
VHOST_USER_MAX
} VhostUserRequest;
@@ -167,6 +168,7 @@ static const char *vhost_req_name(VhostUserRequest req)
VHOST_USER_CASE(GET_SHARED_OBJECT)
VHOST_USER_CASE(SET_DEVICE_STATE_FD)
VHOST_USER_CASE(CHECK_DEVICE_STATE)
+ VHOST_USER_CASE(GET_VRING_BASE_SKIP_DRAIN)
default:
return "<unknown>";
}
@@ -1402,12 +1404,18 @@ static VhostUserHostNotifier *fetch_notifier(VhostUserState *u,
return g_ptr_array_index(u->notifiers, idx);
}
-static int vhost_user_get_vring_base(struct vhost_dev *dev,
- struct vhost_vring_state *ring)
+static int get_vring_base(struct vhost_dev *dev,
+ struct vhost_vring_state *ring,
+ bool skip_drain)
{
int ret;
+ int request = VHOST_USER_GET_VRING_BASE;
+ if (skip_drain) {
+ request = VHOST_USER_GET_VRING_BASE_SKIP_DRAIN;
+ }
+
VhostUserMsg msg = {
- .hdr.request = VHOST_USER_GET_VRING_BASE,
+ .hdr.request = request,
.hdr.flags = VHOST_USER_VERSION,
.payload.state = *ring,
.hdr.size = sizeof(msg.payload.state),
@@ -1427,9 +1435,9 @@ static int vhost_user_get_vring_base(struct vhost_dev *dev,
return ret;
}
- if (msg.hdr.request != VHOST_USER_GET_VRING_BASE) {
+ if (msg.hdr.request != request) {
error_report("Received unexpected msg type. Expected %d received %d",
- VHOST_USER_GET_VRING_BASE, msg.hdr.request);
+ request, msg.hdr.request);
return -EPROTO;
}
@@ -1443,6 +1451,25 @@ static int vhost_user_get_vring_base(struct vhost_dev *dev,
return 0;
}
+static int vhost_user_get_vring_base(struct vhost_dev *dev,
+ struct vhost_vring_state *ring)
+{
+ return get_vring_base(dev, ring, false);
+}
+
+static int vhost_user_get_vring_base_skip_drain(struct vhost_dev *dev,
+ struct vhost_vring_state *ring)
+{
+ bool skip_drain_supported = vhost_user_has_protocol_feature(dev,
+ VHOST_USER_PROTOCOL_F_GET_VRING_BASE_SKIP_DRAIN);
+
+ if (!skip_drain_supported) {
+ return 0;
+ }
+
+ return get_vring_base(dev, ring, true);
+}
+
static int vhost_set_vring_file(struct vhost_dev *dev,
VhostUserRequest request,
struct vhost_vring_file *file)
@@ -2576,6 +2603,12 @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque,
VHOST_USER_PROTOCOL_F_GET_VRING_BASE_INFLIGHT);
}
+ if (!virtio_has_feature(protocol_features,
+ VHOST_USER_PROTOCOL_F_GET_VRING_BASE_INFLIGHT)) {
+ protocol_features &= ~(1ULL <<
+ VHOST_USER_PROTOCOL_F_GET_VRING_BASE_SKIP_DRAIN);
+ }
+
/* final set of protocol features */
u->protocol_features = protocol_features;
err = vhost_user_set_protocol_features(dev, u->protocol_features);
@@ -3434,6 +3467,7 @@ const VhostOps user_ops = {
.vhost_set_vring_num = vhost_user_set_vring_num,
.vhost_set_vring_base = vhost_user_set_vring_base,
.vhost_get_vring_base = vhost_user_get_vring_base,
+ .vhost_get_vring_base_skip_drain = vhost_user_get_vring_base_skip_drain,
.vhost_set_vring_kick = vhost_user_set_vring_kick,
.vhost_set_vring_call = vhost_user_set_vring_call,
.vhost_set_vring_err = vhost_user_set_vring_err,
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index 48fe1805bb..7294d3c42a 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -1505,7 +1505,7 @@ static int do_vhost_virtqueue_stop(struct vhost_dev *dev,
{
if (skip_drain) {
assert(vhost_user_has_protocol_feature(dev,
- VHOST_USER_PROTOCOL_F_GET_VRING_BASE_INFLIGHT));
+ VHOST_USER_PROTOCOL_F_GET_VRING_BASE_SKIP_DRAIN));
}
int vhost_vq_index = dev->vhost_ops->vhost_get_vq_index(dev, idx);
struct vhost_vring_state state = {
diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h
index d878d7b733..daa979a1aa 100644
--- a/include/hw/virtio/vhost-backend.h
+++ b/include/hw/virtio/vhost-backend.h
@@ -190,6 +190,7 @@ typedef struct VhostOps {
vhost_set_vring_num_op vhost_set_vring_num;
vhost_set_vring_base_op vhost_set_vring_base;
vhost_get_vring_base_op vhost_get_vring_base;
+ vhost_get_vring_base_op vhost_get_vring_base_skip_drain;
vhost_set_vring_kick_op vhost_set_vring_kick;
vhost_set_vring_call_op vhost_set_vring_call;
vhost_set_vring_err_op vhost_set_vring_err;
diff --git a/include/hw/virtio/vhost-user.h b/include/hw/virtio/vhost-user.h
index 06c360af18..47c13f8677 100644
--- a/include/hw/virtio/vhost-user.h
+++ b/include/hw/virtio/vhost-user.h
@@ -36,6 +36,7 @@ enum VhostUserProtocolFeature {
VHOST_USER_PROTOCOL_F_GET_VRING_BASE_INFLIGHT = 20,
VHOST_USER_PROTOCOL_F_GPA_ADDRESSES = 21,
VHOST_USER_PROTOCOL_F_SHMEM = 22,
+ VHOST_USER_PROTOCOL_F_GET_VRING_BASE_SKIP_DRAIN = 23,
VHOST_USER_PROTOCOL_F_MAX
};