Skip to content

Commit 1d68e61

Browse files
bonzinijiangliu
authored andcommitted
guest_memory: introduce GuestAddressSpace
This provides a way for devices to access memory independent on how the whole program stores the memory map. To do so, devices use GuestAddressSpace instead of something like &'a M or Arc<M>, so that the VMM can optionally define a smart pointer that will guard accesses to the memory; for example, the result of memory() could be a lock guard such as a RwLockReadGuard. This would not be very scalable but it would be simple. If the VMM instead uses an immutable memory map, i.e. a GuestMemory, vm-memory already provides trivial implementations of the trait that works for both references and reference-counted objects. For references, the implementation imposes no abstraction cost on device that use GuestAddressSpace<M> instead of &'a M. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent 9e252ee commit 1d68e61

File tree

3 files changed

+136
-8
lines changed

3 files changed

+136
-8
lines changed

src/guest_memory.rs

Lines changed: 101 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,17 @@
2626
//! - hide the detail of accessing guest's physical address.
2727
//! - map a request address to a GuestMemoryRegion object and relay the request to it.
2828
//! - handle cases where an access request spanning two or more GuestMemoryRegion objects.
29+
//!
30+
//! Whenever a collection of GuestMemoryRegion objects is mutable,
31+
//! [GuestAddressSpace](trait.GuestAddressSpace.html) should be implemented
32+
//! for clients to obtain a [GuestMemory] reference or smart pointer.
2933
3034
use std::convert::From;
3135
use std::fmt::{self, Display};
3236
use std::fs::File;
3337
use std::io::{self, Read, Write};
34-
use std::ops::{BitAnd, BitOr};
38+
use std::ops::{BitAnd, BitOr, Deref};
39+
use std::rc::Rc;
3540
use std::sync::Arc;
3641

3742
use crate::address::{Address, AddressValue};
@@ -243,14 +248,104 @@ pub trait GuestMemoryRegion: Bytes<MemoryRegionAddress, E = Error> {
243248
}
244249
}
245250

