Skip to content

Commit 48d614f

Browse files
committed
refactor(balloon): move remove_range to GuestMemoryExtension
Move the function to discard guest memory under the GuestMemoryExtension trait. This is done so that it could be reused in other parts of the code. Furthermore, this also changes it to use try_access, which is the correct way to access a range of guest memory as it deals with the possibility of it spanning multiple regions. Signed-off-by: Riccardo Mancini <[email protected]>
1 parent 3970df8 commit 48d614f

File tree

4 files changed

+183
-142
lines changed

4 files changed

+183
-142
lines changed

src/vmm/src/devices/virtio/balloon/device.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use super::super::ActivateError;
1414
use super::super::device::{DeviceState, VirtioDevice};
1515
use super::super::queue::Queue;
1616
use super::metrics::METRICS;
17-
use super::util::{compact_page_frame_numbers, remove_range};
17+
use super::util::compact_page_frame_numbers;
1818
use super::{
1919
BALLOON_DEV_ID, BALLOON_NUM_QUEUES, BALLOON_QUEUE_SIZES, DEFLATE_INDEX, INFLATE_INDEX,
2020
MAX_PAGE_COMPACT_BUFFER, MAX_PAGES_IN_DESC, MIB_TO_4K_PAGES, STATS_INDEX,
@@ -32,7 +32,9 @@ use crate::devices::virtio::queue::InvalidAvailIdx;
3232
use crate::devices::virtio::transport::{VirtioInterrupt, VirtioInterruptType};
3333
use crate::logger::IncMetric;
3434
use crate::utils::u64_to_usize;
35-
use crate::vstate::memory::{Address, ByteValued, Bytes, GuestAddress, GuestMemoryMmap};
35+
use crate::vstate::memory::{
36+
Address, ByteValued, Bytes, GuestAddress, GuestMemoryExtension, GuestMemoryMmap,
37+
};
3638
use crate::{impl_device_type, mem_size_mib};
3739

3840
const SIZE_OF_U32: usize = std::mem::size_of::<u32>();
@@ -335,10 +337,9 @@ impl Balloon {
335337
let guest_addr =
336338
GuestAddress(u64::from(page_frame_number) << VIRTIO_BALLOON_PFN_SHIFT);
337339

338-
if let Err(err) = remove_range(
339-
mem,
340-
(guest_addr, u64::from(range_len) << VIRTIO_BALLOON_PFN_SHIFT),
341-
self.restored_from_file,
340+
if let Err(err) = mem.discard_range(
341+
guest_addr,
342+
usize::try_from(range_len).unwrap() << VIRTIO_BALLOON_PFN_SHIFT,
342343
) {
343344
error!("Error removing memory range: {:?}", err);
344345
}

src/vmm/src/devices/virtio/balloon/util.rs

Lines changed: 0 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -65,57 +65,6 @@ pub(crate) fn compact_page_frame_numbers(v: &mut [u32]) -> Vec<(u32, u32)> {
6565
result
6666
}
6767

68-
pub(crate) fn remove_range(
69-
guest_memory: &GuestMemoryMmap,
70-
range: (GuestAddress, u64),
71-
restored_from_file: bool,
72-
) -> Result<(), RemoveRegionError> {
73-
let (guest_address, range_len) = range;
74-
75-
if let Some(region) = guest_memory.find_region(guest_address) {
76-
if guest_address.0 + range_len > region.start_addr().0 + region.len() {
77-
return Err(RemoveRegionError::MalformedRange);
78-
}
79-
let phys_address = guest_memory
80-
.get_host_address(guest_address)
81-
.map_err(|_| RemoveRegionError::AddressTranslation)?;
82-
83-
// Mmap a new anonymous region over the present one in order to create a hole.
84-
// This workaround is (only) needed after resuming from a snapshot because the guest memory
85-
// is mmaped from file as private and there is no `madvise` flag that works for this case.
86-
if restored_from_file {
87-
// SAFETY: The address and length are known to be valid.
88-
let ret = unsafe {
89-
libc::mmap(
90-
phys_address.cast(),
91-
u64_to_usize(range_len),
92-
libc::PROT_READ | libc::PROT_WRITE,
93-
libc::MAP_FIXED | libc::MAP_ANONYMOUS | libc::MAP_PRIVATE,
94-
-1,
95-
0,
96-
)
97-
};
98-
if ret == libc::MAP_FAILED {
99-
return Err(RemoveRegionError::MmapFail(io::Error::last_os_error()));
100-
}
101-
};
102-
103-
// Madvise the region in order to mark it as not used.
104-
// SAFETY: The address and length are known to be valid.
105-
let ret = unsafe {
106-
let range_len = u64_to_usize(range_len);
107-
libc::madvise(phys_address.cast(), range_len, libc::MADV_DONTNEED)
108-
};
109-
if ret < 0 {
110-
return Err(RemoveRegionError::MadviseFail(io::Error::last_os_error()));
111-
}
112-
113-
Ok(())
114-
} else {
115-
Err(RemoveRegionError::RegionNotFound)
116-
}
117-
}
118-
11968
#[cfg(test)]
12069
mod tests {
12170
use std::fmt::Debug;
@@ -174,88 +123,6 @@ mod tests {
174123
);
175124
}
176125

177-
#[test]
178-
fn test_remove_range() {
179-
let page_size: usize = 0x1000;
180-
let mem = single_region_mem(2 * page_size);
181-
182-
// Fill the memory with ones.
183-
let ones = vec![1u8; 2 * page_size];
184-
mem.write(&ones[..], GuestAddress(0)).unwrap();
185-
186-
// Remove the first page.
187-
remove_range(&mem, (GuestAddress(0), page_size as u64), false).unwrap();
188-
189-
// Check that the first page is zeroed.
190-
let mut actual_page = vec![0u8; page_size];
191-
mem.read(actual_page.as_mut_slice(), GuestAddress(0))
192-
.unwrap();
193-
assert_eq!(vec![0u8; page_size], actual_page);
194-
// Check that the second page still contains ones.
195-
mem.read(actual_page.as_mut_slice(), GuestAddress(page_size as u64))
196-
.unwrap();
197-
assert_eq!(vec![1u8; page_size], actual_page);
198-
199-
// Malformed range: the len is too big.
200-
assert_match!(
201-
remove_range(&mem, (GuestAddress(0), 0x10000), false).unwrap_err(),
202-
RemoveRegionError::MalformedRange
203-
);
204-
205-
// Region not mapped.
206-
assert_match!(
207-
remove_range(&mem, (GuestAddress(0x10000), 0x10), false).unwrap_err(),
208-
RemoveRegionError::RegionNotFound
209-
);
210-
211-
// Madvise fail: the guest address is not aligned to the page size.
212-
assert_match!(
213-
remove_range(&mem, (GuestAddress(0x20), page_size as u64), false).unwrap_err(),
214-
RemoveRegionError::MadviseFail(_)
215-
);
216-
}
217-
218-
#[test]
219-
fn test_remove_range_on_restored() {
220-
let page_size: usize = 0x1000;
221-
let mem = single_region_mem(2 * page_size);
222-
223-
// Fill the memory with ones.
224-
let ones = vec![1u8; 2 * page_size];
225-
mem.write(&ones[..], GuestAddress(0)).unwrap();
226-
227-
// Remove the first page.
228-
remove_range(&mem, (GuestAddress(0), page_size as u64), true).unwrap();
229-
230-
// Check that the first page is zeroed.
231-
let mut actual_page = vec![0u8; page_size];
232-
mem.read(actual_page.as_mut_slice(), GuestAddress(0))
233-
.unwrap();
234-
assert_eq!(vec![0u8; page_size], actual_page);
235-
// Check that the second page still contains ones.
236-
mem.read(actual_page.as_mut_slice(), GuestAddress(page_size as u64))
237-
.unwrap();
238-
assert_eq!(vec![1u8; page_size], actual_page);
239-
240-
// Malformed range: the len is too big.
241-
assert_match!(
242-
remove_range(&mem, (GuestAddress(0), 0x10000), true).unwrap_err(),
243-
RemoveRegionError::MalformedRange
244-
);
245-
246-
// Region not mapped.
247-
assert_match!(
248-
remove_range(&mem, (GuestAddress(0x10000), 0x10), true).unwrap_err(),
249-
RemoveRegionError::RegionNotFound
250-
);
251-
252-
// Mmap fail: the guest address is not aligned to the page size.
253-
assert_match!(
254-
remove_range(&mem, (GuestAddress(0x20), page_size as u64), true).unwrap_err(),
255-
RemoveRegionError::MmapFail(_)
256-
);
257-
}
258-
259126
/// -------------------------------------
260127
/// BEGIN PROPERTY BASED TESTING
261128
use proptest::prelude::*;

src/vmm/src/test_utils/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ use crate::vm_memory_vendored::GuestRegionCollection;
1616
use crate::vmm_config::boot_source::BootSourceConfig;
1717
use crate::vmm_config::instance_info::InstanceInfo;
1818
use crate::vmm_config::machine_config::HugePageConfig;
19-
use crate::vstate::memory;
20-
use crate::vstate::memory::{GuestMemoryMmap, GuestRegionMmap, GuestRegionMmapExt};
19+
use crate::vstate::memory::{self, GuestRegionMmapExt};
20+
use crate::vstate::memory::{GuestMemoryMmap, GuestRegionMmap};
2121
use crate::{EventManager, Vmm};
2222

2323
pub mod mock_resources;

0 commit comments

Comments
 (0)