Skip to content

Commit 5fa263d

Browse files
committed
IoMemory: Add IOVA-space bitmap
Without an IOMMU, we have direct access to guest physical addresses (GPAs). In order to track our writes to guest memory (during migration), we log them into dirty bitmaps, and a page's bit index is its GPA divided by the page size. With an IOMMU, however, we no longer know the GPA, instead we operate on I/O virtual addresses (IOVAs) and VMM user-space addresses (VUAs). Here, the dirty bitmap bit index is the IOVA divided by the page size. `IoMemory` types contain an internal "physical" memory type that operates on these VUAs (`IoMemory::PhysicalMemory). Any bitmap functionality that this internal type may already have (e.g. `GuestMemoryMmap` does) cannot be used for dirty bitmap tracking with an IOMMU because they would use the VUA, but we need to use the IOVA, and this information is not available on that lower layer. Therefore, `IoMemory` itself needs to support bitmaps separately from its inner `PhysicalMemory`, which will be used when the IOMMU is in use. Add an associated `IoMemory::Bitmap` type and add a bitmap object to `IommuMemory`. Ensure that writes to memory dirty that bitmap appropriately: - In `try_access()`, if write access was requested, dirty the handled region of the bitmap after the access is done. - In `get_slice()`, replace the `VolatileSlice`'s bitmap (which comes from the inner `PhysicalMemory`) by the correct slice of our IOVA bitmap before returning it. Signed-off-by: Hanna Czenczek <[email protected]>
1 parent 3420ac8 commit 5fa263d

File tree

3 files changed

+129
-15
lines changed

3 files changed

+129
-15
lines changed

src/io_memory.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@
1313
//! In addition, any access to virtual memory must be annotated with the intended access mode (i.e.
1414
//! reading and/or writing).
1515
16+
use crate::bitmap::{self, Bitmap};
1617
use crate::guest_memory::Result;
17-
use crate::{bitmap, GuestAddress, GuestMemory, MemoryRegionAddress, VolatileSlice};
18+
use crate::{GuestAddress, GuestMemory, GuestMemoryRegion, MemoryRegionAddress, VolatileSlice};
1819

1920
/// Permissions for accessing virtual memory.
2021
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -52,6 +53,11 @@ impl Permissions {
5253
pub fn allow(&self, access: Self) -> bool {
5354
*self & access == access
5455
}
56+
57+
/// Check whether the permissions `self` include write access.
58+
pub fn has_write(&self) -> bool {
59+
*self & Permissions::Write == Permissions::Write
60+
}
5561
}
5662

