mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2025-01-03 11:25:20 +00:00
hypervisor, vmm: Abstract the interfaces to start/stop dirty log
Following KVM interfaces, the `hypervisor` crate now provides interfaces to start/stop the dirty pages logging on a per region basis, and asks its users (e.g. the `vmm` crate) to iterate over the regions that needs dirty pages log. MSHV only has a global control to start/stop dirty pages log on all regions at once. This patch refactors related APIs from the `hypervisor` crate to provide a global control to start/stop dirty pages log (following MSHV's behaviors), and keeps tracking the regions need dirty pages log for KVM. It avoids leaking hypervisor-specific behaviors out of the `hypervisor` crate. Signed-off-by: Bo Chen <chen.bo@intel.com>
This commit is contained in:
parent
2723995cfa
commit
e7c9954dc1
@ -22,6 +22,7 @@ use crate::vm::{self, VmmOps};
|
|||||||
use crate::{arm64_core_reg_id, offset__of};
|
use crate::{arm64_core_reg_id, offset__of};
|
||||||
use kvm_ioctls::{NoDatamatch, VcpuFd, VmFd};
|
use kvm_ioctls::{NoDatamatch, VcpuFd, VmFd};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use std::collections::HashMap;
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
@ -30,7 +31,7 @@ use std::os::unix::io::{AsRawFd, RawFd};
|
|||||||
use std::result;
|
use std::result;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc, RwLock};
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use vm_memory::Address;
|
use vm_memory::Address;
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
@ -110,12 +111,21 @@ enum TdxCommand {
|
|||||||
pub struct KvmVmState {}
|
pub struct KvmVmState {}
|
||||||
|
|
||||||
pub use KvmVmState as VmState;
|
pub use KvmVmState as VmState;
|
||||||
|
|
||||||
|
struct KvmDirtyLogSlot {
|
||||||
|
slot: u32,
|
||||||
|
guest_phys_addr: u64,
|
||||||
|
memory_size: u64,
|
||||||
|
userspace_addr: u64,
|
||||||
|
}
|
||||||
|
|
||||||
/// Wrapper over KVM VM ioctls.
|
/// Wrapper over KVM VM ioctls.
|
||||||
pub struct KvmVm {
|
pub struct KvmVm {
|
||||||
fd: Arc<VmFd>,
|
fd: Arc<VmFd>,
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
msrs: MsrEntries,
|
msrs: MsrEntries,
|
||||||
state: KvmVmState,
|
state: KvmVmState,
|
||||||
|
dirty_log_slots: Arc<RwLock<HashMap<u32, KvmDirtyLogSlot>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@ -267,10 +277,35 @@ impl vm::Vm for KvmVm {
|
|||||||
/// Creates a guest physical memory region.
|
/// Creates a guest physical memory region.
|
||||||
///
|
///
|
||||||
fn create_user_memory_region(&self, user_memory_region: MemoryRegion) -> vm::Result<()> {
|
fn create_user_memory_region(&self, user_memory_region: MemoryRegion) -> vm::Result<()> {
|
||||||
|
let mut region = user_memory_region;
|
||||||
|
|
||||||
|
if (region.flags & KVM_MEM_LOG_DIRTY_PAGES) != 0 {
|
||||||
|
if (region.flags & KVM_MEM_READONLY) != 0 {
|
||||||
|
return Err(vm::HypervisorVmError::CreateUserMemory(anyhow!(
|
||||||
|
"Error creating regions with both 'dirty-pages-log' and 'read-only'."
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep track of the regions that need dirty pages log
|
||||||
|
self.dirty_log_slots.write().unwrap().insert(
|
||||||
|
region.slot,
|
||||||
|
KvmDirtyLogSlot {
|
||||||
|
slot: region.slot,
|
||||||
|
guest_phys_addr: region.guest_phys_addr,
|
||||||
|
memory_size: region.memory_size,
|
||||||
|
userspace_addr: region.userspace_addr,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Always create guest physical memory region without `KVM_MEM_LOG_DIRTY_PAGES`.
|
||||||
|
// For regions that need this flag, dirty pages log will be turned on in `start_dirty_log`.
|
||||||
|
region.flags = 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Safe because guest regions are guaranteed not to overlap.
|
// Safe because guest regions are guaranteed not to overlap.
|
||||||
unsafe {
|
unsafe {
|
||||||
self.fd
|
self.fd
|
||||||
.set_user_memory_region(user_memory_region)
|
.set_user_memory_region(region)
|
||||||
.map_err(|e| vm::HypervisorVmError::CreateUserMemory(e.into()))
|
.map_err(|e| vm::HypervisorVmError::CreateUserMemory(e.into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -280,6 +315,9 @@ impl vm::Vm for KvmVm {
|
|||||||
fn remove_user_memory_region(&self, user_memory_region: MemoryRegion) -> vm::Result<()> {
|
fn remove_user_memory_region(&self, user_memory_region: MemoryRegion) -> vm::Result<()> {
|
||||||
let mut region = user_memory_region;
|
let mut region = user_memory_region;
|
||||||
|
|
||||||
|
// Remove the corresponding entry from "self.dirty_log_slots" if needed
|
||||||
|
self.dirty_log_slots.write().unwrap().remove(®ion.slot);
|
||||||
|
|
||||||
// Setting the size to 0 means "remove"
|
// Setting the size to 0 means "remove"
|
||||||
region.memory_size = 0;
|
region.memory_size = 0;
|
||||||
// Safe because guest regions are guaranteed not to overlap.
|
// Safe because guest regions are guaranteed not to overlap.
|
||||||
@ -386,54 +424,49 @@ impl vm::Vm for KvmVm {
|
|||||||
///
|
///
|
||||||
/// Start logging dirty pages
|
/// Start logging dirty pages
|
||||||
///
|
///
|
||||||
fn start_dirty_log(
|
fn start_dirty_log(&self) -> vm::Result<()> {
|
||||||
&self,
|
let dirty_log_slots = self.dirty_log_slots.read().unwrap();
|
||||||
slot: u32,
|
for (_, s) in dirty_log_slots.iter() {
|
||||||
guest_phys_addr: u64,
|
let region = MemoryRegion {
|
||||||
memory_size: u64,
|
slot: s.slot,
|
||||||
userspace_addr: u64,
|
guest_phys_addr: s.guest_phys_addr,
|
||||||
) -> vm::Result<()> {
|
memory_size: s.memory_size,
|
||||||
let region = self.make_user_memory_region(
|
userspace_addr: s.userspace_addr,
|
||||||
slot,
|
flags: KVM_MEM_LOG_DIRTY_PAGES,
|
||||||
guest_phys_addr,
|
};
|
||||||
memory_size,
|
// Safe because guest regions are guaranteed not to overlap.
|
||||||
userspace_addr,
|
unsafe {
|
||||||
false,
|
self.fd
|
||||||
true,
|
.set_user_memory_region(region)
|
||||||
);
|
.map_err(|e| vm::HypervisorVmError::StartDirtyLog(e.into()))?;
|
||||||
// Safe because guest regions are guaranteed not to overlap.
|
}
|
||||||
unsafe {
|
|
||||||
self.fd
|
|
||||||
.set_user_memory_region(region)
|
|
||||||
.map_err(|e| vm::HypervisorVmError::StartDirtyLog(e.into()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Stop logging dirty pages
|
/// Stop logging dirty pages
|
||||||
///
|
///
|
||||||
fn stop_dirty_log(
|
fn stop_dirty_log(&self) -> vm::Result<()> {
|
||||||
&self,
|
let dirty_log_slots = self.dirty_log_slots.read().unwrap();
|
||||||
slot: u32,
|
for (_, s) in dirty_log_slots.iter() {
|
||||||
guest_phys_addr: u64,
|
let region = MemoryRegion {
|
||||||
memory_size: u64,
|
slot: s.slot,
|
||||||
userspace_addr: u64,
|
guest_phys_addr: s.guest_phys_addr,
|
||||||
) -> vm::Result<()> {
|
memory_size: s.memory_size,
|
||||||
let region = self.make_user_memory_region(
|
userspace_addr: s.userspace_addr,
|
||||||
slot,
|
flags: 0,
|
||||||
guest_phys_addr,
|
};
|
||||||
memory_size,
|
// Safe because guest regions are guaranteed not to overlap.
|
||||||
userspace_addr,
|
unsafe {
|
||||||
false,
|
self.fd
|
||||||
false,
|
.set_user_memory_region(region)
|
||||||
);
|
.map_err(|e| vm::HypervisorVmError::StartDirtyLog(e.into()))?;
|
||||||
|
}
|
||||||
// Safe because guest regions are guaranteed not to overlap.
|
|
||||||
unsafe {
|
|
||||||
self.fd
|
|
||||||
.set_user_memory_region(region)
|
|
||||||
.map_err(|e| vm::HypervisorVmError::StopDirtyLog(e.into()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@ -624,6 +657,7 @@ impl hypervisor::Hypervisor for KvmHypervisor {
|
|||||||
fd: vm_fd,
|
fd: vm_fd,
|
||||||
msrs,
|
msrs,
|
||||||
state: VmState {},
|
state: VmState {},
|
||||||
|
dirty_log_slots: Arc::new(RwLock::new(HashMap::new())),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -632,6 +666,7 @@ impl hypervisor::Hypervisor for KvmHypervisor {
|
|||||||
Ok(Arc::new(KvmVm {
|
Ok(Arc::new(KvmVm {
|
||||||
fd: vm_fd,
|
fd: vm_fd,
|
||||||
state: VmState {},
|
state: VmState {},
|
||||||
|
dirty_log_slots: Arc::new(RwLock::new(HashMap::new())),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -879,13 +879,7 @@ impl vm::Vm for MshvVm {
|
|||||||
///
|
///
|
||||||
/// Start logging dirty pages
|
/// Start logging dirty pages
|
||||||
///
|
///
|
||||||
fn start_dirty_log(
|
fn start_dirty_log(&self) -> vm::Result<()> {
|
||||||
&self,
|
|
||||||
_slot: u32,
|
|
||||||
_guest_phys_addr: u64,
|
|
||||||
_memory_size: u64,
|
|
||||||
_userspace_addr: u64,
|
|
||||||
) -> vm::Result<()> {
|
|
||||||
Err(vm::HypervisorVmError::StartDirtyLog(anyhow!(
|
Err(vm::HypervisorVmError::StartDirtyLog(anyhow!(
|
||||||
"functionality not implemented"
|
"functionality not implemented"
|
||||||
)))
|
)))
|
||||||
@ -893,13 +887,7 @@ impl vm::Vm for MshvVm {
|
|||||||
///
|
///
|
||||||
/// Stop logging dirty pages
|
/// Stop logging dirty pages
|
||||||
///
|
///
|
||||||
fn stop_dirty_log(
|
fn stop_dirty_log(&self) -> vm::Result<()> {
|
||||||
&self,
|
|
||||||
_slot: u32,
|
|
||||||
_guest_phys_addr: u64,
|
|
||||||
_memory_size: u64,
|
|
||||||
_userspace_addr: u64,
|
|
||||||
) -> vm::Result<()> {
|
|
||||||
Err(vm::HypervisorVmError::StopDirtyLog(anyhow!(
|
Err(vm::HypervisorVmError::StopDirtyLog(anyhow!(
|
||||||
"functionality not implemented"
|
"functionality not implemented"
|
||||||
)))
|
)))
|
||||||
|
@ -281,21 +281,9 @@ pub trait Vm: Send + Sync {
|
|||||||
/// Set the VM state
|
/// Set the VM state
|
||||||
fn set_state(&self, state: VmState) -> Result<()>;
|
fn set_state(&self, state: VmState) -> Result<()>;
|
||||||
/// Start logging dirty pages
|
/// Start logging dirty pages
|
||||||
fn start_dirty_log(
|
fn start_dirty_log(&self) -> Result<()>;
|
||||||
&self,
|
|
||||||
slot: u32,
|
|
||||||
guest_phys_addr: u64,
|
|
||||||
memory_size: u64,
|
|
||||||
userspace_addr: u64,
|
|
||||||
) -> Result<()>;
|
|
||||||
/// Stop logging dirty pages
|
/// Stop logging dirty pages
|
||||||
fn stop_dirty_log(
|
fn stop_dirty_log(&self) -> Result<()>;
|
||||||
&self,
|
|
||||||
slot: u32,
|
|
||||||
guest_phys_addr: u64,
|
|
||||||
memory_size: u64,
|
|
||||||
userspace_addr: u64,
|
|
||||||
) -> Result<()>;
|
|
||||||
/// Get dirty pages bitmap
|
/// Get dirty pages bitmap
|
||||||
fn get_dirty_log(&self, slot: u32, memory_size: u64) -> Result<Vec<u64>>;
|
fn get_dirty_log(&self, slot: u32, memory_size: u64) -> Result<Vec<u64>>;
|
||||||
#[cfg(feature = "tdx")]
|
#[cfg(feature = "tdx")]
|
||||||
|
@ -1541,23 +1541,14 @@ impl MemoryManager {
|
|||||||
Ok(table)
|
Ok(table)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the dirty log on guest RAM in the hypervisor (kvm/mshv).
|
// Start the dirty log in the hypervisor (kvm/mshv).
|
||||||
// Also, reset the dirty bitmap logged by the vmm.
|
// Also, reset the dirty bitmap logged by the vmm.
|
||||||
// Just before we do a bulk copy we want to start/clear the dirty log so that
|
// Just before we do a bulk copy we want to start/clear the dirty log so that
|
||||||
// pages touched during our bulk copy are tracked.
|
// pages touched during our bulk copy are tracked.
|
||||||
pub fn start_memory_dirty_log(&self) -> std::result::Result<(), MigratableError> {
|
pub fn start_memory_dirty_log(&self) -> std::result::Result<(), MigratableError> {
|
||||||
for r in &self.guest_ram_mappings {
|
self.vm.start_dirty_log().map_err(|e| {
|
||||||
let user_addr = self
|
MigratableError::MigrateSend(anyhow!("Error starting VM dirty log {}", e))
|
||||||
.guest_memory()
|
})?;
|
||||||
.memory()
|
|
||||||
.get_host_address(GuestAddress(r.gpa))
|
|
||||||
.unwrap();
|
|
||||||
self.vm
|
|
||||||
.start_dirty_log(r.slot, r.gpa, r.size, user_addr as u64)
|
|
||||||
.map_err(|e| {
|
|
||||||
MigratableError::MigrateSend(anyhow!("Error starting VM dirty log {}", e))
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
|
|
||||||
for r in self.guest_memory.memory().iter() {
|
for r in self.guest_memory.memory().iter() {
|
||||||
r.bitmap().reset();
|
r.bitmap().reset();
|
||||||
@ -1567,18 +1558,9 @@ impl MemoryManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop_memory_dirty_log(&self) -> std::result::Result<(), MigratableError> {
|
pub fn stop_memory_dirty_log(&self) -> std::result::Result<(), MigratableError> {
|
||||||
for r in &self.guest_ram_mappings {
|
self.vm.stop_dirty_log().map_err(|e| {
|
||||||
let user_addr = self
|
MigratableError::MigrateSend(anyhow!("Error stopping VM dirty log {}", e))
|
||||||
.guest_memory()
|
})?;
|
||||||
.memory()
|
|
||||||
.get_host_address(GuestAddress(r.gpa))
|
|
||||||
.unwrap();
|
|
||||||
self.vm
|
|
||||||
.stop_dirty_log(r.slot, r.gpa, r.size, user_addr as u64)
|
|
||||||
.map_err(|e| {
|
|
||||||
MigratableError::MigrateSend(anyhow!("Error stopping VM dirty log {}", e))
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user