Skip to content

Commit 9070fb5

Browse files
committed
implement builder for MmapRegion
Currently the MmapRegion provides several methods to create MmapRegion objects, but we often need to change the existing methods to support new functionalities. So it would be better to adopt Builder pattern and implement MmapRegionBuilder, so we could add new functionality without affecting existing users. Signed-off-by: Liu Jiang <[email protected]> Signed-off-by: Alexandru Agache <[email protected]>
1 parent 6457090 commit 9070fb5

File tree

4 files changed

+182
-67
lines changed

4 files changed

+182
-67
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
### Added
55

6+
- [[#149]](https://github.com/rust-vmm/vm-memory/issues/149): Implement builder for MmapRegion.
67
- [[#140]](https://github.com/rust-vmm/vm-memory/issues/140): Add dirty bitmap tracking abstractions.
78

89
### Deprecated

coverage_config_x86_64.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"coverage_score": 88.1,
2+
"coverage_score": 88.5,
33
"exclude_path": "mmap_windows.rs",
44
"crate_features": "backend-mmap,backend-atomic,backend-bitmap"
55
}

src/mmap.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use crate::volatile_memory::{VolatileMemory, VolatileSlice};
3131
use crate::{AtomicAccess, Bytes};
3232

3333
#[cfg(unix)]
34-
pub use crate::mmap_unix::{Error as MmapRegionError, MmapRegion};
34+
pub use crate::mmap_unix::{Error as MmapRegionError, MmapRegion, MmapRegionBuilder};
3535

3636
#[cfg(windows)]
3737
pub use crate::mmap_windows::MmapRegion;

src/mmap_unix.rs

Lines changed: 179 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,154 @@ impl error::Error for Error {}
7474

7575
pub type Result<T> = result::Result<T, Error>;
7676

77+
/// A factory struct to build `MmapRegion` objects.
78+
pub struct MmapRegionBuilder<B = ()> {
79+
size: usize,
80+
prot: i32,
81+
flags: i32,
82+
file_offset: Option<FileOffset>,
83+
raw_ptr: Option<*mut u8>,
84+
hugetlbfs: Option<bool>,
85+
bitmap: B,
86+
}
87+
88+
impl<B: Bitmap + Default> MmapRegionBuilder<B> {
89+
/// Create a new `MmapRegionBuilder` using the default value for
90+
/// the inner `Bitmap` object.
91+
pub fn new(size: usize) -> Self {
92+
Self::new_with_bitmap(size, B::default())
93+
}
94+
}
95+
96+
impl<B: Bitmap> MmapRegionBuilder<B> {
97+
/// Create a new `MmapRegionBuilder` using the provided `Bitmap` object.
98+
///
99+
/// When instantiating the builder for a region that does not require dirty bitmap
100+
/// bitmap tracking functionality, we can specify a trivial `Bitmap` implementation
101+
/// such as `()`.
102+
pub fn new_with_bitmap(size: usize, bitmap: B) -> Self {
103+
MmapRegionBuilder {
104+
size,
105+
prot: 0,
106+
flags: libc::MAP_ANONYMOUS | libc::MAP_PRIVATE,
107+
file_offset: None,
108+
raw_ptr: None,
109+
hugetlbfs: None,
110+
bitmap,
111+
}
112+
}
113+
114+
/// Create the `MmapRegion` object with the specified mmap memory protection flag `prot`.
115+
pub fn with_mmap_prot(mut self, prot: i32) -> Self {
116+
self.prot = prot;
117+
self
118+
}
119+
120+
/// Create the `MmapRegion` object with the specified mmap `flags`.
121+
pub fn with_mmap_flags(mut self, flags: i32) -> Self {
122+
self.flags = flags;
123+
self
124+
}
125+
126+
/// Create the `MmapRegion` object with the specified `file_offset`.
127+
pub fn with_file_offset(mut self, file_offset: FileOffset) -> Self {
128+
self.file_offset = Some(file_offset);
129+
self
130+
}
131+
132+
/// Create the `MmapRegion` object with the specified `hugetlbfs` flag.
133+
pub fn with_hugetlbfs(mut self, hugetlbfs: bool) -> Self {
134+
self.hugetlbfs = Some(hugetlbfs);
135+
self
136+
}
137+
138+
/// Create the `MmapRegion` object with pre-mmapped raw pointer.
139+
///
140+
/// # Safety
141+
///
142+
/// To use this safely, the caller must guarantee that `raw_addr` and `self.size` define a
143+
/// region within a valid mapping that is already present in the process.
144+
pub unsafe fn with_raw_mmap_pointer(mut self, raw_ptr: *mut u8) -> Self {
145+
self.raw_ptr = Some(raw_ptr);
146+
self
147+
}
148+
149+
/// Build the `MmapRegion` object.
150+
pub fn build(self) -> Result<MmapRegion<B>> {
151+
if self.raw_ptr.is_some() {
152+
return self.build_raw();
153+
}
154+
155+
// Forbid MAP_FIXED, as it doesn't make sense in this context, and is pretty dangerous
156+
// in general.
157+
if self.flags & libc::MAP_FIXED != 0 {
158+
return Err(Error::MapFixed);
159+
}
160+
161+
let (fd, offset) = if let Some(ref f_off) = self.file_offset {
162+
check_file_offset(f_off, self.size)?;
163+
(f_off.file().as_raw_fd(), f_off.start())
164+
} else {
165+
(-1, 0)
166+
};
167+
168+
// This is safe because we're not allowing MAP_FIXED, and invalid parameters cannot break
169+
// Rust safety guarantees (things may change if we're mapping /dev/mem or some wacky file).
170+
let addr = unsafe {
171+
libc::mmap(
172+
null_mut(),
173+
self.size,
174+
self.prot,
175+
self.flags,
176+
fd,
177+
offset as libc::off_t,
178+
)
179+
};
180+
181+
if addr == libc::MAP_FAILED {
182+
return Err(Error::Mmap(io::Error::last_os_error()));
183+
}
184+
185+
Ok(MmapRegion {
186+
addr: addr as *mut u8,
187+
size: self.size,
188+
bitmap: self.bitmap,
189+
file_offset: self.file_offset,
190+
prot: self.prot,
191+
flags: self.flags,
192+
owned: true,
193+
hugetlbfs: self.hugetlbfs,
194+
})
195+
}
196+
197+
fn build_raw(self) -> Result<MmapRegion<B>> {
198+
// Safe because this call just returns the page size and doesn't have any side effects.
199+
let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) } as usize;
200+
let addr = self.raw_ptr.clone().unwrap();
201+
202+
// Check that the pointer to the mapping is page-aligned.
203+
if (addr as usize) & (page_size - 1) != 0 {
204+
return Err(Error::InvalidPointer);
205+
}
206+
207+
// Check that the size is a multiple of the page size.
208+
if self.size & (page_size - 1) != 0 {
209+
return Err(Error::InvalidSize);
210+
}
211+
212+
Ok(MmapRegion {
213+
addr,
214+
size: self.size,
215+
bitmap: self.bitmap,
216+
file_offset: self.file_offset,
217+
prot: self.prot,
218+
flags: self.flags,
219+
owned: false,
220+
hugetlbfs: self.hugetlbfs,
221+
})
222+
}
223+
}
224+
77225
/// Helper structure for working with mmaped memory regions in Unix.
78226
///
79227
/// The structure is used for accessing the guest's physical memory by mmapping it into
@@ -108,12 +256,10 @@ impl<B: NewBitmap> MmapRegion<B> {
108256
/// # Arguments
109257
/// * `size` - The size of the memory region in bytes.
110258
pub fn new(size: usize) -> Result<Self> {
111-
Self::build(
112-
None,
113-
size,
114-
libc::PROT_READ | libc::PROT_WRITE,
115-
libc::MAP_ANONYMOUS | libc::MAP_NORESERVE | libc::MAP_PRIVATE,
116-
)
259+
MmapRegionBuilder::new_with_bitmap(size, B::with_len(size))
260+
.with_mmap_prot(libc::PROT_READ | libc::PROT_WRITE)
261+
.with_mmap_flags(libc::MAP_ANONYMOUS | libc::MAP_NORESERVE | libc::MAP_PRIVATE)
262+
.build()
117263
}
118264

119265
/// Creates a shared file mapping of `size` bytes.
@@ -123,12 +269,11 @@ impl<B: NewBitmap> MmapRegion<B> {
123269
/// referred to by `file_offset.file`.
124270
/// * `size` - The size of the memory region in bytes.
125271
pub fn from_file(file_offset: FileOffset, size: usize) -> Result<Self> {
126-
Self::build(
127-
Some(file_offset),
128-
size,
129-
libc::PROT_READ | libc::PROT_WRITE,
130-
libc::MAP_NORESERVE | libc::MAP_SHARED,
131-
)
272+
MmapRegionBuilder::new_with_bitmap(size, B::with_len(size))
273+
.with_file_offset(file_offset)
274+
.with_mmap_prot(libc::PROT_READ | libc::PROT_WRITE)
275+
.with_mmap_flags(libc::MAP_NORESERVE | libc::MAP_SHARED)
276+
.build()
132277
}
133278

134279
/// Creates a mapping based on the provided arguments.
@@ -147,37 +292,13 @@ impl<B: NewBitmap> MmapRegion<B> {
147292
prot: i32,
148293
flags: i32,
149294
) -> Result<Self> {
150-
// Forbid MAP_FIXED, as it doesn't make sense in this context, and is pretty dangerous
151-
// in general.
152-
if flags & libc::MAP_FIXED != 0 {
153-
return Err(Error::MapFixed);
154-
}
155-
156-
let (fd, offset) = if let Some(ref f_off) = file_offset {
157-
check_file_offset(f_off, size)?;
158-
(f_off.file().as_raw_fd(), f_off.start())
159-
} else {
160-
(-1, 0)
161-
};
162-
163-
// This is safe because we're not allowing MAP_FIXED, and invalid parameters cannot break
164-
// Rust safety guarantees (things may change if we're mapping /dev/mem or some wacky file).
165-
let addr = unsafe { libc::mmap(null_mut(), size, prot, flags, fd, offset as libc::off_t) };
166-
167-
if addr == libc::MAP_FAILED {
168-
return Err(Error::Mmap(io::Error::last_os_error()));
295+
let mut builder = MmapRegionBuilder::new_with_bitmap(size, B::with_len(size))
296+
.with_mmap_prot(prot)
297+
.with_mmap_flags(flags);
298+
if let Some(v) = file_offset {
299+
builder = builder.with_file_offset(v);
169300
}
170-
171-
Ok(Self {
172-
addr: addr as *mut u8,
173-
size,
174-
bitmap: B::with_len(size),
175-
file_offset,
176-
prot,
177-
flags,
178-
owned: true,
179-
hugetlbfs: None,
180-
})
301+
builder.build()
181302
}
182303

183304
/// Creates a `MmapRegion` instance for an externally managed mapping.
@@ -198,29 +319,11 @@ impl<B: NewBitmap> MmapRegion<B> {
198319
/// To use this safely, the caller must guarantee that `addr` and `size` define a region within
199320
/// a valid mapping that is already present in the process.
200321
pub unsafe fn build_raw(addr: *mut u8, size: usize, prot: i32, flags: i32) -> Result<Self> {
201-
// Safe because this call just returns the page size and doesn't have any side effects.
202-
let page_size = libc::sysconf(libc::_SC_PAGESIZE) as usize;
203-
204-
// Check that the pointer to the mapping is page-aligned.
205-
if (addr as usize) & (page_size - 1) != 0 {
206-
return Err(Error::InvalidPointer);
207-
}
208-
209-
// Check that the size is a multiple of the page size.
210-
if size & (page_size - 1) != 0 {
211-
return Err(Error::InvalidSize);
212-
}
213-
214-
Ok(Self {
215-
addr,
216-
size,
217-
bitmap: B::with_len(size),
218-
file_offset: None,
219-
prot,
220-
flags,
221-
owned: false,
222-
hugetlbfs: None,
223-
})
322+
MmapRegionBuilder::new_with_bitmap(size, B::with_len(size))
323+
.with_raw_mmap_pointer(addr)
324+
.with_mmap_prot(prot)
325+
.with_mmap_flags(flags)
326+
.build()
224327
}
225328
}
226329

@@ -510,6 +613,17 @@ mod tests {
510613
assert_eq!(r.prot(), libc::PROT_READ | libc::PROT_WRITE);
511614
assert_eq!(r.flags(), libc::MAP_NORESERVE | libc::MAP_PRIVATE);
512615
assert!(r.owned());
616+
617+
let region_size = 0x10_0000;
618+
let bitmap = AtomicBitmap::new(region_size, 0x1000);
619+
let builder = MmapRegionBuilder::new_with_bitmap(region_size, bitmap)
620+
.with_hugetlbfs(true)
621+
.with_mmap_prot(libc::PROT_READ | libc::PROT_WRITE);
622+
assert_eq!(builder.size, region_size);
623+
assert_eq!(builder.hugetlbfs, Some(true));
624+
assert_eq!(builder.prot, libc::PROT_READ | libc::PROT_WRITE);
625+
626+
crate::bitmap::tests::test_volatile_memory(&(builder.build().unwrap()));
513627
}
514628

515629
#[test]

0 commit comments

Comments
 (0)