5763
impl std::ops::BitOr for Permissions {
@@ -87,6 +93,8 @@ impl std::ops::BitAnd for Permissions {
8793
pub trait IoMemory {
8894
/// Underlying `GuestMemory` type.
8995
type PhysicalMemory: GuestMemory + ?Sized;
96+
/// Dirty bitmap type for tracking writes to the IOVA address space.
97+
type Bitmap: Bitmap;
9098

9199
/// Return `true` if `addr..(addr + count)` is accessible with `access`.
92100
fn range_accessible(&self, addr: GuestAddress, count: usize, access: Permissions) -> bool;
@@ -138,7 +146,7 @@ pub trait IoMemory {
138146
addr: GuestAddress,
139147
count: usize,
140148
access: Permissions,
141-
) -> Result<impl Iterator<Item = Result<VolatileSlice<'a, bitmap::MS<'a, Self::PhysicalMemory>>>>>;
149+
) -> Result<impl Iterator<Item = Result<VolatileSlice<'a, bitmap::BS<'a, Self::Bitmap>>>>>;
142150

143151
/// If this virtual memory is just a plain `GuestMemory` object underneath without an IOMMU
144152
/// translation layer in between, return that `GuestMemory` object.
@@ -159,6 +167,7 @@ pub trait IoMemory {
159167
/// the same [`GuestMemory`] methods (if available), discarding the `access` parameter.
160168
impl<M: GuestMemory + ?Sized> IoMemory for M {
161169
type PhysicalMemory = M;
170+
type Bitmap = <M::R as GuestMemoryRegion>::B;
162171

163172
fn range_accessible(&self, addr: GuestAddress, count: usize, _access: Permissions) -> bool {
164173
if let Ok(done) = <M as GuestMemory>::try_access(self, count, addr, |_, len, _, _| Ok(len))
@@ -192,8 +201,7 @@ impl<M: GuestMemory + ?Sized> IoMemory for M {
192201
addr: GuestAddress,
193202
count: usize,
194203
_access: Permissions,
195-
) -> Result<impl Iterator<Item = Result<VolatileSlice<'a, bitmap::MS<'a, Self::PhysicalMemory>>>>>
196-
{
204+
) -> Result<impl Iterator<Item = Result<VolatileSlice<'a, bitmap::BS<'a, Self::Bitmap>>>>> {
197205
Ok(<M as GuestMemory>::get_slices(self, addr, count))
198206
}
199207

src/iommu.rs

Lines changed: 106 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,17 @@
1212
//! IOTLB misses require sending a notification to the front-end and awaiting a reply that supplies
1313
//! the desired mapping.
1414
15+
use crate::bitmap::{self, Bitmap};
1516
use crate::guest_memory::{
1617
Error as GuestMemoryError, GuestMemorySliceIterator, Result as GuestMemoryResult,
1718
};
1819
use crate::{
19-
bitmap, GuestAddress, GuestMemory, IoMemory, MemoryRegionAddress, Permissions, VolatileSlice,
20+
Address, GuestAddress, GuestMemory, GuestMemoryRegion, GuestUsize, IoMemory,
21+
MemoryRegionAddress, Permissions, VolatileSlice,
2022
};
2123
use rangemap::RangeMap;
2224
use std::cmp;
23-
use std::fmt::Debug;
25+
use std::fmt::{self, Debug};
2426
use std::num::Wrapping;
2527
use std::ops::{Deref, Range};
2628
use std::sync::Arc;
@@ -183,7 +185,15 @@ pub struct IotlbFails {
183185
/// The underlying [`GuestMemory`] is basically the physical memory, and the [`Iommu`] translates
184186
/// the I/O virtual address space that `IommuMemory` provides into that underlying physical address
185187
/// space.
186-
#[derive(Debug, Default)]
188+
///
189+
/// Note on memory write tracking (“logging”):
190+
/// - When the IOMMU is disabled ([`IommuMemory::set_iommu_enabled()`]), writes to memory are
191+
/// tracked by the underlying [`GuestMemory`] in its bitmap(s).
192+
/// - When it is enabled, they are instead tracked in the [`IommuMemory`]’s dirty bitmap; the
193+
/// offset in the bitmap is calculated from the write’s IOVA.
194+
///
195+
/// Therefore, this type should only be used when this is the desired behavior (IOVA-based memory
196+
/// write logging when IOMMU is used).
187197
pub struct IommuMemory<M: GuestMemory, I: Iommu> {
188198
/// Physical memory
189199
inner: M,
@@ -192,6 +202,8 @@ pub struct IommuMemory<M: GuestMemory, I: Iommu> {
192202
/// Whether the IOMMU is even to be used or not; disabling it makes this a pass-through to
193203
/// `inner`.
194204
use_iommu: bool,
205+
/// Dirty bitmap to use for IOVA accesses
206+
bitmap: Arc<<M::R as GuestMemoryRegion>::B>,
195207
}
196208

197209
impl IommuMapping {
@@ -355,27 +367,36 @@ impl TryFrom<Range<u64>> for IovaRange {
355367

356368
impl<M: GuestMemory, I: Iommu> IommuMemory<M, I> {
357369
/// Create a new `IommuMemory` instance.
358-
pub fn new(inner: M, iommu: I, use_iommu: bool) -> Self {
370+
pub fn new(inner: M, iommu: I, use_iommu: bool, bitmap: <Self as IoMemory>::Bitmap) -> Self {
359371
IommuMemory {
360372
inner,
361373
iommu: Arc::new(iommu),
362374
use_iommu,
375+
bitmap: Arc::new(bitmap),
363376
}
364377
}
365378

366379
/// Create a new version of `self` with the underlying physical memory replaced.
367380
///
368-
/// Note that the inner `Arc` reference to the IOMMU is cloned, i.e. both the existing and the
369-
/// new `IommuMemory` object will share an IOMMU instance. (The `use_iommu` flag however is
370-
/// copied, so is independent between the two instances.)
381+
/// Note that the inner `Arc` references to the IOMMU and bitmap are cloned, i.e. both the
382+
/// existing and the new `IommuMemory` object will share the IOMMU and bitmap instances. (The
383+
/// `use_iommu` flag however is copied, so is independent between the two instances.)
371384
pub fn inner_replaced(&self, inner: M) -> Self {
372385
IommuMemory {
373386
inner,
374387
iommu: Arc::clone(&self.iommu),
375388
use_iommu: self.use_iommu,
389+
bitmap: Arc::clone(&self.bitmap),
376390
}
377391
}
378392

393+
/// Return a reference to the IOVA address space's dirty bitmap.
394+
///
395+
/// This bitmap tracks write accesses done while the IOMMU is enabled.
396+
pub fn bitmap(&self) -> &Arc<<Self as IoMemory>::Bitmap> {
397+
&self.bitmap
398+
}
399+
379400
/// Enable or disable the IOMMU.
380401
///
381402
/// Disabling the IOMMU switches to pass-through mode, where every access is done directly on
@@ -401,12 +422,42 @@ impl<M: GuestMemory + Clone, I: Iommu> Clone for IommuMemory<M, I> {
401422
inner: self.inner.clone(),
402423
iommu: Arc::clone(&self.iommu),
403424
use_iommu: self.use_iommu,
425+
bitmap: Arc::clone(&self.bitmap),
426+
}
427+
}
428+
}
429+
430+
impl<M: GuestMemory + Debug, I: Iommu> Debug for IommuMemory<M, I>
431+
where
432+
<M::R as GuestMemoryRegion>::B: Debug,
433+
{
434+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
435+
f.debug_struct("IommuMemory")
436+
.field("inner", &self.inner)
437+
.field("iommu", &self.iommu)
438+
.field("use_iommu", &self.use_iommu)
439+
.field("bitmap", &self.bitmap)
440+
.finish()
441+
}
442+
}
443+
444+
impl<M: GuestMemory + Default, I: Iommu + Default> Default for IommuMemory<M, I>
445+
where
446+
<M::R as GuestMemoryRegion>::B: Default,
447+
{
448+
fn default() -> Self {
449+
IommuMemory {
450+
inner: Default::default(),
451+
iommu: Default::default(),
452+
use_iommu: Default::default(),
453+
bitmap: Default::default(),
404454
}
405455
}
406456
}
407457

408458
impl<M: GuestMemory, I: Iommu> IoMemory for IommuMemory<M, I> {
409459
type PhysicalMemory = M;
460+
type Bitmap = <M::R as GuestMemoryRegion>::B;
410461

411462
fn range_accessible(&self, addr: GuestAddress, count: usize, access: Permissions) -> bool {
412463
if !self.use_iommu {
@@ -459,7 +510,13 @@ impl<M: GuestMemory, I: Iommu> IoMemory for IommuMemory<M, I> {
459510

460511
if handled == 0 {
461512
break;
462-
} else if handled > count {
513+
}
514+
515+
if access.has_write() {
516+
self.bitmap.mark_dirty(addr.0 as usize + total, handled);
517+
}
518+
519+
if handled > count {
463520
return Err(GuestMemoryError::CallbackOutOfRange);
464521
}
465522

@@ -480,7 +537,7 @@ impl<M: GuestMemory, I: Iommu> IoMemory for IommuMemory<M, I> {
480537
count: usize,
481538
access: Permissions,
482539
) -> GuestMemoryResult<
483-
impl Iterator<Item = GuestMemoryResult<VolatileSlice<'a, bitmap::MS<'a, M>>>>,
540+
impl Iterator<Item = GuestMemoryResult<VolatileSlice<'a, bitmap::BS<'a, Self::Bitmap>>>>,
484541
> {
485542
if self.use_iommu {
486543
IommuMemorySliceIterator::virt(self, addr, count, access)
@@ -502,8 +559,11 @@ impl<M: GuestMemory, I: Iommu> IoMemory for IommuMemory<M, I> {
502559
/// Iterates over [`VolatileSlice`]s that together form an area in an `IommuMemory`.
503560
///
504561
/// Returned by [`IommuMemory::get_slices()`]
505-
#[derive(Debug)]
506562
pub struct IommuMemorySliceIterator<'a, M: GuestMemory, I: Iommu + 'a> {
563+
/// Current IOVA (needed to access the right slice of the IOVA space dirty bitmap)
564+
iova: GuestAddress,
565+
/// IOVA space dirty bitmap
566+
bitmap: Option<&'a <M::R as GuestMemoryRegion>::B>,
507567
/// Underlying physical memory (i.e. not the `IommuMemory`)
508568
phys_mem: &'a M,
509569
/// IOMMU translation result (i.e. remaining physical regions to visit)
@@ -520,6 +580,8 @@ impl<'a, M: GuestMemory, I: Iommu> IommuMemorySliceIterator<'a, M, I> {
520580
/// the underlying physical memory for the given address range.
521581
fn phys(mem: &'a IommuMemory<M, I>, addr: GuestAddress, count: usize) -> Self {
522582
IommuMemorySliceIterator {
583+
iova: addr,
584+
bitmap: None,
523585
phys_mem: &mem.inner,
524586
translation: None,
525587
current_translated_iter: Some(mem.inner.get_slices(addr, count)),
@@ -538,6 +600,8 @@ impl<'a, M: GuestMemory, I: Iommu> IommuMemorySliceIterator<'a, M, I> {
538600
) -> Result<Self, Error> {
539601
let translation = mem.iommu.translate(addr, count, access)?;
540602
Ok(IommuMemorySliceIterator {
603+
iova: addr,
604+
bitmap: Some(mem.bitmap.as_ref()),
541605
phys_mem: &mem.inner,
542606
translation: Some(translation),
543607
current_translated_iter: None,
@@ -569,7 +633,22 @@ impl<'a, M: GuestMemory, I: Iommu> IommuMemorySliceIterator<'a, M, I> {
569633
.as_mut()
570634
.and_then(|iter| iter.next())
571635
{
572-
return Some(item);
636+
let mut item = match item {
637+
Ok(item) => item,
638+
Err(err) => return Some(Err(err)),
639+
};
640+
641+
if let Some(bitmap) = self.bitmap.as_ref() {
642+
let bitmap_slice = bitmap.slice_at(self.iova.0 as usize);
643+
item = item.replace_bitmap(bitmap_slice);
644+
}
645+
646+
self.iova = match self.iova.overflowing_add(item.len() as GuestUsize) {
647+
(x @ GuestAddress(0), _) | (x, false) => x,
648+
(_, true) => return Some(Err(GuestMemoryError::GuestAddressOverflow)),
649+
};
650+
651+
return Some(Ok(item));
573652
}
574653

575654
let next_mapping = self.translation.as_mut()?.next()?;
@@ -598,3 +677,19 @@ impl<'a, M: GuestMemory, I: Iommu> Iterator for IommuMemorySliceIterator<'a, M,
598677
}
599678
}
600679
}
680+
681+
impl<'a, M: GuestMemory + Debug, I: Iommu> Debug for IommuMemorySliceIterator<'a, M, I>
682+
where
683+
I::IotlbGuard<'a>: Debug,
684+
<M::R as GuestMemoryRegion>::B: Debug,
685+
{
686+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
687+
f.debug_struct("IommuMemorySliceIterator")
688+
.field("iova", &self.iova)
689+
.field("bitmap", &self.bitmap)
690+
.field("phys_mem", &self.phys_mem)
691+
.field("translation", &self.translation)
692+
.field("current_translated_iter", &self.current_translated_iter)
693+
.finish()
694+
}
695+
}

src/volatile_memory.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,17 @@ impl<'a, B: BitmapSlice> VolatileSlice<'a, B> {
435435
}
436436
}
437437

438+
/// Replaces the bitmap in `self` by `new_bitmap`.
439+
#[cfg(feature = "iommu")]
440+
pub(crate) fn replace_bitmap<NB: BitmapSlice>(self, new_bitmap: NB) -> VolatileSlice<'a, NB> {
441+
VolatileSlice {
442+
addr: self.addr,
443+
size: self.size,
444+
bitmap: new_bitmap,
445+
mmap: self.mmap,
446+
}
447+
}
448+
438449
/// Returns a guard for the pointer to the underlying memory.
439450
pub fn ptr_guard(&self) -> PtrGuard {
440451
PtrGuard::read(self.mmap, self.addr, self.len())

0 commit comments

Comments
 (0)