Skip to content

Commit bd7ee70

Browse files
committed
add concept of "swiotlb regions" to struct Vm
Add a special `GuestMemoryMmap` to `struct Vm` that has the semantics that if its not empty, it represents the swiotlb area in the guest. Have all virtio devices only get access to swiotlb regions if they exist, only falling back onto having access to all of guest memory otherwise. This means that in case of swiotlb regions being set up, virtio devices cannot access generic memory, so if the guest decides to (incorrectly) place I/O buffers outside of swiotlb we'll get a somewhat meaningful error of "invalid guest address" (or similar), instead of random things failing further down the line in case generic memory isn't accessible by firecracker/the host kernel for some reason (e.g. if its guest_memfd backed in the future). When a VM with swiotlb regions is being snapshotted, the regions are simply concatenated to the end of the snapshot memory file. This changes the snapshot memory file format in the sense that regions in the file are not necessarily sorted by guest address anymore. Signed-off-by: Patrick Roy <[email protected]>
1 parent d6c88a3 commit bd7ee70

File tree

6 files changed

+74
-20
lines changed

6 files changed

+74
-20
lines changed

src/vmm/src/arch/aarch64/vm.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ impl ArchVm {
7070
pub fn save_state(&self, mpidrs: &[u64]) -> Result<VmState, ArchVmError> {
7171
Ok(VmState {
7272
memory: self.common.guest_memory.describe(),
73+
io_memory: self.common.swiotlb_regions.describe(),
7374
gic: self
7475
.get_irqchip()
7576
.save_device(mpidrs)
@@ -96,6 +97,8 @@ impl ArchVm {
9697
pub struct VmState {
9798
/// Guest memory state
9899
pub memory: GuestMemoryState,
100+
/// io memory state
101+
pub io_memory: GuestMemoryState,
99102
/// GIC state.
100103
pub gic: GicState,
101104
}

src/vmm/src/arch/x86_64/vm.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ impl ArchVm {
187187

188188
Ok(VmState {
189189
memory: self.common.guest_memory.describe(),
190+
io_memory: self.common.swiotlb_regions.describe(),
190191
pitstate,
191192
clock,
192193
pic_master,
@@ -211,6 +212,8 @@ impl ArchVm {
211212
pub struct VmState {
212213
/// guest memory state
213214
pub memory: GuestMemoryState,
215+
/// io memory state
216+
pub io_memory: GuestMemoryState,
214217
pitstate: kvm_pit_state2,
215218
clock: kvm_clock_data,
216219
// TODO: rename this field to adopt inclusive language once Linux updates it, too.

src/vmm/src/builder.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ use std::sync::{Arc, Mutex};
1313
use event_manager::{MutEventSubscriber, SubscriberOps};
1414
use libc::EFD_NONBLOCK;
1515
use linux_loader::cmdline::Cmdline as LoaderKernelCmdline;
16-
#[cfg(target_arch = "aarch64")]
17-
use linux_loader::loader::pe::PE as Loader;
1816
use utils::time::TimestampUs;
1917
#[cfg(target_arch = "aarch64")]
2018
use vm_superio::Rtc;
@@ -64,7 +62,6 @@ use crate::vmm_config::machine_config::MachineConfigError;
6462
use crate::vmm_config::snapshot::{MemBackendConfig, MemBackendType};
6563
use crate::vstate::kvm::Kvm;
6664
use crate::vstate::memory;
67-
use crate::vstate::memory::GuestMemory;
6865
use crate::vstate::vcpu::{Vcpu, VcpuError};
6966
use crate::vstate::vm::Vm;
7067
use crate::{EventManager, Vmm, VmmError, device_manager};
@@ -648,7 +645,7 @@ fn attach_virtio_device<T: 'static + VirtioDevice + MutEventSubscriber + Debug>(
648645
event_manager.add_subscriber(device.clone());
649646

650647
// The device mutex mustn't be locked here otherwise it will deadlock.
651-
let device = MmioTransport::new(vmm.vm.guest_memory().clone(), device, is_vhost_user);
648+
let device = MmioTransport::new(vmm.vm.io_memory().clone(), device, is_vhost_user);
652649
vmm.mmio_device_manager
653650
.register_mmio_virtio_for_boot(
654651
vmm.vm.fd(),

src/vmm/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -700,7 +700,7 @@ impl Vmm {
700700
pub fn update_balloon_config(&mut self, amount_mib: u32) -> Result<(), BalloonError> {
701701
// The balloon cannot have a target size greater than the size of
702702
// the guest memory.
703-
if u64::from(amount_mib) > mem_size_mib(&self.vm.guest_memory()) {
703+
if u64::from(amount_mib) > mem_size_mib(self.vm.guest_memory()) {
704704
return Err(BalloonError::TooManyPagesRequested);
705705
}
706706

src/vmm/src/persist.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ use crate::snapshot::Snapshot;
3131
use crate::utils::u64_to_usize;
3232
use crate::vmm_config::boot_source::BootSourceConfig;
3333
use crate::vmm_config::instance_info::InstanceInfo;
34-
use crate::vmm_config::snapshot::{CreateSnapshotParams, LoadSnapshotParams};
3534
use crate::vmm_config::machine_config::{
3635
HugePageConfig, MachineConfigError, MachineConfigUpdate, MemoryConfig,
3736
};
37+
use crate::vmm_config::snapshot::{CreateSnapshotParams, LoadSnapshotParams};
3838
use crate::vstate::kvm::KvmState;
3939
use crate::vstate::memory::MemoryError;
4040
use crate::vstate::vcpu::{VcpuSendEventError, VcpuState};
@@ -138,7 +138,7 @@ pub enum CreateSnapshotError {
138138
/// Cannot get dirty bitmap: {0}
139139
DirtyBitmap(kvm_ioctls::Error),
140140
/// Cannot write memory file: {0}
141-
Memory(MemoryError),
141+
Memory(#[from] MemoryError),
142142
/// Cannot perform {0} on the memory backing file: {1}
143143
MemoryBackingFile(&'static str, io::Error),
144144
/// Cannot save the microVM state: {0}

src/vmm/src/vstate/vm.rs

Lines changed: 64 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,9 @@ use vmm_sys_util::eventfd::EventFd;
1717

1818
pub use crate::arch::{ArchVm as Vm, ArchVmError, VmState};
1919
use crate::logger::info;
20-
use crate::persist::CreateSnapshotError;
20+
use crate::persist::{CreateSnapshotError, GuestRegionUffdMapping};
2121
use crate::utils::u64_to_usize;
2222
use crate::vmm_config::snapshot::SnapshotType;
23-
use crate::persist::GuestRegionUffdMapping;
2423
use crate::vstate::memory::{
2524
GuestMemory, GuestMemoryExtension, GuestMemoryMmap, GuestMemoryRegion, GuestRegionMmap,
2625
KvmRegion,
@@ -36,6 +35,8 @@ pub struct VmCommon {
3635
max_memslots: usize,
3736
/// The guest memory of this Vm.
3837
pub guest_memory: GuestMemoryMmap,
38+
/// The swiotlb regions of this Vm.
39+
pub swiotlb_regions: GuestMemoryMmap,
3940
}
4041

4142
/// Errors associated with the wrappers over KVM ioctls.
@@ -103,6 +104,7 @@ impl Vm {
103104
fd,
104105
max_memslots: kvm.max_nr_memslots(),
105106
guest_memory: GuestMemoryMmap::default(),
107+
swiotlb_regions: GuestMemoryMmap::default(),
106108
})
107109
}
108110

@@ -142,6 +144,8 @@ impl Vm {
142144
let next_slot = self
143145
.guest_memory()
144146
.num_regions()
147+
.checked_add(self.io_memory().num_regions())
148+
.ok_or(VmError::NotEnoughMemorySlots)?
145149
.try_into()
146150
.map_err(|_| VmError::NotEnoughMemorySlots)?;
147151

@@ -177,16 +181,56 @@ impl Vm {
177181
Ok(())
178182
}
179183

184+
/// Registers a new io memory region to this [`Vm`].
185+
pub fn register_swiotlb_region(&mut self, region: GuestRegionMmap) -> Result<(), VmError> {
186+
let arcd_region = Arc::new(self.kvmify_region(region)?);
187+
let new_collection = self
188+
.common
189+
.swiotlb_regions
190+
.insert_region(Arc::clone(&arcd_region))?;
191+
192+
self.register_kvm_region(arcd_region.as_ref())?;
193+
194+
self.common.swiotlb_regions = new_collection;
195+
Ok(())
196+
}
197+
180198
/// Gets a reference to the kvm file descriptor owned by this VM.
181199
pub fn fd(&self) -> &VmFd {
182200
&self.common.fd
183201
}
184202

185-
/// Gets a reference to this [`Vm`]'s [`GuestMemoryMmap`] object
203+
/// Gets a reference to this [`Vm`]'s [`GuestMemoryMmap`] object, which
204+
/// contains all non-swiotlb guest memory regions.
186205
pub fn guest_memory(&self) -> &GuestMemoryMmap {
187206
&self.common.guest_memory
188207
}
189208

209+
/// Returns a reference to the [`GuestMemoryMmap`] that I/O devices attached to this [`Vm`]
210+
/// have access to. If no I/O regions were registered, return the same as [`Vm::guest_memory`],
211+
/// otherwise returns the [`GuestMemoryMmap`] describing a specific swiotlb region.
212+
pub fn io_memory(&self) -> &GuestMemoryMmap {
213+
if self.common.swiotlb_regions.num_regions() > 0 {
214+
&self.common.swiotlb_regions
215+
} else {
216+
&self.common.guest_memory
217+
}
218+
}
219+
220+
/// Gets a reference to the [`GuestMemoryMmap`] holding the swiotlb regions registered to
221+
/// this [`Vm`]. Unlike [`Vm::io_memory`], does not fall back to returning the
222+
/// [`GuestMemoryMmap`] of normal memory when no swiotlb regions were registered.
223+
pub fn swiotlb_regions(&self) -> &GuestMemoryMmap {
224+
&self.common.swiotlb_regions
225+
}
226+
227+
/// Returns an iterator over all regions, normal and swiotlb.
228+
fn all_regions(&self) -> impl Iterator<Item = &KvmRegion> {
229+
self.guest_memory()
230+
.iter()
231+
.chain(self.common.swiotlb_regions.iter())
232+
}
233+
190234
pub(crate) fn create_uffd(
191235
&self,
192236
) -> Result<(Uffd, Vec<GuestRegionUffdMapping>), userfaultfd::Error> {
@@ -203,24 +247,27 @@ impl Vm {
203247

204248
let mut offset = 0;
205249

206-
for mem_region in self.common.guest_memory.iter() {
207-
uffd.register(mem_region.as_ptr().cast(), mem_region.size() as _)?;
250+
for mem_region in self.all_regions() {
251+
uffd.register(
252+
mem_region.inner().userspace_addr as *mut _,
253+
u64_to_usize(mem_region.len()),
254+
)?;
208255
mappings.push(GuestRegionUffdMapping {
209-
base_host_virt_addr: mem_region.as_ptr() as u64,
210-
size: mem_region.size(),
256+
base_host_virt_addr: mem_region.inner().userspace_addr,
257+
size: u64_to_usize(mem_region.len()),
211258
offset,
212259
..Default::default()
213260
});
214261

215-
offset += mem_region.size() as u64;
262+
offset += mem_region.len();
216263
}
217264

218265
Ok((uffd, mappings))
219266
}
220267

221268
/// Resets the KVM dirty bitmap for each of the guest's memory regions.
222269
pub fn reset_dirty_bitmap(&self) {
223-
self.guest_memory().iter().for_each(|region| {
270+
self.all_regions().for_each(|region| {
224271
let _ = self
225272
.fd()
226273
.get_dirty_log(region.inner().slot, u64_to_usize(region.len()));
@@ -230,7 +277,7 @@ impl Vm {
230277
/// Retrieves the KVM dirty bitmap for each of the guest's memory regions.
231278
pub fn get_dirty_bitmap(&self) -> Result<DirtyBitmap, vmm_sys_util::errno::Error> {
232279
let mut bitmap: DirtyBitmap = HashMap::new();
233-
self.guest_memory().iter().try_for_each(|region| {
280+
self.all_regions().try_for_each(|region| {
234281
self.fd()
235282
.get_dirty_log(region.inner().slot, u64_to_usize(region.len()))
236283
.map(|bitmap_region| _ = bitmap.insert(region.inner().slot, bitmap_region))
@@ -293,18 +340,22 @@ impl Vm {
293340
let dirty_bitmap = self.get_dirty_bitmap().map_err(DirtyBitmap)?;
294341
self.guest_memory()
295342
.dump_dirty(&mut file, &dirty_bitmap)
296-
.map_err(Memory)
343+
.and_then(|_| self.swiotlb_regions().dump_dirty(&mut file, &dirty_bitmap))
297344
}
298345
SnapshotType::Full => {
299-
let dump_res = self.guest_memory().dump(&mut file).map_err(Memory);
346+
let dump_res = self
347+
.guest_memory()
348+
.dump(&mut file)
349+
.and_then(|_| self.swiotlb_regions().dump(&mut file));
300350
if dump_res.is_ok() {
301351
self.reset_dirty_bitmap();
302352
self.guest_memory().reset_dirty();
303353
}
304354

305355
dump_res
306356
}
307-
}?;
357+
}
358+
.map_err(Memory)?;
308359

309360
file.flush()
310361
.map_err(|err| MemoryBackingFile("flush", err))?;

0 commit comments

Comments
 (0)