246-
/// Represents a container for a collection of GuestMemoryRegion objects.
251+
/// GuestAddressSpace provides a way to retrieve a GuestMemory object.
252+
/// The vm-memory crate already provides trivial implementation for
253+
/// references to GuestMemory or reference-counted GuestMemory objects,
254+
/// but the trait can also be implemented by any other struct in order
255+
/// to provide temporary access to a snapshot of the memory map.
256+
///
257+
/// In order to support generic mutable memory maps, devices (or other things
258+
/// that access memory) should store the memory as a GuestAddressSpace<M>.
259+
/// This example shows that references can also be used as the GuestAddressSpace
260+
/// implementation, providing a zero-cost abstraction whenever immutable memory
261+
/// maps are sufficient.
262+
///
263+
/// ```
264+
/// # use std::sync::Arc;
265+
/// # #[cfg(feature = "backend-mmap")]
266+
/// # use vm_memory::GuestMemoryMmap;
267+
/// # use vm_memory::{GuestAddress, GuestMemory, GuestAddressSpace};
268+
///
269+
/// pub struct VirtioDevice<AS: GuestAddressSpace> {
270+
/// mem: Option<AS>,
271+
/// }
272+
///
273+
/// impl<AS: GuestAddressSpace> VirtioDevice<AS> {
274+
/// fn new() -> Self {
275+
/// VirtioDevice { mem: None }
276+
/// }
277+
/// fn activate(&mut self, mem: AS) {
278+
/// self.mem = Some(mem)
279+
/// }
280+
/// }
281+
///
282+
/// # #[cfg(feature = "backend-mmap")]
283+
/// # fn get_mmap() -> GuestMemoryMmap {
284+
/// # GuestMemoryMmap::from_ranges(&[(GuestAddress(0),0)]).unwrap()
285+
/// # }
286+
///
287+
/// # #[cfg(feature = "backend-mmap")]
288+
/// # fn test_1() {
289+
/// // Using `VirtioDevice` with an immutable GuestMemoryMmap:
290+
/// let mut for_immutable_mmap: VirtioDevice<&GuestMemoryMmap> =
291+
/// VirtioDevice::new();
292+
/// let mmap = get_mmap();
293+
/// for_immutable_mmap.activate(&mmap);
294+
/// let mut another: VirtioDevice<&GuestMemoryMmap> =
295+
/// VirtioDevice::new();
296+
/// another.activate(&mmap);
297+
/// # }
298+
/// ```
299+
300+
pub trait GuestAddressSpace {
301+
/// The type that will be used to access guest memory.
302+
type M: GuestMemory;
303+
304+
/// A type that provides access to the memory.
305+
type T: Deref<Target = Self::M>;
306+
307+
/// Return an object (e.g. a reference or guard) that can be used
308+
/// to access memory through this address space. The object provides
309+
/// a consistent snapshot of the memory map.
310+
fn memory(&self) -> Self::T;
311+
}
312+
313+
impl<M: GuestMemory> GuestAddressSpace for &M {
314+
type T = Self;
315+
type M = M;
316+
317+
fn memory(&self) -> Self {
318+
self
319+
}
320+
}
321+
322+
impl<M: GuestMemory> GuestAddressSpace for Rc<M> {
323+
type T = Self;
324+
type M = M;
325+
326+
fn memory(&self) -> Self {
327+
self.clone()
328+
}
329+
}
330+
331+
impl<M: GuestMemory> GuestAddressSpace for Arc<M> {
332+
type T = Self;
333+
type M = M;
334+
335+
fn memory(&self) -> Self {
336+
self.clone()
337+
}
338+
}
339+
340+
/// GuestMemory represents a container for an *immutable* collection of
341+
/// GuestMemoryRegion objects. GuestMemory provides the `Bytes<GuestAddress>`
342+
/// trait to hide the details of accessing guest memory by physical address.
343+
/// Interior mutability is not allowed for implementations of GuestMemory so
344+
/// that they always provide a consistent view of the memory map.
247345
///
248-
/// The main responsibilities of the GuestMemory trait are:
249-
/// - hide the detail of accessing guest's physical address.
346+
/// The task of the GuestMemory trait are:
250347
/// - map a request address to a GuestMemoryRegion object and relay the request to it.
251348
/// - handle cases where an access request spanning two or more GuestMemoryRegion objects.
252-
///
253-
/// Note: the regions inside a [`GuestMemory`](trait.GuestMemory.html) object must not overlap.
254349
pub trait GuestMemory {
255350
/// Type of objects hosted by the address space.
256351
type R: GuestMemoryRegion;

src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ pub use endian::{Be16, Be32, Be64, BeSize, Le16, Le32, Le64, LeSize};
3030

3131
pub mod guest_memory;
3232
pub use guest_memory::{
33-
Error as GuestMemoryError, FileOffset, GuestAddress, GuestMemory, GuestMemoryRegion,
34-
GuestUsize, MemoryRegionAddress, Result as GuestMemoryResult,
33+
Error as GuestMemoryError, FileOffset, GuestAddress, GuestAddressSpace, GuestMemory,
34+
GuestMemoryRegion, GuestUsize, MemoryRegionAddress, Result as GuestMemoryResult,
3535
};
3636

3737
#[cfg(all(feature = "backend-mmap", unix))]

src/mmap.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,7 @@ mod tests {
518518
extern crate vmm_sys_util;
519519

520520
use super::*;
521+
use crate::GuestAddressSpace;
521522

522523
use std::fs::File;
523524
use std::mem;
@@ -1093,6 +1094,38 @@ mod tests {
10931094
assert_eq!(gm.clone().regions[1].guest_base, regions[1].0);
10941095
}
10951096

1097+
#[test]
1098+
fn test_memory() {
1099+
let region_size = 0x400;
1100+
let regions = vec![
1101+
(GuestAddress(0x0), region_size),
1102+
(GuestAddress(0x1000), region_size),
1103+
];
1104+
let mut iterated_regions = Vec::new();
1105+
let gm = Arc::new(GuestMemoryMmap::from_ranges(&regions).unwrap());
1106+
let mem = gm.memory();
1107+
1108+
let res: guest_memory::Result<()> = mem.with_regions(|_, region| {
1109+
assert_eq!(region.len(), region_size as GuestUsize);
1110+
Ok(())
1111+
});
1112+
assert!(res.is_ok());
1113+
let res: guest_memory::Result<()> = mem.with_regions_mut(|_, region| {
1114+
iterated_regions.push((region.start_addr(), region.len() as usize));
1115+
Ok(())
1116+
});
1117+
assert!(res.is_ok());
1118+
assert_eq!(regions, iterated_regions);
1119+
1120+
assert!(regions
1121+
.iter()
1122+
.map(|x| (x.0, x.1))
1123+
.eq(iterated_regions.iter().map(|x| *x)));
1124+
1125+
assert_eq!(gm.clone().regions[0].guest_base, regions[0].0);
1126+
assert_eq!(gm.clone().regions[1].guest_base, regions[1].0);
1127+
}
1128+
10961129
#[test]
10971130
fn test_access_cross_boundary() {
10981131
let f1 = TempFile::new().unwrap().into_file();

0 commit comments

Comments
 (0)