Skip to content

Commit 7d42461

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 ce7dc83 commit 7d42461

File tree

3 files changed

+123
-13
lines changed

3 files changed

+123
-13
lines changed

src/guest_memory.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ use std::sync::atomic::Ordering;
5252
use std::sync::Arc;
5353

5454
use crate::address::{Address, AddressValue};
55-
use crate::bitmap::{BitmapSlice, MS};
55+
use crate::bitmap::{Bitmap, BitmapSlice, BS, MS};
5656
use crate::bytes::{AtomicAccess, Bytes};
5757
use crate::io::{ReadVolatile, WriteVolatile};
5858
#[cfg(feature = "iommu")]
@@ -771,6 +771,11 @@ impl Permissions {
771771
pub fn allow(&self, access: Self) -> bool {
772772
*self & access == access
773773
}
774+
775+
/// Check whether the permissions `self` include write access.
776+
pub fn has_write(&self) -> bool {
777+
*self & Permissions::Write == Permissions::Write
778+
}
774779
}
775780

776781
impl std::ops::BitOr for Permissions {
@@ -806,6 +811,8 @@ impl std::ops::BitAnd for Permissions {
806811
pub trait IoMemory {
807812
/// Underlying `GuestMemory` type.
808813
type PhysicalMemory: GuestMemory + ?Sized;
814+
/// Dirty bitmap type for tracking writes to the IOVA address space.
815+
type Bitmap: Bitmap;
809816

810817
/// Return `true` if `addr..(addr + count)` is accessible with `access`.
811818
fn check_range(&self, addr: GuestAddress, count: usize, access: Permissions) -> bool;
@@ -828,7 +835,7 @@ pub trait IoMemory {
828835
addr: GuestAddress,
829836
count: usize,
830837
access: Permissions,
831-
) -> Result<impl IoMemorySliceIterator<'a, MS<'a, Self::PhysicalMemory>>>;
838+
) -> Result<impl IoMemorySliceIterator<'a, BS<'a, Self::Bitmap>>>;
832839

833840
/// If this virtual memory is just a plain `GuestMemory` object underneath without an IOMMU
834841
/// translation layer in between, return that `GuestMemory` object.
@@ -867,6 +874,7 @@ pub trait IoMemorySliceIterator<'a, B: BitmapSlice>:
867874
/// the same [`GuestMemory`] methods (if available), discarding the `access` parameter.
868875
impl<M: GuestMemory + ?Sized> IoMemory for M {
869876
type PhysicalMemory = M;
877+
type Bitmap = <M::R as GuestMemoryRegion>::B;
870878

871879
fn check_range(&self, addr: GuestAddress, count: usize, _access: Permissions) -> bool {
872880
<M as GuestMemory>::check_range(self, addr, count)
@@ -877,7 +885,7 @@ impl<M: GuestMemory + ?Sized> IoMemory for M {
877885
addr: GuestAddress,
878886
count: usize,
879887
_access: Permissions,
880-
) -> Result<impl IoMemorySliceIterator<'a, MS<'a, Self::PhysicalMemory>>> {
888+
) -> Result<impl IoMemorySliceIterator<'a, BS<'a, Self::Bitmap>>> {
881889
Ok(<M as GuestMemory>::get_slices(self, addr, count))
882890
}
883891

src/iommu.rs

Lines changed: 101 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,18 @@
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, IoMemorySliceIterator,
1718
Result as GuestMemoryResult,
1819
};
19-
use crate::{bitmap, GuestAddress, GuestMemory, IoMemory, Permissions, VolatileSlice};
20+
use crate::{
21+
Address, GuestAddress, GuestMemory, GuestMemoryRegion, GuestUsize, IoMemory, Permissions,
22+
VolatileSlice,
23+
};
2024
use rangemap::RangeMap;
2125
use std::cmp;
22-
use std::fmt::Debug;
26+
use std::fmt::{self, Debug};
2327
use std::num::Wrapping;
2428
use std::ops::{Deref, Range};
2529
use std::sync::Arc;
@@ -182,7 +186,15 @@ pub struct IotlbFails {
182186
/// The underlying [`GuestMemory`] is basically the physical memory, and the [`Iommu`] translates
183187
/// the I/O virtual address space that `IommuMemory` provides into that underlying physical address
184188
/// space.
185-
#[derive(Debug, Default)]
189+
///
190+
/// Note on memory write tracking (“logging”):
191+
/// - When the IOMMU is disabled ([`IommuMemory::set_iommu_enabled()`]), writes to memory are
192+
/// tracked by the underlying [`GuestMemory`] in its bitmap(s).
193+
/// - When it is enabled, they are instead tracked in the [`IommuMemory`]’s dirty bitmap; the
194+
/// offset in the bitmap is calculated from the write’s IOVA.
195+
///
196+
/// Therefore, this type should only be used when this is the desired behavior (IOVA-based memory
197+
/// write logging when IOMMU is used).
186198
pub struct IommuMemory<M: GuestMemory, I: Iommu> {
187199
/// Physical memory
188200
inner: M,
@@ -191,6 +203,8 @@ pub struct IommuMemory<M: GuestMemory, I: Iommu> {
191203
/// Whether the IOMMU is even to be used or not; disabling it makes this a pass-through to
192204
/// `inner`.
193205
use_iommu: bool,
206+
/// Dirty bitmap to use for IOVA accesses
207+
bitmap: Arc<<M::R as GuestMemoryRegion>::B>,
194208
}
195209

196210
impl IommuMapping {
@@ -354,27 +368,36 @@ impl TryFrom<Range<u64>> for IovaRange {
354368

355369
impl<M: GuestMemory, I: Iommu> IommuMemory<M, I> {
356370
/// Create a new `IommuMemory` instance.
357-
pub fn new(inner: M, iommu: I, use_iommu: bool) -> Self {
371+
pub fn new(inner: M, iommu: I, use_iommu: bool, bitmap: <Self as IoMemory>::Bitmap) -> Self {
358372
IommuMemory {
359373
inner,
360374
iommu: Arc::new(iommu),
361375
use_iommu,
376+
bitmap: Arc::new(bitmap),
362377
}
363378
}
364379

365380
/// Create a new version of `self` with the underlying physical memory replaced.
366381
///
367-
/// Note that the inner `Arc` reference to the IOMMU is cloned, i.e. both the existing and the
368-
/// new `IommuMemory` object will share an IOMMU instance. (The `use_iommu` flag however is
369-
/// copied, so is independent between the two instances.)
382+
/// Note that the inner `Arc` references to the IOMMU and bitmap are cloned, i.e. both the
383+
/// existing and the new `IommuMemory` object will share the IOMMU and bitmap instances. (The
384+
/// `use_iommu` flag however is copied, so is independent between the two instances.)
370385
pub fn inner_replaced(&self, inner: M) -> Self {
371386
IommuMemory {
372387
inner,
373388
iommu: Arc::clone(&self.iommu),
374389
use_iommu: self.use_iommu,
390+
bitmap: Arc::clone(&self.bitmap),
375391
}
376392
}
377393

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

407459
impl<M: GuestMemory, I: Iommu> IoMemory for IommuMemory<M, I> {
408460
type PhysicalMemory = M;
461+
type Bitmap = <M::R as GuestMemoryRegion>::B;
409462

410463
fn check_range(&self, addr: GuestAddress, count: usize, access: Permissions) -> bool {
411464
if !self.use_iommu {
@@ -424,7 +477,7 @@ impl<M: GuestMemory, I: Iommu> IoMemory for IommuMemory<M, I> {
424477
addr: GuestAddress,
425478
count: usize,
426479
access: Permissions,
427-
) -> GuestMemoryResult<impl IoMemorySliceIterator<'a, bitmap::MS<'a, M>>> {
480+
) -> GuestMemoryResult<impl IoMemorySliceIterator<'a, bitmap::BS<'a, Self::Bitmap>>> {
428481
if self.use_iommu {
429482
IommuMemorySliceIterator::virt(self, addr, count, access)
430483
.map_err(GuestMemoryError::IommuError)
@@ -445,8 +498,11 @@ impl<M: GuestMemory, I: Iommu> IoMemory for IommuMemory<M, I> {
445498
/// Iterates over [`VolatileSlice`]s that together form an area in an `IommuMemory`.
446499
///
447500
/// Returned by [`IommuMemory::get_slices()`]
448-
#[derive(Debug)]
449501
pub struct IommuMemorySliceIterator<'a, M: GuestMemory, I: Iommu + 'a> {
502+
/// Current IOVA (needed to access the right slice of the IOVA space dirty bitmap)
503+
iova: GuestAddress,
504+
/// IOVA space dirty bitmap
505+
bitmap: Option<&'a <M::R as GuestMemoryRegion>::B>,
450506
/// Underlying physical memory (i.e. not the `IommuMemory`)
451507
phys_mem: &'a M,
452508
/// IOMMU translation result (i.e. remaining physical regions to visit)
@@ -463,6 +519,8 @@ impl<'a, M: GuestMemory, I: Iommu> IommuMemorySliceIterator<'a, M, I> {
463519
/// the underlying physical memory for the given address range.
464520
fn phys(mem: &'a IommuMemory<M, I>, addr: GuestAddress, count: usize) -> Self {
465521
IommuMemorySliceIterator {
522+
iova: addr,
523+
bitmap: None,
466524
phys_mem: &mem.inner,
467525
translation: None,
468526
current_translated_iter: Some(mem.inner.get_slices(addr, count)),
@@ -481,6 +539,8 @@ impl<'a, M: GuestMemory, I: Iommu> IommuMemorySliceIterator<'a, M, I> {
481539
) -> Result<Self, Error> {
482540
let translation = mem.iommu.translate(addr, count, access)?;
483541
Ok(IommuMemorySliceIterator {
542+
iova: addr,
543+
bitmap: Some(mem.bitmap.as_ref()),
484544
phys_mem: &mem.inner,
485545
translation: Some(translation),
486546
current_translated_iter: None,
@@ -512,7 +572,22 @@ impl<'a, M: GuestMemory, I: Iommu> IommuMemorySliceIterator<'a, M, I> {
512572
.as_mut()
513573
.and_then(|iter| iter.next())
514574
{
515-
return Some(item);
575+
let mut item = match item {
576+
Ok(item) => item,
577+
Err(err) => return Some(Err(err)),
578+
};
579+
580+
if let Some(bitmap) = self.bitmap.as_ref() {
581+
let bitmap_slice = bitmap.slice_at(self.iova.0 as usize);
582+
item = item.replace_bitmap(bitmap_slice);
583+
}
584+
585+
self.iova = match self.iova.overflowing_add(item.len() as GuestUsize) {
586+
(x @ GuestAddress(0), _) | (x, false) => x,
587+
(_, true) => return Some(Err(GuestMemoryError::GuestAddressOverflow)),
588+
};
589+
590+
return Some(Ok(item));
516591
}
517592

518593
let next_mapping = self.translation.as_mut()?.next()?;
@@ -546,3 +621,19 @@ impl<'a, M: GuestMemory, I: Iommu> IoMemorySliceIterator<'a, bitmap::MS<'a, M>>
546621
for IommuMemorySliceIterator<'a, M, I>
547622
{
548623
}
624+
625+
impl<'a, M: GuestMemory + Debug, I: Iommu> Debug for IommuMemorySliceIterator<'a, M, I>
626+
where
627+
I::IotlbGuard<'a>: Debug,
628+
<M::R as GuestMemoryRegion>::B: Debug,
629+
{
630+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
631+
f.debug_struct("IommuMemorySliceIterator")
632+
.field("iova", &self.iova)
633+
.field("bitmap", &self.bitmap)
634+
.field("phys_mem", &self.phys_mem)
635+
.field("translation", &self.translation)
636+
.field("current_translated_iter", &self.current_translated_iter)
637+
.finish()
638+
}
639+
}

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)