Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generalize GuestMemoryMmap to arbitrary GuestMemoryRegions #312

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

roypat
Copy link
Collaborator

@roypat roypat commented Feb 10, 2025

Summary of the PR

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.

Long term, this can be a first step towards resolving the incompatibility between the unix and xen features (currently, the code in mmap.rs only works if exactly one of the modules mmap_windows, mmap_xen and mmap_unix exist, since they all define roughly the same types which are then imported by the mmap module without renames)

The last commit is only tangentially related, but I ran into it when moving around the error type, and I like having PRs that remove more lines than they add, so I included it here xP

Requirements

Before submitting your PR, please make sure you addressed the following
requirements:

  • All commits in this PR have Signed-Off-By trailers (with
    git commit -s), and the commit message has max 60 characters for the
    summary and max 75 characters for each description line.
  • All added/changed functionality has a corresponding unit/integration
    test.
  • All added/changed public-facing functionality has entries in the "Upcoming
    Release" section of CHANGELOG.md (if no such section exists, please create one).
  • Any newly added unsafe code is properly documented.

This function can be default-implemented in terms of
`GuestMemory::iter()`. Downstream impls can overwrite this more
specialized and efficient versions of course (such as GuestMemoryMmap
using a binary search).

Signed-off-by: Patrick Roy <roypat@amazon.co.uk>
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>
@roypat roypat force-pushed the generic-region-container branch from 99d1a51 to df86175 Compare February 10, 2025 17:19
Some tests that were explicitly testing for error conditions used
converted errors to strings to determine whether two errors are the same
(by saying they're only the same if their string representation was
identical). Replace this with more roboust assertions on `matches!`.

Signed-off-by: Patrick Roy <roypat@amazon.co.uk>
@roypat roypat force-pushed the generic-region-container branch from df86175 to c76a6ca Compare February 10, 2025 17:20
Comment on lines +672 to +674
if regions.is_empty() {
return Err(GuestRegionCollectionError::NoMemoryRegion);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it really an error to provide no regions?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is just moved from mmap.rs without changes. There might be improvements that can be made to the actual API, but that's out of scope here I'd say, because ideally we'd keep backwards compatibility here.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

Comment on lines +696 to +705
pub fn insert_region(
&self,
region: Arc<R>,
) -> std::result::Result<GuestRegionCollection<R>, GuestRegionCollectionError> {
let mut regions = self.regions.clone();
regions.push(region);
regions.sort_by_key(|x| x.start_addr());

Self::from_arc_regions(regions)
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should just take &mut self and mutate in place. The check for region intersection can be moved into a separate function and used here and in the from_arc_regions

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is just moved from mmap.rs without changes. There might be improvements that can be made to the actual API, but that's out of scope here I'd say, because ideally we'd keep backwards compatibility here.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

/// * `base`: base address of the region to be removed
/// * `size`: size of the region to be removed
pub fn remove_region(
&self,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, why not &mut self

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is just moved from mmap.rs without changes. There might be improvements that can be made to the actual API, but that's out of scope here I'd say, because ideally we'd keep backwards compatibility here.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

}
}

Err(GuestRegionCollectionError::NoMemoryRegion)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this an error to remove a non existing region? Maybe return type should be (Self, Option<Arc<R>>) where optional is the removed region if it existed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is just moved from mmap.rs without changes. There might be improvements that can be made to the actual API, but that's out of scope here I'd say, because ideally we'd keep backwards compatibility here.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

Comment on lines +738 to +744
let index = match self.regions.binary_search_by_key(&addr, |x| x.start_addr()) {
Ok(x) => Some(x),
// Within the closest region with starting address < addr
Err(x) if (x > 0 && addr <= self.regions[x - 1].last_addr()) => Some(x - 1),
_ => None,
};
index.map(|x| self.regions[x].as_ref())

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How many regions there can possibly be? I think a simple for loop where we check if address is in the region will be as fast in general case and more readable.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is also just moved from mmap.rs, so I also err'd on the side of keeping things as they are. The default implementation for this trait function indeed just uses a simple loop.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

@@ -586,6 +587,168 @@ pub trait GuestMemory {
}
}

/// Errors that can occur when creating a memory map.
#[derive(Debug, thiserror::Error)]
pub enum GuestRegionCollectionError {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole GuestRegionCollection looks like it can be moved into it's own module. Also maybe rename to just Regions? I assume there are no other regions other than memory regions.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mh, I'd be open to having it in its own module. What should we call it though?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just regions.rs?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants