Skip to content

Commit

Permalink
ioctls: Rework get_dirty_bitmap
Browse files Browse the repository at this point in the history
Update to use reworked MSHV_GET_GPAP_ACCESS_BITMAP and simplify the
code.

Signed-off-by: Nuno Das Neves <[email protected]>
  • Loading branch information
NunoDasNeves committed Apr 30, 2024
1 parent 69fa03d commit 7ae30cf
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 77 deletions.
117 changes: 43 additions & 74 deletions mshv-ioctls/src/ioctls/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -606,99 +606,68 @@ impl VmFd {
flag,
)
}
/// Get page access state
/// The data provides each page's access state whether it is dirty or accessed
/// Get page access states as bitmap
/// A bitmap of dirty or accessed bits for a range of guest pages
/// Prerequisite: Need to enable page_acess_tracking
/// Flags:
/// bit 1: ClearAccessed
/// bit 2: SetAccessed
/// bit 3: ClearDirty
/// bit 4: SetDirty
/// Number of bits reserved: 60
pub fn get_gpa_access_state(
/// Args:
/// base_pfn: Guest page number
/// page_count: Number of pages
/// access_type: MSHV_GPAP_ACCESS_TYPE_*
/// access_op: MSHV_GPAP_ACCESS_OP_* to optionally clear or set bits
pub fn get_gpap_access_bitmap(
&self,
base_pfn: u64,
nr_pfns: u32,
flags: u64,
) -> Result<mshv_get_gpa_pages_access_state> {
let mut states: Vec<hv_gpa_page_access_state> =
vec![hv_gpa_page_access_state { as_uint8: 0 }; nr_pfns as usize];
let mut gpa_pages_access_state: mshv_get_gpa_pages_access_state =
mshv_get_gpa_pages_access_state {
count: nr_pfns,
hv_gpa_page_number: base_pfn,
flags,
states: states.as_mut_ptr(),
page_count: u32,
access_type: u8,
access_op: u8,
) -> Result<Vec<u64>> {
let buf_sz = (page_count + 63) / 64;
let mut bitmap: Vec<u64> = vec![0u64; buf_sz as usize];
let mut args = mshv_gpap_access_bitmap {
access_type,
access_op,
page_count,
gpap_base: base_pfn,
bitmap_ptr: bitmap.as_mut_ptr() as u64,
..Default::default()
};

// SAFETY: IOCTL with correct types
let ret = unsafe {
ioctl_with_mut_ref(
self,
MSHV_GET_GPA_ACCESS_STATES(),
&mut gpa_pages_access_state,
MSHV_GET_GPAP_ACCESS_BITMAP(),
&mut args,
)
};
if ret == 0 {
Ok(gpa_pages_access_state)
Ok(bitmap)
} else {
Err(errno::Error::last().into())
}
}
/// Gets the bitmap of pages dirtied since the last call of this function
///
/// Flags:
/// bit 1: ClearAccessed
/// bit 2: SetAccessed
/// bit 3: ClearDirty
/// bit 4: SetDirty
/// Number of bits reserved: 60
pub fn get_dirty_log(&self, base_pfn: u64, memory_size: usize, flags: u64) -> Result<Vec<u64>> {
// Compute the length of the bitmap needed for all dirty pages in one memory slot.
// One memory page is `page_size` bytes and `KVM_GET_DIRTY_LOG` returns one dirty bit for
// each page.
// SAFETY: FFI call to libc
let page_size = match unsafe { libc::sysconf(libc::_SC_PAGESIZE) } {
-1 => return Err(errno::Error::last().into()),
ps => ps as usize,
};

/// Args:
/// base_pfn: Guest page number
/// memory_size: In bytes
/// access_op: MSHV_GPAP_ACCESS_OP_* to optionally clear or set bits
pub fn get_dirty_log(&self, base_pfn: u64, memory_size: usize, access_op: u8) -> Result<Vec<u64>> {
// For ease of access we are saving the bitmap in a u64 vector. We are using ceil to
// make sure we count all dirty pages even when `memory_size` is not a multiple of
// `page_size * 64`.
let div_ceil = |dividend, divisor| (dividend + divisor - 1) / divisor;
let bitmap_size = div_ceil(memory_size, page_size * 64);
let bitmap_size = div_ceil(memory_size, HV_PAGE_SIZE * 64);
let mut bitmap = vec![0u64; bitmap_size];

let mut processed: usize = 0;
let mut mask;
let mut state: u8;
let mut current_size;
let mut remaining = (memory_size / page_size) as u32;
let mut bit_index = 0;
let mut bitmap_index = 0;

while remaining != 0 {
current_size = cmp::min(PAGE_ACCESS_STATES_BATCH_SIZE, remaining);
let page_states =
self.get_gpa_access_state(base_pfn + processed as u64, current_size, flags)?;
// SAFETY: we're sure states and count meet the requirements for from_raw_parts
let slices: &[hv_gpa_page_access_state] = unsafe {
std::slice::from_raw_parts(page_states.states, page_states.count as usize)
};
for item in slices.iter() {
let bits = &mut bitmap[bitmap_index];
mask = 1 << bit_index;
// SAFETY: access union field
state = unsafe { item.__bindgen_anon_1.dirty() };
if state == 1 {
*bits |= mask;
}
processed += 1;
bitmap_index = processed / 64;
bit_index = processed % 64;
}
remaining -= page_states.count;
let mut completed = 0;
let total = (memory_size / HV_PAGE_SIZE) as u32;

while completed < total {
let remaining = total - completed;
let batch_size = cmp::min(PAGE_ACCESS_STATES_BATCH_SIZE, remaining);
let mut bitmap_part = self.get_gpap_access_bitmap(base_pfn + completed as u64,
batch_size, MSHV_GPAP_ACCESS_TYPE_DIRTY as u8, access_op,)?;
bitmap.append(&mut bitmap_part);
completed += batch_size;
}
Ok(bitmap)
}
Expand Down Expand Up @@ -918,7 +887,7 @@ mod tests {
assert!(vm.set_msi_routing(&msi_routing).is_ok());
}
#[test]
fn test_get_gpa_access_states() {
fn test_get_gpa_access_bitmap() {
let hv = Mshv::new().unwrap();
let vm = hv.create_vm().unwrap();
// Try to allocate 32 MB memory
Expand All @@ -944,8 +913,8 @@ mod tests {
// number of bits returned etc.
vm.map_user_memory(mem_region).unwrap();
vm.enable_dirty_page_tracking().unwrap();
let bitmaps_1: Vec<u64> = vm.get_dirty_log(0, mem_size, 0x4).unwrap();
let bitmaps_2: Vec<u64> = vm.get_dirty_log(0, mem_size, 0x8).unwrap();
let bitmaps_1: Vec<u64> = vm.get_dirty_log(0, mem_size, MSHV_GPAP_ACCESS_OP_CLEAR as u8).unwrap();
let bitmaps_2: Vec<u64> = vm.get_dirty_log(0, mem_size, MSHV_GPAP_ACCESS_OP_SET as u8).unwrap();
vm.disable_dirty_page_tracking().unwrap();
assert!(bitmaps_1.len() == bitmaps_2.len());
vm.unmap_user_memory(mem_region).unwrap();
Expand Down
6 changes: 3 additions & 3 deletions mshv-ioctls/src/mshv_ioctls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ ioctl_iow_nr!(
);
ioctl_iowr_nr!(MSHV_VP_TRANSLATE_GVA, MSHV_IOCTL, 0x0E, mshv_translate_gva);
ioctl_iowr_nr!(
MSHV_GET_GPA_ACCESS_STATES,
MSHV_GET_GPAP_ACCESS_BITMAP,
MSHV_IOCTL,
0x12,
mshv_get_gpa_pages_access_state
0x07,
mshv_gpap_access_bitmap
);

ioctl_iowr_nr!(MSHV_CREATE_DEVICE, MSHV_IOCTL, 0x13, mshv_create_device);
Expand Down

0 comments on commit 7ae30cf

Please sign in to comment.