Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
# Changelog

## Upcoming version
## 0.18.0

### Changed

This release includes a major change to the `vm-memory` API in order to support
memory access permissions. The previous `GuestMemory` trait is now renamed
to `GuestMemoryBackend`, and `GuestMemory` has a completely new definition.
However, common interfaces such as `GuestAddressSpace` and `Bytes` remain on
`GuestMemory`; therefore, all callers that just use the `Bytes` interface
remain completely unchanged.

- \[[#362](https://github.com/rust-vmm/vm-memory/pull/362)\] Rename `GuestMemory` to `GuestMemoryBackend`, `IoMemory` to `GuestMemoryBackend`
Copy link
Contributor

Choose a reason for hiding this comment

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

Typo: IoMemory to GuestMemoryBackend GuestMemory


### Added

- \[[#327](https://github.com/rust-vmm/vm-memory/pull/327)\] I/O virtual memory support via `IoMemory`, `IommuMemory`, and `Iommu`/`Iotlb`

### Fixed

- \[[#361](https://github.com/rust-vmm/vm-memory/pull/361)\] clippy: allow mut_from_ref

## \[v0.17.1\]

No visible changes.
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "vm-memory"
version = "0.17.1"
version = "0.18.0"
description = "Safe abstractions for accessing the VM physical memory"
keywords = ["memory"]
categories = ["memory-management"]
Expand Down
57 changes: 29 additions & 28 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,28 +74,29 @@ of the VM using the following traits:
`u64` is used to store the the raw value no matter if it is a 32-bit or
a 64-bit virtual machine.
- `GuestMemoryRegion`: represents a continuous region of the VM memory.
- `GuestMemory`: represents a collection of `GuestMemoryRegion` objects. The
main responsibilities of the `GuestMemory` trait are:
- hide the detail of accessing physical addresses (for example complex
hierarchical structures).
- `GuestMemoryBackend`: represents a collection of `GuestMemoryRegion` objects. The
main responsibilities of the `GuestMemoryBackend` trait are:
- map an address request to a `GuestMemoryRegion` object and relay the
request to it.
- handle cases where an access request is spanning two or more
`GuestMemoryRegion` objects.
- `GuestMemory`: the primary external interface; it adds permission checks
to `GuestMemoryBackend`, and is more suited to implementations that
have a potentially very large set of non-continuous mappings.

The VM memory consumers should only rely on traits and structs defined here to
access VM's physical memory and not on the implementation of the traits.

### Backend Implementation Based on `mmap`

Provides an implementation of the `GuestMemory` trait by mmapping the VM's physical
Provides an implementation of the `GuestMemoryBackend` trait by mmapping the VM's physical
memory into the current process.

- `MmapRegion`: implementation of mmap a continuous range of physical memory
with methods for accessing the mapped memory.
- `GuestRegionMmap`: implementation of `GuestMemoryRegion` providing a wrapper
used to map VM's physical address into a `(mmap_region, offset)` tuple.
- `GuestMemoryMmap`: implementation of `GuestMemory` that manages a collection
- `GuestMemoryMmap`: implementation of `GuestMemoryBackend` that manages a collection
of `GuestRegionMmap` objects for a VM.

One of the main responsibilities of `GuestMemoryMmap` is to handle the use
Expand Down Expand Up @@ -125,25 +126,25 @@ let result = guest_memory_mmap.write(buf, addr);
### I/O Virtual Address Space

When using an IOMMU, there no longer is direct access to the guest (physical)
address space, but instead only to I/O virtual address space. In this case:

- `IoMemory` replaces `GuestMemory`: It requires specifying the required access
permissions (which are relevant for virtual memory). It also removes
interfaces that imply a mostly linear memory layout, because virtual memory is
fragmented into many pages instead of few (large) memory regions.
- Any `IoMemory` still has a `GuestMemory` inside as the underlying address
space, but if an IOMMU is used, that will generally not be guest physical
address space. With vhost-user, for example, it will be the VMM’s user
address space instead.
- `IommuMemory` as our only actually IOMMU-supporting `IoMemory`
implementation uses an `Iommu` object to translate I/O virtual addresses
(IOVAs) into VMM user addresses (VUAs), which are then passed to the inner
`GuestMemory` implementation (like `GuestMemoryMmap`).
- `GuestAddress` (for compatibility) refers to an address in any of these
address spaces:
- Guest physical addresses (GPAs) when no IOMMU is used,
- I/O virtual addresses (IOVAs),
- VMM user addresses (VUAs).
address space, but instead only to I/O virtual address space. In order to
support this usecase, `GuestMemory` (unlike `GuestMemoryBackend`) requires
specifying the required access permissions (which are relevant for virtual
memory).

`GuestMemory` can still use `GuestMemoryBackend` inside as the underlying
address space, but if an IOMMU is used, that may not be the guest
physical address space. With vhost-user, for example, it will be the
VMM’s user address space instead. For compatibility, `GuestAddress`
can refer to an address in any address space:
- Guest physical addresses (GPAs) when no IOMMU is used,
- I/O virtual addresses (IOVAs),
- VMM user addresses (VUAs).

`vm-memory` provides an example implementation of `GuestMemory` when
compiled with the `iommu` feature. `IommuMemory` uses an `Iommu`
object to translate I/O virtual addresses (IOVAs) into VMM user addresses
(VUAs), which are then passed to the inner `GuestMemoryBackend`
implementation (like `GuestMemoryMmap`).

### Utilities and Helpers

Expand All @@ -166,8 +167,8 @@ with minor changes:
- `Address` inherits `AddressValue`
- `GuestMemoryRegion` inherits `Bytes<MemoryRegionAddress, E = Error>`. The
`Bytes` trait must be implemented.
- `GuestMemory` has a generic implementation of `IoMemory`
- `IoMemory` has a generic implementation of `Bytes<GuestAddress>`.
- `GuestMemoryBackend` has a generic implementation of `GuestMemory`
- `GuestMemory` has a generic implementation of `Bytes<GuestAddress>`.

**Types**:

Expand All @@ -178,6 +179,6 @@ with minor changes:

- `MmapRegion` implements `VolatileMemory`
- `GuestRegionMmap` implements `Bytes<MemoryRegionAddress> + GuestMemoryRegion`
- `GuestMemoryMmap` implements `GuestMemory`
- `GuestMemoryMmap` implements `GuestMemoryBackend`
- `VolatileSlice` implements
`Bytes<usize, E = volatile_memory::Error> + VolatileMemory`
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Then add `extern crate vm-memory;` to your crate root.
## Examples

- Creating a VM physical memory object in hypervisor specific ways using the
`GuestMemoryMmap` implementation of the `GuestMemory` trait:
`GuestMemoryMmap` implementation of the `GuestMemoryBackend` trait:

```rust
fn provide_mem_to_virt_dev() {
Expand All @@ -77,7 +77,7 @@ fn provide_mem_to_virt_dev() {
- Consumers accessing the VM's physical memory:

```rust
fn virt_device_io<T: GuestMemory>(mem: &T) {
fn virt_device_io<T: GuestMemoryBackend>(mem: &T) {
let sample_buf = &[1, 2, 3, 4, 5];
assert_eq!(mem.write(sample_buf, GuestAddress(0xffc)).unwrap(), 5);
let buf = &mut [0u8; 5];
Expand Down
2 changes: 1 addition & 1 deletion benches/guest_memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use core::hint::black_box;
pub use criterion::Criterion;

use vm_memory::bitmap::Bitmap;
use vm_memory::{GuestAddress, GuestMemory, GuestMemoryMmap};
use vm_memory::{GuestAddress, GuestMemoryBackend, GuestMemoryMmap};

const REGION_SIZE: usize = 0x10_0000;
const REGIONS_COUNT: u64 = 256;
Expand Down
2 changes: 1 addition & 1 deletion benches/mmap/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use std::path::Path;
use core::hint::black_box;
use criterion::Criterion;

use vm_memory::{ByteValued, Bytes, GuestAddress, GuestMemory};
use vm_memory::{ByteValued, Bytes, GuestAddress, GuestMemoryBackend};

const REGION_SIZE: usize = 0x8000_0000;
const REGIONS_COUNT: u64 = 8;
Expand Down
36 changes: 18 additions & 18 deletions src/atomic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Copyright (C) 2020 Red Hat, Inc. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

//! A wrapper over an `ArcSwap<IoMemory>` struct to support RCU-style mutability.
//! A wrapper over an `ArcSwap<GuestMemory>` struct to support RCU-style mutability.
//!
//! With the `backend-atomic` feature enabled, simply replacing `GuestMemoryMmap`
//! with `GuestMemoryAtomic<GuestMemoryMmap>` will enable support for mutable memory maps.
Expand All @@ -15,17 +15,17 @@ use arc_swap::{ArcSwap, Guard};
use std::ops::Deref;
use std::sync::{Arc, LockResult, Mutex, MutexGuard, PoisonError};

use crate::{GuestAddressSpace, IoMemory};
use crate::{GuestAddressSpace, GuestMemory};

/// A fast implementation of a mutable collection of memory regions.
///
/// This implementation uses `ArcSwap` to provide RCU-like snapshotting of the memory map:
/// every update of the memory map creates a completely new `IoMemory` object, and
/// every update of the memory map creates a completely new `GuestMemory` object, and
/// readers will not be blocked because the copies they retrieved will be collected once
/// no one can access them anymore. Under the assumption that updates to the memory map
/// are rare, this allows a very efficient implementation of the `memory()` method.
#[derive(Debug)]
pub struct GuestMemoryAtomic<M: IoMemory> {
pub struct GuestMemoryAtomic<M: GuestMemory> {
// GuestAddressSpace<M>, which we want to implement, is basically a drop-in
// replacement for &M. Therefore, we need to pass to devices the `GuestMemoryAtomic`
// rather than a reference to it. To obtain this effect we wrap the actual fields
Expand All @@ -34,9 +34,9 @@ pub struct GuestMemoryAtomic<M: IoMemory> {
inner: Arc<(ArcSwap<M>, Mutex<()>)>,
}

impl<M: IoMemory> From<Arc<M>> for GuestMemoryAtomic<M> {
impl<M: GuestMemory> From<Arc<M>> for GuestMemoryAtomic<M> {
/// create a new `GuestMemoryAtomic` object whose initial contents come from
/// the `map` reference counted `IoMemory`.
/// the `map` reference counted `GuestMemory`.
fn from(map: Arc<M>) -> Self {
let inner = (ArcSwap::new(map), Mutex::new(()));
GuestMemoryAtomic {
Expand All @@ -45,9 +45,9 @@ impl<M: IoMemory> From<Arc<M>> for GuestMemoryAtomic<M> {
}
}

impl<M: IoMemory> GuestMemoryAtomic<M> {
impl<M: GuestMemory> GuestMemoryAtomic<M> {
/// create a new `GuestMemoryAtomic` object whose initial contents come from
/// the `map` `IoMemory`.
/// the `map` `GuestMemory`.
pub fn new(map: M) -> Self {
Arc::new(map).into()
}
Expand Down Expand Up @@ -75,15 +75,15 @@ impl<M: IoMemory> GuestMemoryAtomic<M> {
}
}

impl<M: IoMemory> Clone for GuestMemoryAtomic<M> {
impl<M: GuestMemory> Clone for GuestMemoryAtomic<M> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}

impl<M: IoMemory> GuestAddressSpace for GuestMemoryAtomic<M> {
impl<M: GuestMemory> GuestAddressSpace for GuestMemoryAtomic<M> {
type T = GuestMemoryLoadGuard<M>;
type M = M;

Expand All @@ -94,14 +94,14 @@ impl<M: IoMemory> GuestAddressSpace for GuestMemoryAtomic<M> {

/// A guard that provides temporary access to a `GuestMemoryAtomic`. This
/// object is returned from the `memory()` method. It dereference to
/// a snapshot of the `IoMemory`, so it can be used transparently to
/// a snapshot of the `GuestMemory`, so it can be used transparently to
/// access memory.
#[derive(Debug)]
pub struct GuestMemoryLoadGuard<M: IoMemory> {
pub struct GuestMemoryLoadGuard<M: GuestMemory> {
guard: Guard<Arc<M>>,
}

impl<M: IoMemory> GuestMemoryLoadGuard<M> {
impl<M: GuestMemory> GuestMemoryLoadGuard<M> {
/// Make a clone of the held pointer and returns it. This is more
/// expensive than just using the snapshot, but it allows to hold on
/// to the snapshot outside the scope of the guard. It also allows
Expand All @@ -112,15 +112,15 @@ impl<M: IoMemory> GuestMemoryLoadGuard<M> {
}
}

impl<M: IoMemory> Clone for GuestMemoryLoadGuard<M> {
impl<M: GuestMemory> Clone for GuestMemoryLoadGuard<M> {
fn clone(&self) -> Self {
GuestMemoryLoadGuard {
guard: Guard::from_inner(Arc::clone(&*self.guard)),
}
}
}

impl<M: IoMemory> Deref for GuestMemoryLoadGuard<M> {
impl<M: GuestMemory> Deref for GuestMemoryLoadGuard<M> {
type Target = M;

fn deref(&self) -> &Self::Target {
Expand All @@ -133,12 +133,12 @@ impl<M: IoMemory> Deref for GuestMemoryLoadGuard<M> {
/// possibly after updating the memory map represented by the
/// `GuestMemoryAtomic` that created the guard.
#[derive(Debug)]
pub struct GuestMemoryExclusiveGuard<'a, M: IoMemory> {
pub struct GuestMemoryExclusiveGuard<'a, M: GuestMemory> {
parent: &'a GuestMemoryAtomic<M>,
_guard: MutexGuard<'a, ()>,
}

impl<M: IoMemory> GuestMemoryExclusiveGuard<'_, M> {
impl<M: GuestMemory> GuestMemoryExclusiveGuard<'_, M> {
/// Replace the memory map in the `GuestMemoryAtomic` that created the guard
/// with the new memory map, `map`. The lock is then dropped since this
/// method consumes the guard.
Expand All @@ -151,7 +151,7 @@ impl<M: IoMemory> GuestMemoryExclusiveGuard<'_, M> {
mod tests {
use super::*;
use crate::region::tests::{new_guest_memory_collection_from_regions, Collection, MockRegion};
use crate::{GuestAddress, GuestMemory, GuestMemoryRegion, GuestUsize, IoMemory};
use crate::{GuestAddress, GuestMemory, GuestMemoryBackend, GuestMemoryRegion, GuestUsize};

type GuestMemoryMmapAtomic = GuestMemoryAtomic<Collection>;

Expand Down
10 changes: 5 additions & 5 deletions src/bitmap/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
//! This module holds abstractions that enable tracking the areas dirtied by writes of a specified
//! length to a given offset. In particular, this is used to track write accesses within a
//! `GuestMemoryRegion` object, and the resulting bitmaps can then be aggregated to build the
//! global view for an entire `GuestMemory` object.
//! global view for an entire `GuestMemoryBackend` object.

#[cfg(feature = "backend-bitmap")]
mod backend;

use std::fmt::Debug;

use crate::{GuestMemory, GuestMemoryRegion};
use crate::{GuestMemoryBackend, GuestMemoryRegion};

#[cfg(feature = "backend-bitmap")]
pub use backend::{ArcSlice, AtomicBitmap, RefSlice};
Expand Down Expand Up @@ -113,8 +113,8 @@ impl<B: Bitmap> Bitmap for Option<B> {
pub type BS<'a, B> = <B as WithBitmapSlice<'a>>::S;

/// Helper type alias for referring to the `BitmapSlice` concrete type associated with
/// the memory regions of an object `M: GuestMemory`.
pub type MS<'a, M> = BS<'a, <<M as GuestMemory>::R as GuestMemoryRegion>::B>;
/// the memory regions of an object `M: GuestMemoryBackend`.
pub type MS<'a, M> = BS<'a, <<M as GuestMemoryBackend>::R as GuestMemoryRegion>::B>;

#[cfg(test)]
#[cfg(feature = "backend-bitmap")]
Expand Down Expand Up @@ -257,7 +257,7 @@ pub(crate) mod tests {
// Assumptions about M generated by f ...
pub fn test_guest_memory_and_region<M, F>(f: F)
where
M: GuestMemory,
M: GuestMemoryBackend,
F: Fn() -> M,
{
let m = f();
Expand Down
2 changes: 1 addition & 1 deletion src/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ pub trait Bytes<A> {
/// ```
/// # #[cfg(all(feature = "backend-mmap", feature = "rawfd"))]
/// # {
/// # use vm_memory::{Address, GuestMemory, Bytes, GuestAddress, GuestMemoryMmap};
/// # use vm_memory::{Address, GuestMemoryBackend, Bytes, GuestAddress, GuestMemoryMmap};
/// # use std::fs::File;
/// # use std::path::Path;
/// #
Expand Down
Loading