Skip to content

Commit b31bfd0

Browse files
committed
Generalize GuestMemoryMmap to arbitrary GuestMemoryRegions
Add the concept of a `GuestRegionCollection`, which just manages a list of some `GuestMemoryRegion` impls. Functionality wise, it offers the same implementations as `GuestMemoryMmap`. As a result, turn `GuestMemoryMmap` into a type alias for `GuestRegionCollection` with a fixed `R = GuestRegionMmap`. The error type handling is a bit wack, but this is needed to preserve backwards compatibility: The error type of GuestRegionCollection must match what GuestMemoryMmap historically returned, so the error type needs to be lifted from mmap.rs - however, it contains specific variants that are only relevant to GuestMemoryMmap, so cfg those behind the `backend-mmap` feature flag (as to why this specific error type gets be privilege of just being reexported as `Error` from this crate: No idea, but its pre-existing, and changing it would be a breaking change). Signed-off-by: Patrick Roy <roypat@amazon.co.uk>
1 parent 8527515 commit b31bfd0

File tree

4 files changed

+195
-157
lines changed

4 files changed

+195
-157
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
### Added
66

77
- \[[#311](https://github.com/rust-vmm/vm-memory/pull/311)\] Allow compiling without the ReadVolatile and WriteVolatile implementations
8+
- \[[#312](https://github.com/rust-vmm/vm-memory/pull/312)\] `GuestRegionContainer`, a generic container of `GuestMemoryRegion`s, generalizing `GuestMemoryMmap` (which
9+
is now a type alias for `GuestRegionContainer<GuestRegionMmap>`)
810

911
### Changed
1012

src/guest_memory.rs

Lines changed: 164 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,8 @@ pub trait GuestMemory {
409409

410410
/// Returns the region containing the specified address or `None`.
411411
fn find_region(&self, addr: GuestAddress) -> Option<&Self::R> {
412-
self.iter().find(|region| addr >= region.start_addr() && addr <= region.last_addr())
412+
self.iter()
413+
.find(|region| addr >= region.start_addr() && addr <= region.last_addr())
413414
}
414415

415416
/// Gets an iterator over the entries in the collection.
@@ -586,6 +587,168 @@ pub trait GuestMemory {
586587
}
587588
}
588589

590+
/// Errors that can occur when creating a memory map.
591+
#[derive(Debug, thiserror::Error)]
592+
pub enum GuestRegionCollectionError {
593+
/// Adding the guest base address to the length of the underlying mapping resulted
594+
/// in an overflow.
595+
#[error("Adding the guest base address to the length of the underlying mapping resulted in an overflow")]
596+
#[cfg(feature = "backend-mmap")]
597+
InvalidGuestRegion,
598+
/// Error creating a `MmapRegion` object.
599+
#[error("{0}")]
600+
#[cfg(feature = "backend-mmap")]
601+
MmapRegion(crate::mmap::MmapRegionError),
602+
/// No memory region found.
603+
#[error("No memory region found")]
604+
NoMemoryRegion,
605+
/// Some of the memory regions intersect with each other.
606+
#[error("Some of the memory regions intersect with each other")]
607+
MemoryRegionOverlap,
608+
/// The provided memory regions haven't been sorted.
609+
#[error("The provided memory regions haven't been sorted")]
610+
UnsortedMemoryRegions,
611+
}
612+
613+
/// [`GuestMemory`](trait.GuestMemory.html) implementation based on a homogeneous collection
614+
/// of [`GuestMemoryRegion`] implementations.
615+
///
616+
/// Represents a sorted set of non-overlapping physical guest memory regions.
617+
#[derive(Debug)]
618+
pub struct GuestRegionCollection<R: GuestMemoryRegion> {
619+
regions: Vec<Arc<R>>,
620+
}
621+
622+
impl<R: GuestMemoryRegion> Default for GuestRegionCollection<R> {
623+
fn default() -> Self {
624+
Self {
625+
regions: Vec::new(),
626+
}
627+
}
628+
}
629+
630+
impl<R: GuestMemoryRegion> Clone for GuestRegionCollection<R> {
631+
fn clone(&self) -> Self {
632+
GuestRegionCollection {
633+
regions: self.regions.iter().map(Arc::clone).collect(),
634+
}
635+
}
636+
}
637+
638+
impl<R: GuestMemoryRegion> GuestRegionCollection<R> {
639+
/// Creates an empty `GuestMemoryMmap` instance.
640+
pub fn new() -> Self {
641+
Self::default()
642+
}
643+
644+
/// Creates a new [`GuestRegionCollection`] from a vector of regions.
645+
///
646+
/// # Arguments
647+
///
648+
/// * `regions` - The vector of regions.
649+
/// The regions shouldn't overlap, and they should be sorted
650+
/// by the starting address.
651+
pub fn from_regions(
652+
mut regions: Vec<R>,
653+
) -> std::result::Result<Self, GuestRegionCollectionError> {
654+
Self::from_arc_regions(regions.drain(..).map(Arc::new).collect())
655+
}
656+
657+
/// Creates a new [`GuestRegionCollection`] from a vector of Arc regions.
658+
///
659+
/// Similar to the constructor `from_regions()` as it returns a
660+
/// [`GuestRegionCollection`]. The need for this constructor is to provide a way for
661+
/// consumer of this API to create a new [`GuestRegionCollection`] based on existing
662+
/// regions coming from an existing [`GuestRegionCollection`] instance.
663+
///
664+
/// # Arguments
665+
///
666+
/// * `regions` - The vector of `Arc` regions.
667+
/// The regions shouldn't overlap and they should be sorted
668+
/// by the starting address.
669+
pub fn from_arc_regions(
670+
regions: Vec<Arc<R>>,
671+
) -> std::result::Result<Self, GuestRegionCollectionError> {
672+
if regions.is_empty() {
673+
return Err(GuestRegionCollectionError::NoMemoryRegion);
674+
}
675+
676+
for window in regions.windows(2) {
677+
let prev = &window[0];
678+
let next = &window[1];
679+
680+
if prev.start_addr() > next.start_addr() {
681+
return Err(GuestRegionCollectionError::UnsortedMemoryRegions);
682+
}
683+
684+
if prev.last_addr() >= next.start_addr() {
685+
return Err(GuestRegionCollectionError::MemoryRegionOverlap);
686+
}
687+
}
688+
689+
Ok(Self { regions })
690+
}
691+
692+
/// Insert a region into the `GuestMemoryMmap` object and return a new `GuestMemoryMmap`.
693+
///
694+
/// # Arguments
695+
/// * `region`: the memory region to insert into the guest memory object.
696+
pub fn insert_region(
697+
&self,
698+
region: Arc<R>,
699+
) -> std::result::Result<GuestRegionCollection<R>, GuestRegionCollectionError> {
700+
let mut regions = self.regions.clone();
701+
regions.push(region);
702+
regions.sort_by_key(|x| x.start_addr());
703+
704+
Self::from_arc_regions(regions)
705+
}
706+
707+
/// Remove a region from the [`GuestRegionCollection`] object and return a new `GuestRegionCollection`
708+
/// on success, together with the removed region.
709+
///
710+
/// # Arguments
711+
/// * `base`: base address of the region to be removed
712+
/// * `size`: size of the region to be removed
713+
pub fn remove_region(
714+
&self,
715+
base: GuestAddress,
716+
size: GuestUsize,
717+
) -> std::result::Result<(GuestRegionCollection<R>, Arc<R>), GuestRegionCollectionError> {
718+
if let Ok(region_index) = self.regions.binary_search_by_key(&base, |x| x.start_addr()) {
719+
if self.regions.get(region_index).unwrap().len() == size {
720+
let mut regions = self.regions.clone();
721+
let region = regions.remove(region_index);
722+
return Ok((Self { regions }, region));
723+
}
724+
}
725+
726+
Err(GuestRegionCollectionError::NoMemoryRegion)
727+
}
728+
}
729+
730+
impl<R: GuestMemoryRegion> GuestMemory for GuestRegionCollection<R> {
731+
type R = R;
732+
733+
fn num_regions(&self) -> usize {
734+
self.regions.len()
735+
}
736+
737+
fn find_region(&self, addr: GuestAddress) -> Option<&R> {
738+
let index = match self.regions.binary_search_by_key(&addr, |x| x.start_addr()) {
739+
Ok(x) => Some(x),
740+
// Within the closest region with starting address < addr
741+
Err(x) if (x > 0 && addr <= self.regions[x - 1].last_addr()) => Some(x - 1),
742+
_ => None,
743+
};
744+
index.map(|x| self.regions[x].as_ref())
745+
}
746+
747+
fn iter(&self) -> impl Iterator<Item = &Self::R> {
748+
self.regions.iter().map(AsRef::as_ref)
749+
}
750+
}
751+
589752
impl<T: GuestMemory + ?Sized> Bytes<GuestAddress> for T {
590753
type E = Error;
591754

src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ pub use endian::{Be16, Be32, Be64, BeSize, Le16, Le32, Le64, LeSize};
4747
pub mod guest_memory;
4848
pub use guest_memory::{
4949
Error as GuestMemoryError, FileOffset, GuestAddress, GuestAddressSpace, GuestMemory,
50-
GuestMemoryRegion, GuestUsize, MemoryRegionAddress, Result as GuestMemoryResult,
50+
GuestMemoryRegion, GuestRegionCollection, GuestRegionCollectionError as Error, GuestUsize,
51+
MemoryRegionAddress, Result as GuestMemoryResult,
5152
};
5253

5354
pub mod io;
@@ -66,7 +67,7 @@ mod mmap_windows;
6667
pub mod mmap;
6768

6869
#[cfg(feature = "backend-mmap")]
69-
pub use mmap::{Error, GuestMemoryMmap, GuestRegionMmap, MmapRegion};
70+
pub use mmap::{GuestMemoryMmap, GuestRegionMmap, MmapRegion};
7071
#[cfg(all(feature = "backend-mmap", feature = "xen", unix))]
7172
pub use mmap::{MmapRange, MmapXenFlags};
7273

0 commit comments

Comments
 (0)