Skip to content

Commit dfe97ec

Browse files
committed
use userspace bounce buffers if no swiotlb is configured
Mark vhost-user and async block engine as incompatible, as for vhost-user-blk, we would need to communicate the need for bounce buffers to the backend somehow, and for the async block engine we would need to somehow keep the bounce buffers around until io_uring finishes requests (which is not impossible, but complicated and not needed for now). Signed-off-by: Patrick Roy <[email protected]>
1 parent 39228c0 commit dfe97ec

File tree

8 files changed

+101
-69
lines changed

8 files changed

+101
-69
lines changed

src/vmm/src/builder.rs

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -322,16 +322,24 @@ pub fn build_microvm_for_boot(
322322
&mut boot_cmdline,
323323
vm_resources.block.devices.iter(),
324324
event_manager,
325+
secret_free,
325326
)?;
326327
attach_net_devices(
327328
&mut vmm,
328329
&mut boot_cmdline,
329330
vm_resources.net_builder.iter(),
330331
event_manager,
332+
secret_free,
331333
)?;
332334

333335
if let Some(unix_vsock) = vm_resources.vsock.get() {
334-
attach_unixsock_vsock_device(&mut vmm, &mut boot_cmdline, unix_vsock, event_manager)?;
336+
attach_unixsock_vsock_device(
337+
&mut vmm,
338+
&mut boot_cmdline,
339+
unix_vsock,
340+
event_manager,
341+
secret_free,
342+
)?;
335343
}
336344

337345
if let Some(entropy) = vm_resources.entropy.get() {
@@ -708,14 +716,19 @@ fn attach_virtio_device<T: 'static + VirtioDevice + MutEventSubscriber + Debug>(
708716
device: Arc<Mutex<T>>,
709717
cmdline: &mut LoaderKernelCmdline,
710718
is_vhost_user: bool,
719+
needs_bouncing: bool,
711720
) -> Result<(), MmioError> {
712721
event_manager.add_subscriber(device.clone());
713722

714723
// We have to enable swiotlb as part of the boot process, because the device objects
715724
// themselves are created when the corresponding PUT API calls are made, and at that
716725
// point we don't know yet whether swiotlb should be enabled or not.
717726
if vmm.vm.has_swiotlb() {
727+
// If swiotlb was requested via the API, enable it independently of whether
728+
// bouncing is actually required (e.g. of whether secret hiding is enabled)
718729
device.lock().unwrap().force_swiotlb();
730+
} else if needs_bouncing {
731+
device.lock().unwrap().force_userspace_bounce_buffers();
719732
}
720733

721734
// The device mutex mustn't be locked here otherwise it will deadlock.
@@ -773,6 +786,7 @@ fn attach_entropy_device(
773786
entropy_device.clone(),
774787
cmdline,
775788
false,
789+
false,
776790
)
777791
}
778792

