From cd3334a3c27f9b46eb20d2c7a28d2d0d3e6c4ff8 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Thu, 26 Mar 2026 14:22:42 -0700 Subject: [PATCH] virtio-devices: vhost_user: Enable snapshot/restore for vhost-user-* Enable the DEVICE_STATE protocol feature negotiation for all vhost-user devices (block, fs, net, and generic). Restoring the state (including the backend state if present) and vrings. Signed-off-by: Rob Bradford --- virtio-devices/src/vhost_user/blk.rs | 9 +- virtio-devices/src/vhost_user/fs.rs | 9 +- .../src/vhost_user/generic_vhost_user.rs | 113 ++++++++++-------- virtio-devices/src/vhost_user/net.rs | 9 +- 4 files changed, 86 insertions(+), 54 deletions(-) diff --git a/virtio-devices/src/vhost_user/blk.rs b/virtio-devices/src/vhost_user/blk.rs index 8d6003216..203012b8e 100644 --- a/virtio-devices/src/vhost_user/blk.rs +++ b/virtio-devices/src/vhost_user/blk.rs @@ -72,6 +72,7 @@ impl Blk { vu_num_queues, config, paused, + vring_bases, ) = if let Some(state) = state { info!("Restoring vhost-user-block {id}"); @@ -80,6 +81,8 @@ impl Blk { state.acked_protocol_features, )?; + vu.restore_state(&state)?; + ( state.avail_features, state.acked_features, @@ -87,6 +90,7 @@ impl Blk { state.vu_num_queues, state.config, true, + state.vring_bases, ) } else { // Filling device and vring features VMM supports. @@ -111,7 +115,8 @@ impl Blk { | VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS | VhostUserProtocolFeatures::REPLY_ACK | VhostUserProtocolFeatures::INFLIGHT_SHMFD - | VhostUserProtocolFeatures::LOG_SHMFD; + | VhostUserProtocolFeatures::LOG_SHMFD + | VhostUserProtocolFeatures::DEVICE_STATE; let (acked_features, acked_protocol_features) = vu.negotiate_features_vhost_user(avail_features, avail_protocol_features)?; @@ -160,6 +165,7 @@ impl Blk { num_queues, config, false, + None, ) }; @@ -179,6 +185,7 @@ impl Blk { acked_protocol_features, socket_path: vu_cfg.socket, vu_num_queues, + vring_bases, ..Default::default() }, id, diff --git a/virtio-devices/src/vhost_user/fs.rs b/virtio-devices/src/vhost_user/fs.rs index b5bf495ba..1b8edfe6b 100644 --- a/virtio-devices/src/vhost_user/fs.rs +++ b/virtio-devices/src/vhost_user/fs.rs @@ -101,6 +101,7 @@ impl Fs { vu_num_queues, config, paused, + vring_bases, ) = if let Some(state) = state { info!("Restoring vhost-user-fs {id}"); @@ -109,6 +110,8 @@ impl Fs { state.acked_protocol_features, )?; + vu.restore_state(&state)?; + ( state.avail_features, state.acked_features, @@ -116,6 +119,7 @@ impl Fs { state.vu_num_queues, state.config, true, + state.vring_bases, ) } else { // Filling device and vring features VMM supports. @@ -125,7 +129,8 @@ impl Fs { | VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS | VhostUserProtocolFeatures::REPLY_ACK | VhostUserProtocolFeatures::INFLIGHT_SHMFD - | VhostUserProtocolFeatures::LOG_SHMFD; + | VhostUserProtocolFeatures::LOG_SHMFD + | VhostUserProtocolFeatures::DEVICE_STATE; let (acked_features, acked_protocol_features) = vu.negotiate_features_vhost_user(avail_features, avail_protocol_features)?; @@ -168,6 +173,7 @@ impl Fs { num_queues, config, false, + None, ) }; @@ -187,6 +193,7 @@ impl Fs { acked_protocol_features, socket_path: path.to_string(), vu_num_queues, + vring_bases, ..Default::default() }, id, diff --git a/virtio-devices/src/vhost_user/generic_vhost_user.rs b/virtio-devices/src/vhost_user/generic_vhost_user.rs index 9b4c8e499..da8fe53a8 100644 --- a/virtio-devices/src/vhost_user/generic_vhost_user.rs +++ b/virtio-devices/src/vhost_user/generic_vhost_user.rs @@ -68,64 +68,74 @@ impl GenericVhostUser { // Connect to the vhost-user socket. let mut vu = VhostUserHandle::connect_vhost_user(false, path, num_queues as u64, false)?; - let (avail_features, acked_features, acked_protocol_features, vu_num_queues, paused) = - if let Some(state) = state { - info!("Restoring generic vhost-user {id}"); - vu.set_protocol_features_vhost_user( - state.acked_features, - state.acked_protocol_features, - )?; + let ( + avail_features, + acked_features, + acked_protocol_features, + vu_num_queues, + paused, + vring_bases, + ) = if let Some(state) = state { + info!("Restoring generic vhost-user {id}"); + vu.set_protocol_features_vhost_user( + state.acked_features, + state.acked_protocol_features, + )?; - ( - state.avail_features, - state.acked_features, - state.acked_protocol_features, - state.vu_num_queues, - true, - ) - } else { - let avail_protocol_features = VhostUserProtocolFeatures::CONFIG - | VhostUserProtocolFeatures::MQ - | VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS - | VhostUserProtocolFeatures::REPLY_ACK - | VhostUserProtocolFeatures::INFLIGHT_SHMFD - | VhostUserProtocolFeatures::LOG_SHMFD; + vu.restore_state(&state)?; - let avail_features = super::DEFAULT_VIRTIO_FEATURES; + ( + state.avail_features, + state.acked_features, + state.acked_protocol_features, + state.vu_num_queues, + true, + state.vring_bases, + ) + } else { + let avail_protocol_features = VhostUserProtocolFeatures::CONFIG + | VhostUserProtocolFeatures::MQ + | VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS + | VhostUserProtocolFeatures::REPLY_ACK + | VhostUserProtocolFeatures::INFLIGHT_SHMFD + | VhostUserProtocolFeatures::LOG_SHMFD + | VhostUserProtocolFeatures::DEVICE_STATE; - let (acked_features, acked_protocol_features) = - vu.negotiate_features_vhost_user(avail_features, avail_protocol_features)?; + let avail_features = super::DEFAULT_VIRTIO_FEATURES; - let backend_num_queues = - if acked_protocol_features & VhostUserProtocolFeatures::MQ.bits() != 0 { - vu.socket_handle() - .get_queue_num() - .map_err(Error::VhostUserGetQueueMaxNum)? - as usize - } else { - num_queues - }; + let (acked_features, acked_protocol_features) = + vu.negotiate_features_vhost_user(avail_features, avail_protocol_features)?; - if num_queues > backend_num_queues { - error!( - "generic vhost-user requested too many queues ({num_queues}) \ + let backend_num_queues = + if acked_protocol_features & VhostUserProtocolFeatures::MQ.bits() != 0 { + vu.socket_handle() + .get_queue_num() + .map_err(Error::VhostUserGetQueueMaxNum)? as usize + } else { + num_queues + }; + + if num_queues > backend_num_queues { + error!( + "generic vhost-user requested too many queues ({num_queues}) \ since the backend only supports {backend_num_queues}\n", - ); - return Err(Error::BadQueueNum); - } + ); + return Err(Error::BadQueueNum); + } - ( - acked_features, - // If part of the available features that have been acked, the - // PROTOCOL_FEATURES bit must be already set through the VIRTIO - // acked features as we know the guest would never ack it, thus - // the feature would be lost. - acked_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits(), - acked_protocol_features, - num_queues, - false, - ) - }; + ( + acked_features, + // If part of the available features that have been acked, the + // PROTOCOL_FEATURES bit must be already set through the VIRTIO + // acked features as we know the guest would never ack it, thus + // the feature would be lost. + acked_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits(), + acked_protocol_features, + num_queues, + false, + None, + ) + }; Ok(GenericVhostUser { common: VirtioCommon { @@ -143,6 +153,7 @@ since the backend only supports {backend_num_queues}\n", acked_protocol_features, socket_path: path.to_string(), vu_num_queues, + vring_bases, ..Default::default() }, id, diff --git a/virtio-devices/src/vhost_user/net.rs b/virtio-devices/src/vhost_user/net.rs index ac3719d9d..d05626901 100644 --- a/virtio-devices/src/vhost_user/net.rs +++ b/virtio-devices/src/vhost_user/net.rs @@ -82,6 +82,7 @@ impl Net { vu_num_queues, config, paused, + vring_bases, ) = if let Some(state) = state { info!("Restoring vhost-user-net {id}"); @@ -95,6 +96,8 @@ impl Net { state.acked_protocol_features, )?; + vu.restore_state(&state)?; + // If the control queue feature has been negotiated, let's // increase the number of queues. if state.acked_features & (1 << VIRTIO_NET_F_CTRL_VQ) != 0 { @@ -108,6 +111,7 @@ impl Net { state.vu_num_queues, state.config, true, + state.vring_bases, ) } else { // Filling device and vring features VMM supports. @@ -144,7 +148,8 @@ impl Net { | VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS | VhostUserProtocolFeatures::REPLY_ACK | VhostUserProtocolFeatures::INFLIGHT_SHMFD - | VhostUserProtocolFeatures::LOG_SHMFD; + | VhostUserProtocolFeatures::LOG_SHMFD + | VhostUserProtocolFeatures::DEVICE_STATE; let (mut acked_features, acked_protocol_features) = vu.negotiate_features_vhost_user(avail_features, avail_protocol_features)?; @@ -187,6 +192,7 @@ impl Net { vu_num_queues, config, false, + None, ) }; @@ -208,6 +214,7 @@ impl Net { socket_path: vu_cfg.socket, vu_num_queues, server, + vring_bases, ..Default::default() }, config,