Skip to content

Commit 1245063

Browse files
alexandruagandreeaflorescu
authored andcommitted
add dirty bitmap tracking abstractions
Signed-off-by: Alexandru Agache <[email protected]>
1 parent 5670f8e commit 1245063

File tree

10 files changed

+494
-166
lines changed

10 files changed

+494
-166
lines changed

benches/guest_memory.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#![cfg(feature = "backend-mmap")]
55

66
pub use criterion::{black_box, Criterion};
7+
8+
use vm_memory::bitmap::Bitmap;
79
use vm_memory::{GuestAddress, GuestMemory, GuestMemoryMmap};
810

911
const REGION_SIZE: usize = 0x10_0000;
@@ -13,7 +15,10 @@ pub fn benchmark_for_guest_memory(c: &mut Criterion) {
1315
benchmark_find_region(c);
1416
}
1517

16-
fn find_region(mem: &GuestMemoryMmap) {
18+
fn find_region<B>(mem: &GuestMemoryMmap<B>)
19+
where
20+
B: Bitmap + 'static,
21+
{
1722
for i in 0..REGIONS_COUNT {
1823
let _ = mem
1924
.find_region(black_box(GuestAddress(i * REGION_SIZE as u64)))

benches/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use volatile::benchmark_for_volatile;
1717
#[cfg(feature = "backend-mmap")]
1818
// Use this function with caution. It does not check against overflows
1919
// and `GuestMemoryMmap::from_ranges` errors.
20-
fn create_guest_memory_mmap(size: usize, count: u64) -> GuestMemoryMmap {
20+
fn create_guest_memory_mmap(size: usize, count: u64) -> GuestMemoryMmap<()> {
2121
let mut regions: Vec<(GuestAddress, usize)> = Vec::new();
2222
for i in 0..count {
2323
regions.push((GuestAddress(i * size as u64), size));

src/atomic.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,11 +142,10 @@ impl<M: GuestMemory> GuestMemoryExclusiveGuard<'_, M> {
142142
#[cfg(feature = "backend-mmap")]
143143
mod tests {
144144
use super::*;
145-
use crate::{
146-
GuestAddress, GuestMemory, GuestMemoryMmap, GuestMemoryRegion, GuestRegionMmap, GuestUsize,
147-
MmapRegion,
148-
};
145+
use crate::{GuestAddress, GuestMemory, GuestMemoryRegion, GuestUsize, MmapRegion};
149146

147+
type GuestMemoryMmap = crate::GuestMemoryMmap<()>;
148+
type GuestRegionMmap = crate::GuestRegionMmap<()>;
150149
type GuestMemoryMmapAtomic = GuestMemoryAtomic<GuestMemoryMmap>;
151150

152151
#[test]

src/bitmap/mod.rs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
3+
4+
//! This module holds abstractions that enable tracking the areas dirtied by writes of a specified
5+
//! length to a given offset. In particular, this is used to track write accesses within a
6+
//! `GuestMemoryRegion` object, and the resulting bitmaps can then be aggregated to build the
7+
//! global view for an entire `GuestMemory` object.
8+
9+
use std::fmt::Debug;
10+
11+
use crate::{GuestMemory, GuestMemoryRegion};
12+
13+
/// Trait implemented by types that support creating `BitmapSlice` objects.
14+
pub trait WithBitmapSlice<'a> {
15+
/// Type of the bitmap slice.
16+
type S: BitmapSlice;
17+
}
18+
19+
/// Trait used to represent that a `BitmapSlice` is a `Bitmap` itself, but also satisfies the
20+
/// restriction that slices created from it have the same type as `Self`.
21+
pub trait BitmapSlice:
22+
Bitmap + Clone + Copy + Debug + for<'a> WithBitmapSlice<'a, S = Self>
23+
{
24+
}
25+
26+
/// Common bitmap operations. Using Higher-Rank Trait Bounds (HRTBs) to effectively define
27+
/// an associated type that has a lifetime parameter, without tagging the `Bitmap` trait with
28+
/// a lifetime as well.
29+
///
30+
/// Using an associated type allows implementing the `Bitmap` and `BitmapSlice` functionality
31+
/// as a zero-cost abstraction when providing trivial implementations such as the one
32+
/// defined for `()`.
33+
// These methods represent the core functionality that's required by `vm-memory` abstractions
34+
// to implement generic tracking logic, as well as tests that can be reused by different backends.
35+
pub trait Bitmap: for<'a> WithBitmapSlice<'a> {
36+
/// Mark the memory range specified by the given `offset` and `len` as dirtied.
37+
fn mark_dirty(&self, offset: usize, len: usize);
38+
39+
/// Check whether the specified `offset` is marked as dirty.
40+
fn dirty_at(&self, offset: usize) -> bool;
41+
42+
/// Return a `<Self as WithBitmapSlice>::S` slice of the current bitmap, starting at
43+
/// the specified `offset`.
44+
fn slice_at(&self, offset: usize) -> <Self as WithBitmapSlice>::S;
45+
}
46+
47+
/// A no-op `Bitmap` implementation that can be provided for backends that do not actually
48+
/// require the tracking functionality.
49+
50+
impl<'a> WithBitmapSlice<'a> for () {
51+
type S = Self;
52+
}
53+
54+
impl BitmapSlice for () {}
55+
56+
impl Bitmap for () {
57+
fn mark_dirty(&self, _offset: usize, _len: usize) {}
58+
59+
fn dirty_at(&self, _offset: usize) -> bool {
60+
false
61+
}
62+
63+
fn slice_at(&self, _offset: usize) -> Self {}
64+
}
65+
66+
/// A `Bitmap` and `BitmapSlice` implementation for `Option<B>`.
67+
68+
impl<'a, B> WithBitmapSlice<'a> for Option<B>
69+
where
70+
B: WithBitmapSlice<'a>,
71+
{
72+
type S = Option<B::S>;
73+
}
74+
75+
impl<B: BitmapSlice> BitmapSlice for Option<B> {}
76+
77+
impl<B: Bitmap> Bitmap for Option<B> {
78+
fn mark_dirty(&self, offset: usize, len: usize) {
79+
if let Some(inner) = self {
80+
inner.mark_dirty(offset, len)
81+
}
82+
}
83+
84+
fn dirty_at(&self, offset: usize) -> bool {
85+
if let Some(inner) = self {
86+
return inner.dirty_at(offset);
87+
}
88+
false
89+
}
90+
91+
fn slice_at(&self, offset: usize) -> Option<<B as WithBitmapSlice>::S> {
92+
if let Some(inner) = self {
93+
return Some(inner.slice_at(offset));
94+
}
95+
None
96+
}
97+
}
98+
99+
/// Helper type alias for referring to the `BitmapSlice` concrete type associated with
100+
/// an object `B: WithBitmapSlice<'a>`.
101+
pub type BS<'a, B> = <B as WithBitmapSlice<'a>>::S;
102+
103+
/// Helper type alias for referring to the `BitmapSlice` concrete type associated with
104+
/// the memory regions of an object `M: GuestMemory`.
105+
pub type MS<'a, M> = BS<'a, <<M as GuestMemory>::R as GuestMemoryRegion>::B>;

src/bytes.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use std::slice::{from_raw_parts, from_raw_parts_mut};
1818
use std::sync::atomic::Ordering;
1919

2020
use crate::atomic_integer::AtomicInteger;
21-
use crate::VolatileSlice;
21+
use crate::volatile_memory::VolatileSlice;
2222

2323
/// Types for which it is safe to initialize from raw data.
2424
///

src/guest_memory.rs

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@
3232
//! Whenever a collection of `GuestMemoryRegion` objects is mutable,
3333
//! [`GuestAddressSpace`](trait.GuestAddressSpace.html) should be implemented
3434
//! for clients to obtain a [`GuestMemory`] reference or smart pointer.
35+
//!
36+
//! The `GuestMemoryRegion` trait has an associated `B: Bitmap` type which is used to handle
37+
//! dirty bitmap tracking. Backends are free to define the granularity (or whether tracking is
38+
//! actually performed at all). Those that do implement tracking functionality are expected to
39+
//! ensure the correctness of the underlying `Bytes` implementation. The user has to explicitly
40+
//! record (using the handle returned by `GuestRegionMmap::bitmap`) write accesses performed
41+
//! via pointers, references, or slices returned by methods of `GuestMemory`,`GuestMemoryRegion`,
42+
//! `VolatileSlice`, `VolatileRef`, or `VolatileArrayRef`.
3543
3644
use std::convert::From;
3745
use std::fmt::{self, Display};
@@ -43,8 +51,9 @@ use std::sync::atomic::Ordering;
4351
use std::sync::Arc;
4452

4553
use crate::address::{Address, AddressValue};
54+
use crate::bitmap::{Bitmap, BS, MS};
4655
use crate::bytes::{AtomicAccess, Bytes};
47-
use crate::volatile_memory;
56+
use crate::volatile_memory::{self, VolatileSlice};
4857

4958
static MAX_ACCESS_CHUNK: usize = 4096;
5059

@@ -165,6 +174,9 @@ impl FileOffset {
165174
/// Represents a continuous region of guest physical memory.
166175
#[allow(clippy::len_without_is_empty)]
167176
pub trait GuestMemoryRegion: Bytes<MemoryRegionAddress, E = Error> {
177+
/// Type used for dirty memory tracking.
178+
type B: Bitmap;
179+
168180
/// Returns the size of the region.
169181
fn len(&self) -> GuestUsize;
170182

@@ -177,6 +189,9 @@ pub trait GuestMemoryRegion: Bytes<MemoryRegionAddress, E = Error> {
177189
self.start_addr().unchecked_add(self.len() - 1)
178190
}
179191

192+
/// Borrow the associated `Bitmap` object.
193+
fn bitmap(&self) -> &Self::B;
194+
180195
/// Returns the given address if it is within this region.
181196
fn check_address(&self, addr: MemoryRegionAddress) -> Option<MemoryRegionAddress> {
182197
if self.address_in_range(addr) {
@@ -245,7 +260,9 @@ pub trait GuestMemoryRegion: Bytes<MemoryRegionAddress, E = Error> {
245260
///
246261
/// # Safety
247262
///
248-
/// Unsafe because of possible aliasing.
263+
/// Unsafe because of possible aliasing. Mutable accesses performed through the
264+
/// returned slice are not visible to the dirty bitmap tracking functionality of
265+
/// the region, and must be manually recorded using the associated bitmap object.
249266
unsafe fn as_mut_slice(&self) -> Option<&mut [u8]> {
250267
None
251268
}
@@ -257,7 +274,7 @@ pub trait GuestMemoryRegion: Bytes<MemoryRegionAddress, E = Error> {
257274
&self,
258275
offset: MemoryRegionAddress,
259276
count: usize,
260-
) -> Result<volatile_memory::VolatileSlice> {
277+
) -> Result<VolatileSlice<BS<Self::B>>> {
261278
Err(Error::HostAddressNotAvailable)
262279
}
263280

@@ -286,7 +303,7 @@ pub trait GuestMemoryRegion: Bytes<MemoryRegionAddress, E = Error> {
286303
/// assert_eq!(r.load(), v);
287304
/// # }
288305
/// ```
289-
fn as_volatile_slice(&self) -> Result<volatile_memory::VolatileSlice> {
306+
fn as_volatile_slice(&self) -> Result<VolatileSlice<BS<Self::B>>> {
290307
self.get_slice(MemoryRegionAddress(0), self.len() as usize)
291308
}
292309

@@ -386,26 +403,26 @@ pub trait GuestAddressSpace {
386403
}
387404

388405
impl<M: GuestMemory> GuestAddressSpace for &M {
389-
type T = Self;
390406
type M = M;
407+
type T = Self;
391408

392409
fn memory(&self) -> Self {
393410
self
394411
}
395412
}
396413

397414
impl<M: GuestMemory> GuestAddressSpace for Rc<M> {
398-
type T = Self;
399415
type M = M;
416+
type T = Self;
400417

401418
fn memory(&self) -> Self {
402419
self.clone()
403420
}
404421
}
405422

406423
impl<M: GuestMemory> GuestAddressSpace for Arc<M> {
407-
type T = Self;
408424
type M = M;
425+
type T = Self;
409426

410427
fn memory(&self) -> Self {
411428
self.clone()
@@ -705,11 +722,7 @@ pub trait GuestMemory {
705722

706723
/// Returns a [`VolatileSlice`](struct.VolatileSlice.html) of `count` bytes starting at
707724
/// `addr`.
708-
fn get_slice(
709-
&self,
710-
addr: GuestAddress,
711-
count: usize,
712-
) -> Result<volatile_memory::VolatileSlice> {
725+
fn get_slice(&self, addr: GuestAddress, count: usize) -> Result<VolatileSlice<MS<Self>>> {
713726
self.to_region_addr(addr)
714727
.ok_or(Error::InvalidGuestAddress(addr))
715728
.and_then(|(r, addr)| r.get_slice(addr, count))
@@ -836,22 +849,29 @@ impl<T: GuestMemory> Bytes<GuestAddress> for T {
836849
// Check if something bad happened before doing unsafe things.
837850
assert!(offset <= count);
838851
if let Some(dst) = unsafe { region.as_mut_slice() } {
839-
// This is safe cause `start` and `len` are within the `region`.
852+
// This is safe cause `start` and `len` are within the `region`, and we manually
853+
// record the dirty status of the written range below.
840854
let start = caddr.raw_value() as usize;
841855
let end = start + len;
842-
loop {
856+
let result = loop {
843857
match src.read(&mut dst[start..end]) {
844858
Ok(n) => break Ok(n),
845859
Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
846860
Err(e) => break Err(Error::IOError(e)),
847861
}
848-
}
862+
};
863+
864+
region.bitmap().mark_dirty(start, len);
865+
result
849866
} else {
850867
let len = std::cmp::min(len, MAX_ACCESS_CHUNK);
851868
let mut buf = vec![0u8; len].into_boxed_slice();
852869
loop {
853870
match src.read(&mut buf[..]) {
854871
Ok(bytes_read) => {
872+
// We don't need to update the dirty bitmap manually here because it's
873+
// expected to be handled by the logic within the `Bytes`
874+
// implementation for the region object.
855875
let bytes_written = region.write(&buf[0..bytes_read], caddr)?;
856876
assert_eq!(bytes_written, bytes_read);
857877
break Ok(bytes_read);
@@ -1008,14 +1028,17 @@ mod tests {
10081028
#[cfg(feature = "backend-mmap")]
10091029
use crate::bytes::ByteValued;
10101030
#[cfg(feature = "backend-mmap")]
1011-
use crate::{GuestAddress, GuestMemoryMmap};
1031+
use crate::GuestAddress;
10121032
#[cfg(feature = "backend-mmap")]
10131033
use std::io::Cursor;
10141034
#[cfg(feature = "backend-mmap")]
10151035
use std::time::{Duration, Instant};
10161036

10171037
use vmm_sys_util::tempfile::TempFile;
10181038

1039+
#[cfg(feature = "backend-mmap")]
1040+
type GuestMemoryMmap = crate::GuestMemoryMmap<()>;
1041+
10191042
#[cfg(feature = "backend-mmap")]
10201043
fn make_image(size: u8) -> Vec<u8> {
10211044
let mut image: Vec<u8> = Vec::with_capacity(size as usize);

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ pub use atomic::{GuestMemoryAtomic, GuestMemoryLoadGuard};
3131
mod atomic_integer;
3232
pub use atomic_integer::AtomicInteger;
3333

34+
pub mod bitmap;
35+
3436
pub mod bytes;
3537
pub use bytes::{AtomicAccess, ByteValued, Bytes};
3638

0 commit comments

Comments
 (0)