Skip to content

Commit 3fd7249

Browse files
XanClicroypat
authored andcommitted
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. When it comes to vhost-user, however, and we use an IOMMU, 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 (in case of vhost-user) operates on these VUAs (`IoMemory::PhysicalMemory). Any bitmap functionality that this internal type may already have (e.g. `GuestMemoryMmap` does) cannot be used for vhost-user 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 <hreitz@redhat.com>
1 parent 7b2fe5d commit 3fd7249

File tree

3 files changed

+127
-13
lines changed

3 files changed

+127
-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")]
@@ -775,6 +775,11 @@ impl Permissions {
775775
pub fn allow(&self, access: Self) -> bool {
776776
*self & access == access
777777
}
778+
779+
/// Check whether the permissions `self` include write access.
780+
pub fn has_write(&self) -> bool {
781+
*self & Permissions::Write == Permissions::Write
782+
}
778783
}
779784

780785
impl std::ops::BitOr for Permissions {
@@ -810,6 +815,8 @@ impl std::ops::BitAnd for Permissions {
810815
pub trait IoMemory {
811816
/// Underlying `GuestMemory` type.
812817
type PhysicalMemory: GuestMemory + ?Sized;
818+
/// Dirty bitmap type for tracking writes to the IOVA address space.
819+
type Bitmap: Bitmap;
813820

814821
/// Return `true` if `addr..(addr + count)` is accessible with `access`.
815822
fn check_range(&self, addr: GuestAddress, count: usize, access: Permissions) -> bool;
@@ -832,7 +839,7 @@ pub trait IoMemory {
832839
addr: GuestAddress,
833840
count: usize,
834841
access: Permissions,
835-
) -> Result<impl IoMemorySliceIterator<'a, MS<'a, Self::PhysicalMemory>>>;
842+
) -> Result<impl IoMemorySliceIterator<'a, BS<'a, Self::Bitmap>>>;
836843

837844
/// If this virtual memory is just a plain `GuestMemory` object underneath without an IOMMU
838845
/// translation layer in between, return that `GuestMemory` object.
@@ -871,6 +878,7 @@ pub trait IoMemorySliceIterator<'a, B: BitmapSlice>:
871878
/// the same [`GuestMemory`] methods (if available), discarding the `access` parameter.
872879
impl<M: GuestMemory + ?Sized> IoMemory for M {
873880
type PhysicalMemory = M;
881+
type Bitmap = <M::R as GuestMemoryRegion>::B;
874882

875883
fn check_range(&self, addr: GuestAddress, count: usize, _access: Permissions) -> bool {
876884
<M as GuestMemory>::check_range(self, addr, count)
@@ -881,7 +889,7 @@ impl<M: GuestMemory + ?Sized> IoMemory for M {
881889
addr: GuestAddress,
882890
count: usize,
883891
_access: Permissions,
884-
) -> Result<impl IoMemorySliceIterator<'a, MS<'a, Self::PhysicalMemory>>> {
892+
) -> Result<impl IoMemorySliceIterator<'a, BS<'a, Self::Bitmap>>> {
885893
Ok(<M as GuestMemory>::get_slices(self, addr, count))
886894
}
887895

src/iommu.rs

Lines changed: 105 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::iter::FusedIterator;
2428
use std::num::Wrapping;
2529
use std::ops::{Deref, Range};
@@ -183,7 +187,19 @@ pub struct IotlbFails {
183187
/// The underlying [`GuestMemory`] is basically the physical memory, and the [`Iommu`] translates
184188
/// the I/O virtual address space that `IommuMemory` provides into that underlying physical address
185189
/// space.
186-
#[derive(Debug, Default)]
190+
///
191+
/// Note that this type’s implementation of memory write tracking (“logging”) is specific to what
192+
/// is required by vhost-user:
193+
/// - When the IOMMU is disabled ([`IommuMemory::set_iommu_enabled()`]), writes to memory are
194+
/// tracked by the underlying [`GuestMemory`] in its bitmap(s).
195+
/// - When it is enabled, they are instead tracked in the [`IommuMemory`]’s dirty bitmap; the
196+
/// offset in the bitmap is calculated from the write’s IOVA.
197+
///
198+
/// That is, there are two bitmap levels, one in the underlying [`GuestMemory`], and one in
199+
/// [`IommuMemory`]. The former is used when the IOMMU is disabled, the latter when it is enabled.
200+
///
201+
/// If you need a different model (e.g. always use the [`GuestMemory`] bitmaps), you should not use
202+
/// this type.
187203
pub struct IommuMemory<M: GuestMemory, I: Iommu> {
188204
/// Physical memory
189205
backend: M,
@@ -192,6 +208,8 @@ pub struct IommuMemory<M: GuestMemory, I: Iommu> {
192208
/// Whether the IOMMU is even to be used or not; disabling it makes this a pass-through to
193209
/// `backend`.
194210
use_iommu: bool,
211+
/// Dirty bitmap to use for IOVA accesses
212+
bitmap: Arc<<M::R as GuestMemoryRegion>::B>,
195213
}
196214

197215
impl IommuMapping {
@@ -355,27 +373,36 @@ impl TryFrom<Range<u64>> for IovaRange {
355373

356374
impl<M: GuestMemory, I: Iommu> IommuMemory<M, I> {
357375
/// Create a new `IommuMemory` instance.
358-
pub fn new(backend: M, iommu: I, use_iommu: bool) -> Self {
376+
pub fn new(backend: M, iommu: I, use_iommu: bool, bitmap: <Self as IoMemory>::Bitmap) -> Self {
359377
IommuMemory {
360378
backend,
361379
iommu: Arc::new(iommu),
362380
use_iommu,
381+
bitmap: Arc::new(bitmap),
363382
}
364383
}
365384

366385
/// Create a new version of `self` with the underlying physical memory replaced.
367386
///
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.)
387+
/// Note that the inner `Arc` references to the IOMMU and bitmap are cloned, i.e. both the
388+
/// existing and the new `IommuMemory` object will share the IOMMU and bitmap instances. (The
389+
/// `use_iommu` flag however is copied, so is independent between the two instances.)
371390
pub fn with_replaced_backend(&self, new_backend: M) -> Self {
372391
IommuMemory {
373392
backend: new_backend,
374393
iommu: Arc::clone(&self.iommu),
375394
use_iommu: self.use_iommu,
395+
bitmap: Arc::clone(&self.bitmap),
376396
}
377397
}
378398

399+
/// Return a reference to the IOVA address space's dirty bitmap.
400+
///
401+
/// This bitmap tracks write accesses done while the IOMMU is enabled.
402+
pub fn bitmap(&self) -> &Arc<<Self as IoMemory>::Bitmap> {
403+
&self.bitmap
404+
}
405+
379406
/// Enable or disable the IOMMU.
380407
///
381408
/// Disabling the IOMMU switches to pass-through mode, where every access is done directly on
@@ -409,12 +436,42 @@ impl<M: GuestMemory + Clone, I: Iommu> Clone for IommuMemory<M, I> {
409436
backend: self.backend.clone(),
410437
iommu: Arc::clone(&self.iommu),
411438
use_iommu: self.use_iommu,
439+
bitmap: Arc::clone(&self.bitmap),
440+
}
441+
}
442+
}
443+
444+
impl<M: GuestMemory + Debug, I: Iommu> Debug for IommuMemory<M, I>
445+
where
446+
<M::R as GuestMemoryRegion>::B: Debug,
447+
{
448+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
449+
f.debug_struct("IommuMemory")
450+
.field("backend", &self.backend)
451+
.field("iommu", &self.iommu)
452+
.field("use_iommu", &self.use_iommu)
453+
.field("bitmap", &self.bitmap)
454+
.finish()
455+
}
456+
}
457+
458+
impl<M: GuestMemory + Default, I: Iommu + Default> Default for IommuMemory<M, I>
459+
where
460+
<M::R as GuestMemoryRegion>::B: Default,
461+
{
462+
fn default() -> Self {
463+
IommuMemory {
464+
backend: Default::default(),
465+
iommu: Default::default(),
466+
use_iommu: Default::default(),
467+
bitmap: Default::default(),
412468
}
413469
}
414470
}
415471

416472
impl<M: GuestMemory, I: Iommu> IoMemory for IommuMemory<M, I> {
417473
type PhysicalMemory = M;
474+
type Bitmap = <M::R as GuestMemoryRegion>::B;
418475

419476
fn check_range(&self, addr: GuestAddress, count: usize, access: Permissions) -> bool {
420477
if !self.use_iommu {
@@ -434,7 +491,7 @@ impl<M: GuestMemory, I: Iommu> IoMemory for IommuMemory<M, I> {
434491
addr: GuestAddress,
435492
count: usize,
436493
access: Permissions,
437-
) -> GuestMemoryResult<impl IoMemorySliceIterator<'a, bitmap::MS<'a, M>>> {
494+
) -> GuestMemoryResult<impl IoMemorySliceIterator<'a, bitmap::BS<'a, Self::Bitmap>>> {
438495
if self.use_iommu {
439496
IommuMemorySliceIterator::virt(self, addr, count, access)
440497
.map_err(GuestMemoryError::IommuError)
@@ -455,8 +512,11 @@ impl<M: GuestMemory, I: Iommu> IoMemory for IommuMemory<M, I> {
455512
/// Iterates over [`VolatileSlice`]s that together form an area in an `IommuMemory`.
456513
///
457514
/// Returned by [`IommuMemory::get_slices()`]
458-
#[derive(Debug)]
459515
pub struct IommuMemorySliceIterator<'a, M: GuestMemory, I: Iommu + 'a> {
516+
/// Current IOVA (needed to access the right slice of the IOVA space dirty bitmap)
517+
iova: GuestAddress,
518+
/// IOVA space dirty bitmap
519+
bitmap: Option<&'a <M::R as GuestMemoryRegion>::B>,
460520
/// Underlying physical memory (i.e. not the `IommuMemory`)
461521
phys_mem: &'a M,
462522
/// IOMMU translation result (i.e. remaining physical regions to visit)
@@ -473,6 +533,8 @@ impl<'a, M: GuestMemory, I: Iommu> IommuMemorySliceIterator<'a, M, I> {
473533
/// the underlying physical memory for the given address range.
474534
fn phys(mem: &'a IommuMemory<M, I>, addr: GuestAddress, count: usize) -> Self {
475535
IommuMemorySliceIterator {
536+
iova: addr,
537+
bitmap: None,
476538
phys_mem: &mem.backend,
477539
translation: None,
478540
current_translated_iter: Some(mem.backend.get_slices(addr, count)),
@@ -491,6 +553,8 @@ impl<'a, M: GuestMemory, I: Iommu> IommuMemorySliceIterator<'a, M, I> {
491553
) -> Result<Self, Error> {
492554
let translation = mem.iommu.translate(addr, count, access)?;
493555
Ok(IommuMemorySliceIterator {
556+
iova: addr,
557+
bitmap: Some(mem.bitmap.as_ref()),
494558
phys_mem: &mem.backend,
495559
translation: Some(translation),
496560
current_translated_iter: None,
@@ -522,7 +586,22 @@ impl<'a, M: GuestMemory, I: Iommu> IommuMemorySliceIterator<'a, M, I> {
522586
.as_mut()
523587
.and_then(|iter| iter.next())
524588
{
525-
return Some(item);
589+
let mut item = match item {
590+
Ok(item) => item,
591+
Err(err) => return Some(Err(err)),
592+
};
593+
594+
if let Some(bitmap) = self.bitmap.as_ref() {
595+
let bitmap_slice = bitmap.slice_at(self.iova.0 as usize);
596+
item = item.replace_bitmap(bitmap_slice);
597+
}
598+
599+
self.iova = match self.iova.overflowing_add(item.len() as GuestUsize) {
600+
(x @ GuestAddress(0), _) | (x, false) => x,
601+
(_, true) => return Some(Err(GuestMemoryError::GuestAddressOverflow)),
602+
};
603+
604+
return Some(Ok(item));
526605
}
527606

528607
let next_mapping = self.translation.as_mut()?.next()?;
@@ -563,3 +642,19 @@ impl<'a, M: GuestMemory, I: Iommu> IoMemorySliceIterator<'a, bitmap::MS<'a, M>>
563642
for IommuMemorySliceIterator<'a, M, I>
564643
{
565644
}
645+
646+
impl<'a, M: GuestMemory + Debug, I: Iommu> Debug for IommuMemorySliceIterator<'a, M, I>
647+
where
648+
I::IotlbGuard<'a>: Debug,
649+
<M::R as GuestMemoryRegion>::B: Debug,
650+
{
651+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
652+
f.debug_struct("IommuMemorySliceIterator")
653+
.field("iova", &self.iova)
654+
.field("bitmap", &self.bitmap)
655+
.field("phys_mem", &self.phys_mem)
656+
.field("translation", &self.translation)
657+
.field("current_translated_iter", &self.current_translated_iter)
658+
.finish()
659+
}
660+
}

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)