Skip to content

Commit c33415a

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 284a200 commit c33415a

File tree

3 files changed

+83
-12
lines changed

3 files changed

+83
-12
lines changed

src/io_memory.rs

Lines changed: 11 additions & 2 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)]
@@ -34,6 +35,11 @@ impl Permissions {
3435
pub fn allow(&self, access: Self) -> bool {
3536
*self & access == access
3637
}
38+
39+
/// Check whether the permissions `self` include write access.
40+
pub fn has_write(&self) -> bool {
41+
*self & Permissions::Write == Permissions::Write
42+
}
3743
}
3844

3945
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;
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;
@@ -127,7 +135,7 @@ pub trait IoMemory {
127135
addr: GuestAddress,
128136
count: usize,
129137
access: Permissions,
130-
) -> Result<VolatileSlice<bitmap::MS<Self::PhysicalMemory>>>;
138+
) -> Result<VolatileSlice<bitmap::BS<Self::Bitmap>>>;
131139

132140
/// If this virtual memory is just a plain `GuestMemory` object underneath without an IOMMU
133141
/// translation layer in between, return that `GuestMemory` object.
@@ -138,6 +146,7 @@ pub trait IoMemory {
138146

139147
impl<M: GuestMemory> IoMemory for M {
140148
type PhysicalMemory = M;
149+
type Bitmap = <M::R as GuestMemoryRegion>::B;
141150

142151
fn range_accessible(&self, addr: GuestAddress, count: usize, _access: Permissions) -> bool {
143152
if count <= 1 {

src/iommu.rs

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@
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::{Error as GuestMemoryError, Result as GuestMemoryResult};
1617
use crate::{
17-
bitmap, GuestAddress, GuestMemory, IoMemory, MemoryRegionAddress, Permissions, VolatileSlice,
18+
GuestAddress, GuestMemory, GuestMemoryRegion, IoMemory, MemoryRegionAddress, Permissions,
19+
VolatileSlice,
1820
};
1921
use rangemap::RangeMap;
2022
use std::cmp;
21-
use std::fmt::Debug;
23+
use std::fmt::{self, Debug};
2224
use std::num::Wrapping;
2325
use std::ops::{Deref, Range};
2426
use std::sync::Arc;
@@ -181,7 +183,6 @@ pub struct IotlbFails {
181183
/// The underlying [`GuestMemory`] is basically the physical memory, and the [`Iommu`] translates
182184
/// the I/O virtual address space that `IommuMemory` provides into that underlying physical address
183185
/// space.
184-
#[derive(Debug, Default)]
185186
pub struct IommuMemory<M: GuestMemory, I: Iommu> {
186187
/// Physical memory
187188
inner: M,
@@ -190,6 +191,8 @@ pub struct IommuMemory<M: GuestMemory, I: Iommu> {
190191
/// Whether the IOMMU is even to be used or not; disabling it makes this a pass-through to
191192
/// `inner`.
192193
use_iommu: bool,
194+
/// Dirty bitmap to use for IOVA accesses
195+
bitmap: Arc<<M::R as GuestMemoryRegion>::B>,
193196
}
194197

195198
impl IommuMapping {
@@ -353,27 +356,36 @@ impl TryFrom<Range<u64>> for IovaRange {
353356

354357
impl<M: GuestMemory, I: Iommu> IommuMemory<M, I> {
355358
/// Create a new `IommuMemory` instance.
356-
pub fn new(inner: M, iommu: I, use_iommu: bool) -> Self {
359+
pub fn new(inner: M, iommu: I, use_iommu: bool, bitmap: <Self as IoMemory>::Bitmap) -> Self {
357360
IommuMemory {
358361
inner,
359362
iommu: Arc::new(iommu),
360363
use_iommu,
364+
bitmap: Arc::new(bitmap),
361365
}
362366
}
363367

364368
/// Create a new version of `self` with the underlying physical memory replaced.
365369
///
366-
/// Note that the inner `Arc` reference to the IOMMU is cloned, i.e. both the existing and the
367-
/// new `IommuMemory` object will share an IOMMU instance. (The `use_iommu` flag however is
368-
/// copied, so is independent between the two instances.)
370+
/// Note that the inner `Arc` references to the IOMMU and bitmap are cloned, i.e. both the
371+
/// existing and the new `IommuMemory` object will share the IOMMU and bitmap instances. (The
372+
/// `use_iommu` flag however is copied, so is independent between the two instances.)
369373
pub fn inner_replaced(&self, inner: M) -> Self {
370374
IommuMemory {
371375
inner,
372376
iommu: Arc::clone(&self.iommu),
373377
use_iommu: self.use_iommu,
378+
bitmap: Arc::clone(&self.bitmap),
374379
}
375380
}
376381

382+
/// Return a reference to the IOVA address space's dirty bitmap.
383+
///
384+
/// This bitmap tracks write accesses done while the IOMMU is enabled.
385+
pub fn bitmap(&self) -> &Arc<<Self as IoMemory>::Bitmap> {
386+
&self.bitmap
387+
}
388+
377389
/// Enable or disable the IOMMU.
378390
///
379391
/// Disabling the IOMMU switches to pass-through mode, where every access is done directly on
@@ -399,12 +411,42 @@ impl<M: GuestMemory + Clone, I: Iommu> Clone for IommuMemory<M, I> {
399411
inner: self.inner.clone(),
400412
iommu: Arc::clone(&self.iommu),
401413
use_iommu: self.use_iommu,
414+
bitmap: Arc::clone(&self.bitmap),
415+
}
416+
}
417+
}
418+
419+
impl<M: GuestMemory + Debug, I: Iommu> Debug for IommuMemory<M, I>
420+
where
421+
<M::R as GuestMemoryRegion>::B: Debug,
422+
{
423+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
424+
f.debug_struct("IommuMemory")
425+
.field("inner", &self.inner)
426+
.field("iommu", &self.iommu)
427+
.field("use_iommu", &self.use_iommu)
428+
.field("bitmap", &self.bitmap)
429+
.finish()
430+
}
431+
}
432+
433+
impl<M: GuestMemory + Default, I: Iommu + Default> Default for IommuMemory<M, I>
434+
where
435+
<M::R as GuestMemoryRegion>::B: Default,
436+
{
437+
fn default() -> Self {
438+
IommuMemory {
439+
inner: Default::default(),
440+
iommu: Default::default(),
441+
use_iommu: Default::default(),
442+
bitmap: Default::default(),
402443
}
403444
}
404445
}
405446

406447
impl<M: GuestMemory, I: Iommu> IoMemory for IommuMemory<M, I> {
407448
type PhysicalMemory = M;
449+
type Bitmap = <M::R as GuestMemoryRegion>::B;
408450

409451
fn range_accessible(&self, addr: GuestAddress, count: usize, access: Permissions) -> bool {
410452
if !self.use_iommu {
@@ -457,7 +499,13 @@ impl<M: GuestMemory, I: Iommu> IoMemory for IommuMemory<M, I> {
457499

458500
if handled == 0 {
459501
break;
460-
} else if handled > count {
502+
}
503+
504+
if access.has_write() {
505+
self.bitmap.mark_dirty(addr.0 as usize + total, handled);
506+
}
507+
508+
if handled > count {
461509
return Err(GuestMemoryError::CallbackOutOfRange);
462510
}
463511

@@ -477,7 +525,7 @@ impl<M: GuestMemory, I: Iommu> IoMemory for IommuMemory<M, I> {
477525
addr: GuestAddress,
478526
count: usize,
479527
access: Permissions,
480-
) -> GuestMemoryResult<VolatileSlice<bitmap::MS<M>>> {
528+
) -> GuestMemoryResult<VolatileSlice<bitmap::BS<Self::Bitmap>>> {
481529
if !self.use_iommu {
482530
return self.inner.get_slice(addr, count);
483531
}
@@ -502,7 +550,10 @@ impl<M: GuestMemory, I: Iommu> IoMemory for IommuMemory<M, I> {
502550
}
503551

504552
assert!(mapping.length == count || (count == 0 && mapping.length == 1));
505-
self.inner.get_slice(mapping.base, count)
553+
Ok(self
554+
.inner
555+
.get_slice(mapping.base, count)?
556+
.replace_bitmap(self.bitmap.slice_at(addr.0 as usize)))
506557
}
507558

508559
fn physical_memory(&self) -> Option<&Self::PhysicalMemory> {

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)