diff --git a/virtio-devices/src/balloon.rs b/virtio-devices/src/balloon.rs index fb7762de0..abe40e8b5 100644 --- a/virtio-devices/src/balloon.rs +++ b/virtio-devices/src/balloon.rs @@ -365,9 +365,14 @@ impl Balloon { ) -> io::Result { let mut queue_sizes = vec![QUEUE_SIZE; MIN_NUM_QUEUES]; - let (avail_features, acked_features, config) = if let Some(state) = state { + let (avail_features, acked_features, config, paused) = if let Some(state) = state { info!("Restoring virtio-balloon {}", id); - (state.avail_features, state.acked_features, state.config) + ( + state.avail_features, + state.acked_features, + state.config, + true, + ) } else { let mut avail_features = 1u64 << VIRTIO_F_VERSION_1; if deflate_on_oom { @@ -382,7 +387,7 @@ impl Balloon { ..Default::default() }; - (avail_features, 0, config) + (avail_features, 0, config, false) }; if free_page_reporting { @@ -397,6 +402,7 @@ impl Balloon { paused_sync: Some(Arc::new(Barrier::new(2))), queue_sizes, min_queues: MIN_NUM_QUEUES as u16, + paused: Arc::new(AtomicBool::new(paused)), ..Default::default() }, id, diff --git a/virtio-devices/src/block.rs b/virtio-devices/src/block.rs index 466395524..5f5e53d03 100644 --- a/virtio-devices/src/block.rs +++ b/virtio-devices/src/block.rs @@ -442,79 +442,81 @@ impl Block { exit_evt: EventFd, state: Option, ) -> io::Result { - let (disk_nsectors, avail_features, acked_features, config) = if let Some(state) = state { - info!("Restoring virtio-block {}", id); - ( - state.disk_nsectors, - state.avail_features, - state.acked_features, - state.config, - ) - } else { - let disk_size = disk_image.size().map_err(|e| { - io::Error::new( - io::ErrorKind::Other, - format!("Failed getting disk size: {}", e), + let (disk_nsectors, avail_features, acked_features, config, paused) = + if let Some(state) = state { + info!("Restoring virtio-block {}", id); + ( + state.disk_nsectors, + state.avail_features, + state.acked_features, + state.config, + true, ) - })?; - if disk_size % SECTOR_SIZE != 0 { - warn!( - "Disk size {} is not a multiple of sector size {}; \ - the remainder will not be visible to the guest.", - disk_size, SECTOR_SIZE - ); - } - - let mut avail_features = (1u64 << VIRTIO_F_VERSION_1) - | (1u64 << VIRTIO_BLK_F_FLUSH) - | (1u64 << VIRTIO_BLK_F_CONFIG_WCE) - | (1u64 << VIRTIO_BLK_F_BLK_SIZE) - | (1u64 << VIRTIO_BLK_F_TOPOLOGY); - - if iommu { - avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM; - } - - if read_only { - avail_features |= 1u64 << VIRTIO_BLK_F_RO; - } - - let topology = disk_image.topology(); - info!("Disk topology: {:?}", topology); - - let logical_block_size = if topology.logical_block_size > 512 { - topology.logical_block_size } else { - 512 + let disk_size = disk_image.size().map_err(|e| { + io::Error::new( + io::ErrorKind::Other, + format!("Failed getting disk size: {}", e), + ) + })?; + if disk_size % SECTOR_SIZE != 0 { + warn!( + "Disk size {} is not a multiple of sector size {}; \ + the remainder will not be visible to the guest.", + disk_size, SECTOR_SIZE + ); + } + + let mut avail_features = (1u64 << VIRTIO_F_VERSION_1) + | (1u64 << VIRTIO_BLK_F_FLUSH) + | (1u64 << VIRTIO_BLK_F_CONFIG_WCE) + | (1u64 << VIRTIO_BLK_F_BLK_SIZE) + | (1u64 << VIRTIO_BLK_F_TOPOLOGY); + + if iommu { + avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM; + } + + if read_only { + avail_features |= 1u64 << VIRTIO_BLK_F_RO; + } + + let topology = disk_image.topology(); + info!("Disk topology: {:?}", topology); + + let logical_block_size = if topology.logical_block_size > 512 { + topology.logical_block_size + } else { + 512 + }; + + // Calculate the exponent that maps physical block to logical block + let mut physical_block_exp = 0; + let mut size = logical_block_size; + while size < topology.physical_block_size { + physical_block_exp += 1; + size <<= 1; + } + + let disk_nsectors = disk_size / SECTOR_SIZE; + let mut config = VirtioBlockConfig { + capacity: disk_nsectors, + writeback: 1, + blk_size: topology.logical_block_size as u32, + physical_block_exp, + min_io_size: (topology.minimum_io_size / logical_block_size) as u16, + opt_io_size: (topology.optimal_io_size / logical_block_size) as u32, + ..Default::default() + }; + + if num_queues > 1 { + avail_features |= 1u64 << VIRTIO_BLK_F_MQ; + config.num_queues = num_queues as u16; + } + + (disk_nsectors, avail_features, 0, config, false) }; - // Calculate the exponent that maps physical block to logical block - let mut physical_block_exp = 0; - let mut size = logical_block_size; - while size < topology.physical_block_size { - physical_block_exp += 1; - size <<= 1; - } - - let disk_nsectors = disk_size / SECTOR_SIZE; - let mut config = VirtioBlockConfig { - capacity: disk_nsectors, - writeback: 1, - blk_size: topology.logical_block_size as u32, - physical_block_exp, - min_io_size: (topology.minimum_io_size / logical_block_size) as u16, - opt_io_size: (topology.optimal_io_size / logical_block_size) as u32, - ..Default::default() - }; - - if num_queues > 1 { - avail_features |= 1u64 << VIRTIO_BLK_F_MQ; - config.num_queues = num_queues as u16; - } - - (disk_nsectors, avail_features, 0, config) - }; - Ok(Block { common: VirtioCommon { device_type: VirtioDeviceType::Block as u32, @@ -523,6 +525,7 @@ impl Block { paused_sync: Some(Arc::new(Barrier::new(num_queues + 1))), queue_sizes: vec![queue_size; num_queues], min_queues: 1, + paused: Arc::new(AtomicBool::new(paused)), ..Default::default() }, id, diff --git a/virtio-devices/src/console.rs b/virtio-devices/src/console.rs index c3a9f341e..ae38c1b90 100644 --- a/virtio-devices/src/console.rs +++ b/virtio-devices/src/console.rs @@ -628,13 +628,15 @@ impl Console { exit_evt: EventFd, state: Option, ) -> io::Result<(Console, Arc)> { - let (avail_features, acked_features, config, in_buffer) = if let Some(state) = state { + let (avail_features, acked_features, config, in_buffer, paused) = if let Some(state) = state + { info!("Restoring virtio-console {}", id); ( state.avail_features, state.acked_features, state.config, state.in_buffer.into(), + true, ) } else { let mut avail_features = 1u64 << VIRTIO_F_VERSION_1 | 1u64 << VIRTIO_CONSOLE_F_SIZE; @@ -647,6 +649,7 @@ impl Console { 0, VirtioConsoleConfig::default(), VecDeque::new(), + false, ) }; @@ -670,6 +673,7 @@ impl Console { acked_features, paused_sync: Some(Arc::new(Barrier::new(2))), min_queues: NUM_QUEUES as u16, + paused: Arc::new(AtomicBool::new(paused)), ..Default::default() }, id, diff --git a/virtio-devices/src/iommu.rs b/virtio-devices/src/iommu.rs index 22cda055b..a2c35c3f9 100644 --- a/virtio-devices/src/iommu.rs +++ b/virtio-devices/src/iommu.rs @@ -879,34 +879,36 @@ impl Iommu { msi_iova_space: (u64, u64), state: Option, ) -> io::Result<(Self, Arc)> { - let (avail_features, acked_features, endpoints, domains) = if let Some(state) = state { - info!("Restoring virtio-iommu {}", id); - ( - state.avail_features, - state.acked_features, - state.endpoints.into_iter().collect(), - state - .domains - .into_iter() - .map(|(k, v)| { - ( - k, - Domain { - mappings: v.0.into_iter().collect(), - bypass: v.1, - }, - ) - }) - .collect(), - ) - } else { - let avail_features = 1u64 << VIRTIO_F_VERSION_1 - | 1u64 << VIRTIO_IOMMU_F_MAP_UNMAP - | 1u64 << VIRTIO_IOMMU_F_PROBE - | 1u64 << VIRTIO_IOMMU_F_BYPASS_CONFIG; + let (avail_features, acked_features, endpoints, domains, paused) = + if let Some(state) = state { + info!("Restoring virtio-iommu {}", id); + ( + state.avail_features, + state.acked_features, + state.endpoints.into_iter().collect(), + state + .domains + .into_iter() + .map(|(k, v)| { + ( + k, + Domain { + mappings: v.0.into_iter().collect(), + bypass: v.1, + }, + ) + }) + .collect(), + true, + ) + } else { + let avail_features = 1u64 << VIRTIO_F_VERSION_1 + | 1u64 << VIRTIO_IOMMU_F_MAP_UNMAP + | 1u64 << VIRTIO_IOMMU_F_PROBE + | 1u64 << VIRTIO_IOMMU_F_BYPASS_CONFIG; - (avail_features, 0, BTreeMap::new(), BTreeMap::new()) - }; + (avail_features, 0, BTreeMap::new(), BTreeMap::new(), false) + }; let config = VirtioIommuConfig { page_size_mask: VIRTIO_IOMMU_PAGE_SIZE_MASK, @@ -930,6 +932,7 @@ impl Iommu { acked_features, paused_sync: Some(Arc::new(Barrier::new(2))), min_queues: NUM_QUEUES as u16, + paused: Arc::new(AtomicBool::new(paused)), ..Default::default() }, config, diff --git a/virtio-devices/src/mem.rs b/virtio-devices/src/mem.rs index d18c95553..bc3c382c1 100644 --- a/virtio-devices/src/mem.rs +++ b/virtio-devices/src/mem.rs @@ -736,10 +736,15 @@ impl Mem { )); } - let (avail_features, acked_features, config) = if let Some(state) = state { + let (avail_features, acked_features, config, paused) = if let Some(state) = state { info!("Restoring virtio-mem {}", id); *(blocks_state.lock().unwrap()) = state.blocks_state.clone(); - (state.avail_features, state.acked_features, state.config) + ( + state.avail_features, + state.acked_features, + state.config, + true, + ) } else { let mut avail_features = 1u64 << VIRTIO_F_VERSION_1; @@ -779,7 +784,7 @@ impl Mem { ) })?; - (avail_features, 0, config) + (avail_features, 0, config, false) }; let host_fd = region @@ -794,6 +799,7 @@ impl Mem { paused_sync: Some(Arc::new(Barrier::new(2))), queue_sizes: QUEUE_SIZES.to_vec(), min_queues: 1, + paused: Arc::new(AtomicBool::new(paused)), ..Default::default() }, id, diff --git a/virtio-devices/src/net.rs b/virtio-devices/src/net.rs index 5ae12aab0..efd1df99e 100644 --- a/virtio-devices/src/net.rs +++ b/virtio-devices/src/net.rs @@ -451,57 +451,65 @@ impl Net { let mtu = taps[0].mtu().map_err(Error::TapError)? as u16; - let (avail_features, acked_features, config, queue_sizes) = if let Some(state) = state { - info!("Restoring virtio-net {}", id); - ( - state.avail_features, - state.acked_features, - state.config, - state.queue_size, - ) - } else { - let mut avail_features = 1 << VIRTIO_NET_F_CSUM - | 1 << VIRTIO_NET_F_CTRL_GUEST_OFFLOADS - | 1 << VIRTIO_NET_F_GUEST_CSUM - | 1 << VIRTIO_NET_F_GUEST_ECN - | 1 << VIRTIO_NET_F_GUEST_TSO4 - | 1 << VIRTIO_NET_F_GUEST_TSO6 - | 1 << VIRTIO_NET_F_GUEST_UFO - | 1 << VIRTIO_NET_F_HOST_ECN - | 1 << VIRTIO_NET_F_HOST_TSO4 - | 1 << VIRTIO_NET_F_HOST_TSO6 - | 1 << VIRTIO_NET_F_HOST_UFO - | 1 << VIRTIO_NET_F_MTU - | 1 << VIRTIO_RING_F_EVENT_IDX - | 1 << VIRTIO_F_VERSION_1; - - if iommu { - avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM; - } - - avail_features |= 1 << VIRTIO_NET_F_CTRL_VQ; - let queue_num = num_queues + 1; - - let mut config = VirtioNetConfig::default(); - if let Some(mac) = guest_mac { - build_net_config_space( - &mut config, - mac, - num_queues, - Some(mtu), - &mut avail_features, - ); + let (avail_features, acked_features, config, queue_sizes, paused) = + if let Some(state) = state { + info!("Restoring virtio-net {}", id); + ( + state.avail_features, + state.acked_features, + state.config, + state.queue_size, + true, + ) } else { - build_net_config_space_with_mq( - &mut config, - num_queues, - Some(mtu), - &mut avail_features, - ); - } + let mut avail_features = 1 << VIRTIO_NET_F_CSUM + | 1 << VIRTIO_NET_F_CTRL_GUEST_OFFLOADS + | 1 << VIRTIO_NET_F_GUEST_CSUM + | 1 << VIRTIO_NET_F_GUEST_ECN + | 1 << VIRTIO_NET_F_GUEST_TSO4 + | 1 << VIRTIO_NET_F_GUEST_TSO6 + | 1 << VIRTIO_NET_F_GUEST_UFO + | 1 << VIRTIO_NET_F_HOST_ECN + | 1 << VIRTIO_NET_F_HOST_TSO4 + | 1 << VIRTIO_NET_F_HOST_TSO6 + | 1 << VIRTIO_NET_F_HOST_UFO + | 1 << VIRTIO_NET_F_MTU + | 1 << VIRTIO_RING_F_EVENT_IDX + | 1 << VIRTIO_F_VERSION_1; - (avail_features, 0, config, vec![queue_size; queue_num]) - }; + if iommu { + avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM; + } + + avail_features |= 1 << VIRTIO_NET_F_CTRL_VQ; + let queue_num = num_queues + 1; + + let mut config = VirtioNetConfig::default(); + if let Some(mac) = guest_mac { + build_net_config_space( + &mut config, + mac, + num_queues, + Some(mtu), + &mut avail_features, + ); + } else { + build_net_config_space_with_mq( + &mut config, + num_queues, + Some(mtu), + &mut avail_features, + ); + } + + ( + avail_features, + 0, + config, + vec![queue_size; queue_num], + false, + ) + }; Ok(Net { common: VirtioCommon { @@ -511,6 +519,7 @@ impl Net { queue_sizes, paused_sync: Some(Arc::new(Barrier::new((num_queues / 2) + 1))), min_queues: 2, + paused: Arc::new(AtomicBool::new(paused)), ..Default::default() }, id, diff --git a/virtio-devices/src/pmem.rs b/virtio-devices/src/pmem.rs index 2ab3e022d..24a9fca44 100644 --- a/virtio-devices/src/pmem.rs +++ b/virtio-devices/src/pmem.rs @@ -301,9 +301,14 @@ impl Pmem { exit_evt: EventFd, state: Option, ) -> io::Result { - let (avail_features, acked_features, config) = if let Some(state) = state { + let (avail_features, acked_features, config, paused) = if let Some(state) = state { info!("Restoring virtio-pmem {}", id); - (state.avail_features, state.acked_features, state.config) + ( + state.avail_features, + state.acked_features, + state.config, + true, + ) } else { let config = VirtioPmemConfig { start: addr.raw_value().to_le(), @@ -315,7 +320,7 @@ impl Pmem { if iommu { avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM; } - (avail_features, 0, config) + (avail_features, 0, config, false) }; Ok(Pmem { @@ -326,6 +331,7 @@ impl Pmem { avail_features, acked_features, min_queues: 1, + paused: Arc::new(AtomicBool::new(paused)), ..Default::default() }, id, diff --git a/virtio-devices/src/rng.rs b/virtio-devices/src/rng.rs index 2992969f2..7cc10b68b 100644 --- a/virtio-devices/src/rng.rs +++ b/virtio-devices/src/rng.rs @@ -178,9 +178,9 @@ impl Rng { ) -> io::Result { let random_file = File::open(path)?; - let (avail_features, acked_features) = if let Some(state) = state { + let (avail_features, acked_features, paused) = if let Some(state) = state { info!("Restoring virtio-rng {}", id); - (state.avail_features, state.acked_features) + (state.avail_features, state.acked_features, true) } else { let mut avail_features = 1u64 << VIRTIO_F_VERSION_1; @@ -188,7 +188,7 @@ impl Rng { avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM; } - (avail_features, 0) + (avail_features, 0, false) }; Ok(Rng { @@ -199,6 +199,7 @@ impl Rng { avail_features, acked_features, min_queues: 1, + paused: Arc::new(AtomicBool::new(paused)), ..Default::default() }, id, diff --git a/virtio-devices/src/vdpa.rs b/virtio-devices/src/vdpa.rs index a7ed1e51e..cf7a0767e 100644 --- a/virtio-devices/src/vdpa.rs +++ b/virtio-devices/src/vdpa.rs @@ -12,7 +12,10 @@ use anyhow::anyhow; use std::{ collections::BTreeMap, io, result, - sync::{atomic::Ordering, Arc, Mutex}, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, Mutex, + }, }; use thiserror::Error; use versionize::{VersionMap, Versionize, VersionizeResult}; @@ -134,6 +137,7 @@ impl Vdpa { queue_sizes, iova_range, backend_features, + paused, ) = if let Some(state) = state { info!("Restoring vDPA {}", id); @@ -152,6 +156,7 @@ impl Vdpa { last: state.iova_range_last, }, state.backend_features, + true, ) } else { let device_type = vhost.get_device_id().map_err(Error::GetDeviceId)?; @@ -175,6 +180,7 @@ impl Vdpa { vec![queue_size; num_queues as usize], iova_range, backend_features, + false, ) }; @@ -185,6 +191,7 @@ impl Vdpa { avail_features, acked_features, min_queues: num_queues, + paused: Arc::new(AtomicBool::new(paused)), ..Default::default() }, id, diff --git a/virtio-devices/src/vhost_user/blk.rs b/virtio-devices/src/vhost_user/blk.rs index e67568b80..6c1c7c8f9 100644 --- a/virtio-devices/src/vhost_user/blk.rs +++ b/virtio-devices/src/vhost_user/blk.rs @@ -13,6 +13,7 @@ use block_util::VirtioBlockConfig; use seccompiler::SeccompAction; use std::mem; use std::result; +use std::sync::atomic::AtomicBool; use std::sync::{Arc, Barrier, Mutex}; use std::thread; use std::vec::Vec; @@ -79,96 +80,102 @@ impl Blk { let mut vu = VhostUserHandle::connect_vhost_user(false, &vu_cfg.socket, num_queues as u64, false)?; - let (avail_features, acked_features, acked_protocol_features, vu_num_queues, config) = - if let Some(state) = state { - info!("Restoring vhost-user-block {}", id); + let ( + avail_features, + acked_features, + acked_protocol_features, + vu_num_queues, + config, + paused, + ) = if let Some(state) = state { + info!("Restoring vhost-user-block {}", id); - vu.set_protocol_features_vhost_user( - state.acked_features, - state.acked_protocol_features, - )?; + 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, - state.config, - ) - } else { - // Filling device and vring features VMM supports. - let mut avail_features = 1 << VIRTIO_BLK_F_SIZE_MAX - | 1 << VIRTIO_BLK_F_SEG_MAX - | 1 << VIRTIO_BLK_F_GEOMETRY - | 1 << VIRTIO_BLK_F_RO - | 1 << VIRTIO_BLK_F_BLK_SIZE - | 1 << VIRTIO_BLK_F_FLUSH - | 1 << VIRTIO_BLK_F_TOPOLOGY - | 1 << VIRTIO_BLK_F_CONFIG_WCE - | 1 << VIRTIO_BLK_F_DISCARD - | 1 << VIRTIO_BLK_F_WRITE_ZEROES - | DEFAULT_VIRTIO_FEATURES; + ( + state.avail_features, + state.acked_features, + state.acked_protocol_features, + state.vu_num_queues, + state.config, + true, + ) + } else { + // Filling device and vring features VMM supports. + let mut avail_features = 1 << VIRTIO_BLK_F_SIZE_MAX + | 1 << VIRTIO_BLK_F_SEG_MAX + | 1 << VIRTIO_BLK_F_GEOMETRY + | 1 << VIRTIO_BLK_F_RO + | 1 << VIRTIO_BLK_F_BLK_SIZE + | 1 << VIRTIO_BLK_F_FLUSH + | 1 << VIRTIO_BLK_F_TOPOLOGY + | 1 << VIRTIO_BLK_F_CONFIG_WCE + | 1 << VIRTIO_BLK_F_DISCARD + | 1 << VIRTIO_BLK_F_WRITE_ZEROES + | DEFAULT_VIRTIO_FEATURES; - if num_queues > 1 { - avail_features |= 1 << VIRTIO_BLK_F_MQ; - } + if num_queues > 1 { + avail_features |= 1 << VIRTIO_BLK_F_MQ; + } - let avail_protocol_features = VhostUserProtocolFeatures::CONFIG - | VhostUserProtocolFeatures::MQ - | VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS - | VhostUserProtocolFeatures::REPLY_ACK - | VhostUserProtocolFeatures::INFLIGHT_SHMFD - | VhostUserProtocolFeatures::LOG_SHMFD; + let avail_protocol_features = VhostUserProtocolFeatures::CONFIG + | VhostUserProtocolFeatures::MQ + | VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS + | VhostUserProtocolFeatures::REPLY_ACK + | VhostUserProtocolFeatures::INFLIGHT_SHMFD + | VhostUserProtocolFeatures::LOG_SHMFD; - let (acked_features, acked_protocol_features) = - vu.negotiate_features_vhost_user(avail_features, avail_protocol_features)?; + let (acked_features, acked_protocol_features) = + vu.negotiate_features_vhost_user(avail_features, avail_protocol_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 { - DEFAULT_QUEUE_NUMBER - }; + 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 { + DEFAULT_QUEUE_NUMBER + }; - if num_queues > backend_num_queues { - error!("vhost-user-blk requested too many queues ({}) since the backend only supports {}\n", + if num_queues > backend_num_queues { + error!("vhost-user-blk requested too many queues ({}) since the backend only supports {}\n", num_queues, backend_num_queues); - return Err(Error::BadQueueNum); - } + return Err(Error::BadQueueNum); + } - let config_len = mem::size_of::(); - let config_space: Vec = vec![0u8; config_len]; - let (_, config_space) = vu - .socket_handle() - .get_config( - VHOST_USER_CONFIG_OFFSET, - config_len as u32, - VhostUserConfigFlags::WRITABLE, - config_space.as_slice(), - ) - .map_err(Error::VhostUserGetConfig)?; - let mut config = VirtioBlockConfig::default(); - if let Some(backend_config) = VirtioBlockConfig::from_slice(config_space.as_slice()) - { - config = *backend_config; - config.num_queues = num_queues as u16; - } - - ( - 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, - config, + let config_len = mem::size_of::(); + let config_space: Vec = vec![0u8; config_len]; + let (_, config_space) = vu + .socket_handle() + .get_config( + VHOST_USER_CONFIG_OFFSET, + config_len as u32, + VhostUserConfigFlags::WRITABLE, + config_space.as_slice(), ) - }; + .map_err(Error::VhostUserGetConfig)?; + let mut config = VirtioBlockConfig::default(); + if let Some(backend_config) = VirtioBlockConfig::from_slice(config_space.as_slice()) { + config = *backend_config; + config.num_queues = num_queues as u16; + } + + ( + 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, + config, + false, + ) + }; Ok(Blk { common: VirtioCommon { @@ -178,6 +185,7 @@ impl Blk { acked_features, paused_sync: Some(Arc::new(Barrier::new(2))), min_queues: DEFAULT_QUEUE_NUMBER as u16, + paused: Arc::new(AtomicBool::new(paused)), ..Default::default() }, vu_common: VhostUserCommon { diff --git a/virtio-devices/src/vhost_user/fs.rs b/virtio-devices/src/vhost_user/fs.rs index 6cc7159c2..6543ccfc3 100644 --- a/virtio-devices/src/vhost_user/fs.rs +++ b/virtio-devices/src/vhost_user/fs.rs @@ -16,6 +16,7 @@ use seccompiler::SeccompAction; use std::io; use std::os::unix::io::AsRawFd; use std::result; +use std::sync::atomic::AtomicBool; use std::sync::{Arc, Barrier, Mutex}; use std::thread; use versionize::{VersionMap, Versionize, VersionizeResult}; @@ -333,6 +334,7 @@ impl Fs { vu_num_queues, config, slave_req_support, + paused, ) = if let Some(state) = state { info!("Restoring vhost-user-fs {}", id); @@ -348,6 +350,7 @@ impl Fs { state.vu_num_queues, state.config, state.slave_req_support, + true, ) } else { // Filling device and vring features VMM supports. @@ -407,6 +410,7 @@ impl Fs { num_queues, config, slave_req_support, + false, ) }; @@ -418,6 +422,7 @@ impl Fs { queue_sizes: vec![queue_size; num_queues], paused_sync: Some(Arc::new(Barrier::new(2))), min_queues: 1, + paused: Arc::new(AtomicBool::new(paused)), ..Default::default() }, vu_common: VhostUserCommon { diff --git a/virtio-devices/src/vhost_user/net.rs b/virtio-devices/src/vhost_user/net.rs index 177adbd59..1973e26e1 100644 --- a/virtio-devices/src/vhost_user/net.rs +++ b/virtio-devices/src/vhost_user/net.rs @@ -13,6 +13,7 @@ use crate::{GuestMemoryMmap, GuestRegionMmap}; use net_util::{build_net_config_space, CtrlQueue, MacAddr, VirtioNetConfig}; use seccompiler::SeccompAction; use std::result; +use std::sync::atomic::AtomicBool; use std::sync::{Arc, Barrier, Mutex}; use std::thread; use std::vec::Vec; @@ -83,106 +84,113 @@ impl Net { let mut vu = VhostUserHandle::connect_vhost_user(server, &vu_cfg.socket, num_queues as u64, false)?; - let (avail_features, acked_features, acked_protocol_features, vu_num_queues, config) = - if let Some(state) = state { - info!("Restoring vhost-user-net {}", id); + let ( + avail_features, + acked_features, + acked_protocol_features, + vu_num_queues, + config, + paused, + ) = if let Some(state) = state { + info!("Restoring vhost-user-net {}", id); - // The backend acknowledged features must not contain - // VIRTIO_NET_F_MAC since we don't expect the backend - // to handle it. - let backend_acked_features = state.acked_features & !(1 << VIRTIO_NET_F_MAC); + // The backend acknowledged features must not contain + // VIRTIO_NET_F_MAC since we don't expect the backend + // to handle it. + let backend_acked_features = state.acked_features & !(1 << VIRTIO_NET_F_MAC); - vu.set_protocol_features_vhost_user( - backend_acked_features, - state.acked_protocol_features, - )?; + vu.set_protocol_features_vhost_user( + backend_acked_features, + state.acked_protocol_features, + )?; - // 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 { - num_queues += 1; - } + // 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 { + num_queues += 1; + } - ( - state.avail_features, - state.acked_features, - state.acked_protocol_features, - state.vu_num_queues, - state.config, - ) - } else { - // Filling device and vring features VMM supports. - let mut avail_features = 1 << VIRTIO_NET_F_CSUM - | 1 << VIRTIO_NET_F_GUEST_CSUM - | 1 << VIRTIO_NET_F_GUEST_TSO4 - | 1 << VIRTIO_NET_F_GUEST_TSO6 - | 1 << VIRTIO_NET_F_GUEST_ECN - | 1 << VIRTIO_NET_F_GUEST_UFO - | 1 << VIRTIO_NET_F_HOST_TSO4 - | 1 << VIRTIO_NET_F_HOST_TSO6 - | 1 << VIRTIO_NET_F_HOST_ECN - | 1 << VIRTIO_NET_F_HOST_UFO - | 1 << VIRTIO_NET_F_MRG_RXBUF - | 1 << VIRTIO_NET_F_CTRL_VQ - | 1 << VIRTIO_F_RING_EVENT_IDX - | 1 << VIRTIO_F_VERSION_1 - | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits(); + ( + state.avail_features, + state.acked_features, + state.acked_protocol_features, + state.vu_num_queues, + state.config, + true, + ) + } else { + // Filling device and vring features VMM supports. + let mut avail_features = 1 << VIRTIO_NET_F_CSUM + | 1 << VIRTIO_NET_F_GUEST_CSUM + | 1 << VIRTIO_NET_F_GUEST_TSO4 + | 1 << VIRTIO_NET_F_GUEST_TSO6 + | 1 << VIRTIO_NET_F_GUEST_ECN + | 1 << VIRTIO_NET_F_GUEST_UFO + | 1 << VIRTIO_NET_F_HOST_TSO4 + | 1 << VIRTIO_NET_F_HOST_TSO6 + | 1 << VIRTIO_NET_F_HOST_ECN + | 1 << VIRTIO_NET_F_HOST_UFO + | 1 << VIRTIO_NET_F_MRG_RXBUF + | 1 << VIRTIO_NET_F_CTRL_VQ + | 1 << VIRTIO_F_RING_EVENT_IDX + | 1 << VIRTIO_F_VERSION_1 + | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits(); - if mtu.is_some() { - avail_features |= 1u64 << VIRTIO_NET_F_MTU; - } + if mtu.is_some() { + avail_features |= 1u64 << VIRTIO_NET_F_MTU; + } - let mut config = VirtioNetConfig::default(); - build_net_config_space(&mut config, mac_addr, num_queues, mtu, &mut avail_features); + let mut config = VirtioNetConfig::default(); + build_net_config_space(&mut config, mac_addr, num_queues, mtu, &mut avail_features); - let avail_protocol_features = VhostUserProtocolFeatures::MQ - | VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS - | VhostUserProtocolFeatures::REPLY_ACK - | VhostUserProtocolFeatures::INFLIGHT_SHMFD - | VhostUserProtocolFeatures::LOG_SHMFD; + let avail_protocol_features = VhostUserProtocolFeatures::MQ + | VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS + | VhostUserProtocolFeatures::REPLY_ACK + | VhostUserProtocolFeatures::INFLIGHT_SHMFD + | VhostUserProtocolFeatures::LOG_SHMFD; - let (mut acked_features, acked_protocol_features) = - vu.negotiate_features_vhost_user(avail_features, avail_protocol_features)?; + let (mut acked_features, acked_protocol_features) = + vu.negotiate_features_vhost_user(avail_features, avail_protocol_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 { - DEFAULT_QUEUE_NUMBER - }; + 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 { + DEFAULT_QUEUE_NUMBER + }; - if num_queues > backend_num_queues { - error!("vhost-user-net requested too many queues ({}) since the backend only supports {}\n", + if num_queues > backend_num_queues { + error!("vhost-user-net requested too many queues ({}) since the backend only supports {}\n", num_queues, backend_num_queues); - return Err(Error::BadQueueNum); - } + return Err(Error::BadQueueNum); + } - // If the control queue feature has been negotiated, let's increase - // the number of queues. - let vu_num_queues = num_queues; - if acked_features & (1 << VIRTIO_NET_F_CTRL_VQ) != 0 { - num_queues += 1; - } + // If the control queue feature has been negotiated, let's increase + // the number of queues. + let vu_num_queues = num_queues; + if acked_features & (1 << VIRTIO_NET_F_CTRL_VQ) != 0 { + num_queues += 1; + } - // Make sure the virtio feature to set the MAC address is exposed to - // the guest, even if it hasn't been negotiated with the backend. - acked_features |= 1 << VIRTIO_NET_F_MAC; + // Make sure the virtio feature to set the MAC address is exposed to + // the guest, even if it hasn't been negotiated with the backend. + acked_features |= 1 << VIRTIO_NET_F_MAC; - ( - 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, - vu_num_queues, - config, - ) - }; + ( + 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, + vu_num_queues, + config, + false, + ) + }; Ok(Net { id, @@ -193,6 +201,7 @@ impl Net { acked_features, paused_sync: Some(Arc::new(Barrier::new(2))), min_queues: DEFAULT_QUEUE_NUMBER as u16, + paused: Arc::new(AtomicBool::new(paused)), ..Default::default() }, vu_common: VhostUserCommon { diff --git a/virtio-devices/src/vsock/device.rs b/virtio-devices/src/vsock/device.rs index 666c0e2ed..25be9e50e 100644 --- a/virtio-devices/src/vsock/device.rs +++ b/virtio-devices/src/vsock/device.rs @@ -348,16 +348,16 @@ where exit_evt: EventFd, state: Option, ) -> io::Result> { - let (avail_features, acked_features) = if let Some(state) = state { + let (avail_features, acked_features, paused) = if let Some(state) = state { info!("Restoring virtio-vsock {}", id); - (state.avail_features, state.acked_features) + (state.avail_features, state.acked_features, true) } else { let mut avail_features = 1u64 << VIRTIO_F_VERSION_1 | 1u64 << VIRTIO_F_IN_ORDER; if iommu { avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM; } - (avail_features, 0) + (avail_features, 0, false) }; Ok(Vsock { @@ -368,6 +368,7 @@ where paused_sync: Some(Arc::new(Barrier::new(2))), queue_sizes: QUEUE_SIZES.to_vec(), min_queues: NUM_QUEUES as u16, + paused: Arc::new(AtomicBool::new(paused)), ..Default::default() }, id, diff --git a/virtio-devices/src/watchdog.rs b/virtio-devices/src/watchdog.rs index 7062225a1..1c3057b55 100644 --- a/virtio-devices/src/watchdog.rs +++ b/virtio-devices/src/watchdog.rs @@ -216,7 +216,7 @@ impl Watchdog { state: Option, ) -> io::Result { let mut last_ping_time = None; - let (avail_features, acked_features) = if let Some(state) = state { + let (avail_features, acked_features, paused) = if let Some(state) = state { info!("Restoring virtio-watchdog {}", id); // When restoring enable the watchdog if it was previously enabled. @@ -226,9 +226,9 @@ impl Watchdog { last_ping_time = Some(Instant::now()); } - (state.avail_features, state.acked_features) + (state.avail_features, state.acked_features, true) } else { - (1u64 << VIRTIO_F_VERSION_1, 0) + (1u64 << VIRTIO_F_VERSION_1, 0, false) }; let timer_fd = timerfd_create().map_err(|e| { @@ -246,6 +246,7 @@ impl Watchdog { avail_features, acked_features, min_queues: 1, + paused: Arc::new(AtomicBool::new(paused)), ..Default::default() }, id, diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index 31d3dfc6a..377a49a2f 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -4135,7 +4135,6 @@ impl DeviceManager { if let Some(migratable) = &node.migratable { info!("Restoring {} from DeviceManager", node.id); if let Some(snapshot) = snapshot.snapshots.get(&node.id) { - migratable.lock().unwrap().pause()?; migratable.lock().unwrap().restore(*snapshot.clone())?; } else { return Err(MigratableError::Restore(anyhow!(