@@ -781,6 +795,7 @@ fn attach_block_devices<'a, I: Iterator<Item = &'a Arc<Mutex<Block>>> + Debug>(
781795
cmdline: &mut LoaderKernelCmdline,
782796
blocks: I,
783797
event_manager: &mut EventManager,
798+
needs_bouncing: bool,
784799
) -> Result<(), StartMicrovmError> {
785800
for block in blocks {
786801
let (id, is_vhost_user) = {
@@ -805,6 +820,7 @@ fn attach_block_devices<'a, I: Iterator<Item = &'a Arc<Mutex<Block>>> + Debug>(
805820
block.clone(),
806821
cmdline,
807822
is_vhost_user,
823+
needs_bouncing,
808824
)?;
809825
}
810826
Ok(())
@@ -815,11 +831,20 @@ fn attach_net_devices<'a, I: Iterator<Item = &'a Arc<Mutex<Net>>> + Debug>(
815831
cmdline: &mut LoaderKernelCmdline,
816832
net_devices: I,
817833
event_manager: &mut EventManager,
834+
needs_bouncing: bool,
818835
) -> Result<(), StartMicrovmError> {
819836
for net_device in net_devices {
820837
let id = net_device.lock().expect("Poisoned lock").id().clone();
821838
// The device mutex mustn't be locked here otherwise it will deadlock.
822-
attach_virtio_device(event_manager, vmm, id, net_device.clone(), cmdline, false)?;
839+
attach_virtio_device(
840+
event_manager,
841+
vmm,
842+
id,
843+
net_device.clone(),
844+
cmdline,
845+
false,
846+
needs_bouncing,
847+
)?;
823848
}
824849
Ok(())
825850
}
@@ -829,10 +854,19 @@ fn attach_unixsock_vsock_device(
829854
cmdline: &mut LoaderKernelCmdline,
830855
unix_vsock: &Arc<Mutex<Vsock<VsockUnixBackend>>>,
831856
event_manager: &mut EventManager,
857+
needs_bouncing: bool,
832858
) -> Result<(), MmioError> {
833859
let id = String::from(unix_vsock.lock().expect("Poisoned lock").id());
834860
// The device mutex mustn't be locked here otherwise it will deadlock.
835-
attach_virtio_device(event_manager, vmm, id, unix_vsock.clone(), cmdline, false)
861+
attach_virtio_device(
862+
event_manager,
863+
vmm,
864+
id,
865+
unix_vsock.clone(),
866+
cmdline,
867+
false,
868+
needs_bouncing,
869+
)
836870
}
837871

838872
fn attach_balloon_device(
@@ -843,7 +877,15 @@ fn attach_balloon_device(
843877
) -> Result<(), MmioError> {
844878
let id = String::from(balloon.lock().expect("Poisoned lock").id());
845879
// The device mutex mustn't be locked here otherwise it will deadlock.
846-
attach_virtio_device(event_manager, vmm, id, balloon.clone(), cmdline, false)
880+
attach_virtio_device(
881+
event_manager,
882+
vmm,
883+
id,
884+
balloon.clone(),
885+
cmdline,
886+
false,
887+
false,
888+
)
847889
}
848890

849891
// Adds `O_NONBLOCK` to the stdout flags.
@@ -1019,6 +1061,7 @@ pub(crate) mod tests {
10191061
cmdline,
10201062
block_dev_configs.devices.iter(),
10211063
event_manager,
1064+
false,
10221065
)
10231066
.unwrap();
10241067
block_files
@@ -1033,7 +1076,7 @@ pub(crate) mod tests {
10331076
let mut net_builder = NetBuilder::new();
10341077
net_builder.build(net_config).unwrap();
10351078

1036-
let res = attach_net_devices(vmm, cmdline, net_builder.iter(), event_manager);
1079+
let res = attach_net_devices(vmm, cmdline, net_builder.iter(), event_manager, false);
10371080
res.unwrap();
10381081
}
10391082

@@ -1054,7 +1097,7 @@ pub(crate) mod tests {
10541097
Arc::new(Mutex::new(mmds)),
10551098
);
10561099

1057-
attach_net_devices(vmm, cmdline, net_builder.iter(), event_manager).unwrap();
1100+
attach_net_devices(vmm, cmdline, net_builder.iter(), event_manager, false).unwrap();
10581101
}
10591102

10601103
pub(crate) fn insert_vsock_device(
@@ -1067,7 +1110,7 @@ pub(crate) mod tests {
10671110
let vsock = VsockBuilder::create_unixsock_vsock(vsock_config).unwrap();
10681111
let vsock = Arc::new(Mutex::new(vsock));
10691112

1070-
attach_unixsock_vsock_device(vmm, cmdline, &vsock, event_manager).unwrap();
1113+
attach_unixsock_vsock_device(vmm, cmdline, &vsock, event_manager, false).unwrap();
10711114

10721115
assert!(
10731116
vmm.mmio_device_manager

src/vmm/src/devices/virtio/block/vhost_user/device.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ impl<T: VhostUserHandleBackend + Send + 'static> VirtioDevice for VhostUserBlock
302302

303303
fn force_userspace_bounce_buffers(&mut self) {
304304
// Nothing Firecracker can do about this, the backend would need to do the bouncing
305+
panic!("vhost-user-blk is incompatible with userspace bounce buffers")
305306
}
306307

307308
fn userspace_bounce_buffers(&self) -> bool {

src/vmm/src/devices/virtio/block/virtio/device.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,9 @@ impl VirtioDevice for VirtioBlock {
586586

587587
fn force_userspace_bounce_buffers(&mut self) {
588588
match self.disk.file_engine {
589-
FileEngine::Async(_) => panic!("No idea how this is supposed to work for io_uring"),
589+
FileEngine::Async(_) => {
590+
panic!("async engine is incompatible with userspace bounce buffers")
591+
}
590592
FileEngine::Sync(ref mut engine) => engine.start_bouncing(),
591593
}
592594
}

src/vmm/src/devices/virtio/net/device.rs

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
// found in the THIRD-PARTY file.
77

88
use std::collections::VecDeque;
9-
use std::io::{ErrorKind, Read, Write};
9+
use std::io::{Read, Write};
1010
use std::mem::{self};
1111
use std::net::Ipv4Addr;
1212
use std::sync::{Arc, Mutex};
@@ -561,17 +561,12 @@ impl Net {
561561

562562
let _metric = net_metrics.tap_write_agg.record_latency_metrics();
563563
match Self::write_tap(tap, frame_iovec, bb) {
564-
Ok(written) if written == frame_iovec.len() as usize => {
564+
Ok(_) => {
565565
let len = u64::from(frame_iovec.len());
566566
net_metrics.tx_bytes_count.add(len);
567567
net_metrics.tx_packets_count.inc();
568568
net_metrics.tx_count.inc();
569569
}
570-
Ok(how_many) => error!(
571-
"Failed to write full frame to tap! {}/{}",
572-
how_many,
573-
frame_iovec.len()
574-
),
575570
Err(err) => {
576571
error!("Failed to write to tap: {:?}", err);
577572
net_metrics.tap_write_fails.inc();
@@ -844,11 +839,10 @@ impl Net {
844839
};
845840

846841
if self.userspace_bouncing {
847-
let how_many = match self.tap.tap_file.read(self.userspace_buffer.as_mut_slice()) {
848-
Err(ioe) if ioe.kind() == ErrorKind::WouldBlock => return Ok(0),
849-
Err(err) => return Err(err),
850-
Ok(read) => read,
851-
};
842+
let how_many = self
843+
.tap
844+
.tap_file
845+
.read(self.userspace_buffer.as_mut_slice())?;
852846

853847
assert!(how_many <= MAX_BUFFER_SIZE);
854848

@@ -877,11 +871,7 @@ impl Net {
877871

878872
Ok(how_many)
879873
} else {
880-
match self.tap.read_iovec(slice) {
881-
Err(ioe) if ioe.kind() == ErrorKind::WouldBlock => Ok(0),
882-
Err(err) => Err(err),
883-
Ok(read) => Ok(read),
884-
}
874+
self.tap.read_iovec(slice)
885875
}
886876
}
887877

src/vmm/src/resources.rs

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize};
1010

1111
use crate::cpu_config::templates::CustomCpuTemplate;
1212
use crate::device_manager::persist::SharedDeviceType;
13+
use crate::devices::virtio::block::device::Block;
1314
use crate::logger::info;
1415
use crate::mmds;
1516
use crate::mmds::data_store::{Mmds, MmdsVersion};
@@ -111,14 +112,6 @@ pub struct VmResources {
111112
}
112113

113114
impl VmResources {
114-
/// Whether this [`VmResources`] object contains any devices that require host kernel access
115-
/// into guest memory.
116-
pub fn has_any_io_devices(&self) -> bool {
117-
!self.block.devices.is_empty()
118-
|| self.vsock.get().is_some()
119-
|| self.net_builder.iter().next().is_some()
120-
}
121-
122115
/// Configures Vmm resources as described by the `config_json` param.
123116
pub fn from_json(
124117
config_json: &str,
@@ -270,16 +263,6 @@ impl VmResources {
270263
return Err(MachineConfigError::IncompatibleBalloonSize);
271264
}
272265

273-
if self.has_any_io_devices()
274-
&& self.machine_config.mem_config.secret_free
275-
&& !self.swiotlb_used()
276-
{
277-
return Err(MachineConfigError::Incompatible(
278-
"secret freedom",
279-
"I/O without swiotlb",
280-
));
281-
}
282-
283266
if self.balloon.get().is_some() && updated.huge_pages != HugePageConfig::None {
284267
return Err(MachineConfigError::Incompatible(
285268
"balloon device",
@@ -292,6 +275,22 @@ impl VmResources {
292275
"secret freedom",
293276
));
294277
}
278+
if !self.swiotlb_used() && updated.mem_config.secret_free {
279+
if self.vhost_user_devices_used() {
280+
return Err(MachineConfigError::Incompatible(
281+
"vhost-user devices",
282+
"userspace bounce buffers",
283+
));
284+
}
285+
286+
if self.async_block_engine_used() {
287+
return Err(MachineConfigError::Incompatible(
288+
"async block engine",
289+
"userspace bounce buffers",
290+
));
291+
}
292+
}
293+
295294
self.machine_config = updated;
296295

297296
Ok(())
@@ -377,13 +376,18 @@ impl VmResources {
377376
&mut self,
378377
block_device_config: BlockDeviceConfig,
379378
) -> Result<(), DriveError> {
380-
if self.has_any_io_devices()
381-
&& self.machine_config.mem_config.secret_free
382-
&& !self.swiotlb_used()
383-
{
384-
return Err(DriveError::SecretFreeWithoutSwiotlb);
379+
if self.machine_config.mem_config.secret_free && !self.swiotlb_used() {
380+
if block_device_config.file_engine_type == Some(FileEngineType::Async) {
381+
return Err(DriveError::IncompatibleWithUserspaceBounceBuffers(
382+
"async file engine",
383+
));
384+
}
385+
if block_device_config.socket.is_some() {
386+
return Err(DriveError::IncompatibleWithUserspaceBounceBuffers(
387+
"vhost-user-blk",
388+
));
389+
}
385390
}
386-
387391
self.block.insert(block_device_config)
388392
}
389393

@@ -392,26 +396,12 @@ impl VmResources {
392396
&mut self,
393397
body: NetworkInterfaceConfig,
394398
) -> Result<(), NetworkInterfaceError> {
395-
if self.has_any_io_devices()
396-
&& self.machine_config.mem_config.secret_free
397-
&& !self.swiotlb_used()
398-
{
399-
return Err(NetworkInterfaceError::SecretFreeWithoutSwiotlb);
400-
}
401-
402399
let _ = self.net_builder.build(body)?;
403400
Ok(())
404401
}
405402

406403
/// Sets a vsock device to be attached when the VM starts.
407404
pub fn set_vsock_device(&mut self, config: VsockDeviceConfig) -> Result<(), VsockConfigError> {
408-
if self.has_any_io_devices()
409-
&& self.machine_config.mem_config.secret_free
410-
&& !self.swiotlb_used()
411-
{
412-
return Err(VsockConfigError::SecretFreeWithoutSwiotlb);
413-
}
414-
415405
self.vsock.insert(config)
416406
}
417407

@@ -505,6 +495,16 @@ impl VmResources {
505495
.any(|b| b.lock().expect("Poisoned lock").is_vhost_user())
506496
}
507497

498+
fn async_block_engine_used(&self) -> bool {
499+
self.block
500+
.devices
501+
.iter()
502+
.any(|b| match &*b.lock().unwrap() {
503+
Block::Virtio(b) => b.file_engine_type() == FileEngineType::Async,
504+
Block::VhostUser(_) => false,
505+
})
506+
}
507+
508508
/// The size of the swiotlb region requested, in MiB
509509
#[cfg(target_arch = "aarch64")]
510510
pub fn swiotlb_size(&self) -> usize {

src/vmm/src/vmm_config/drive.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ pub enum DriveError {
2424
DeviceUpdate(VmmError),
2525
/// A root block device already exists!
2626
RootBlockDeviceAlreadyAdded,
27-
/// A block device was added to a secret free VM that doesnt have a swiotlb region.
28-
SecretFreeWithoutSwiotlb,
27+
/// {0} is incompatible with userspace bounce buffers.
28+
IncompatibleWithUserspaceBounceBuffers(&'static str),
2929
}
3030

3131
/// Use this structure to set up the Block Device before booting the kernel.

src/vmm/src/vmm_config/net.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,6 @@ pub enum NetworkInterfaceError {
7171
GuestMacAddressInUse(String),
7272
/// Cannot open/create the tap device: {0}
7373
OpenTap(#[from] TapError),
74-
/// A net device was added to a secret free VM that doesnt have a swiotlb region.
75-
SecretFreeWithoutSwiotlb,
7674
}
7775

7876
/// Builder for a list of network devices.

0 commit comments

Comments
 (0)