vmm: Update KVM userspace mapping when PCI BAR remapping

In the context of the shared memory region used by virtio-fs in order to
support DAX feature, the shared region is exposed as a dedicated PCI
BAR, and it is backed by a KVM userspace mapping.

Upon BAR remapping, the BAR is moved to a different location in the
guest address space, and the KVM mapping must be updated accordingly.

Additionally, we need the VirtioDevice to report the updated guest
address through the shared memory region returned by get_shm_regions().
That's why a new setter is added to the VirtioDevice trait, so that
after the mapping has been updated for KVM, we can tell the VirtioDevice
the new guest address the shared region is located at.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf 2020-04-17 19:30:33 +02:00
parent 49cc73a4ca
commit d35e775ed9
4 changed files with 74 additions and 7 deletions

View File

@ -42,6 +42,8 @@ pub struct VirtioSharedMemory {
#[derive(Clone)] #[derive(Clone)]
pub struct VirtioSharedMemoryList { pub struct VirtioSharedMemoryList {
pub host_addr: u64,
pub mem_slot: u32,
pub addr: GuestAddress, pub addr: GuestAddress,
pub len: GuestUsize, pub len: GuestUsize,
pub region_list: Vec<VirtioSharedMemory>, pub region_list: Vec<VirtioSharedMemory>,
@ -97,6 +99,14 @@ pub trait VirtioDevice: Send {
None None
} }
/// Updates the list of shared memory regions required by the device.
fn set_shm_regions(
&mut self,
_shm_regions: VirtioSharedMemoryList,
) -> std::result::Result<(), Error> {
std::unimplemented!()
}
fn iommu_translate(&self, addr: u64) -> u64 { fn iommu_translate(&self, addr: u64) -> u64 {
addr addr
} }

View File

@ -186,4 +186,5 @@ pub enum Error {
FailedSignalingDriver(io::Error), FailedSignalingDriver(io::Error),
VhostUserUpdateMemory(vhost_user::Error), VhostUserUpdateMemory(vhost_user::Error),
EventfdError(io::Error), EventfdError(io::Error),
SetShmRegionsNotSupported,
} }

View File

@ -261,7 +261,7 @@ pub struct Fs {
pause_evt: Option<EventFd>, pause_evt: Option<EventFd>,
// Hold ownership of the memory that is allocated for the device // Hold ownership of the memory that is allocated for the device
// which will be automatically dropped when the device is dropped // which will be automatically dropped when the device is dropped
cache: Option<(VirtioSharedMemoryList, u64, MmapRegion)>, cache: Option<(VirtioSharedMemoryList, MmapRegion)>,
slave_req_support: bool, slave_req_support: bool,
queue_evts: Option<Vec<EventFd>>, queue_evts: Option<Vec<EventFd>>,
interrupt_cb: Option<Arc<dyn VirtioInterrupt>>, interrupt_cb: Option<Arc<dyn VirtioInterrupt>>,
@ -276,7 +276,7 @@ impl Fs {
tag: &str, tag: &str,
req_num_queues: usize, req_num_queues: usize,
queue_size: u16, queue_size: u16,
cache: Option<(VirtioSharedMemoryList, u64, MmapRegion)>, cache: Option<(VirtioSharedMemoryList, MmapRegion)>,
) -> Result<Fs> { ) -> Result<Fs> {
let mut slave_req_support = false; let mut slave_req_support = false;
@ -478,7 +478,7 @@ impl VirtioDevice for Fs {
let vu_master_req_handler = Arc::new(Mutex::new(SlaveReqHandler { let vu_master_req_handler = Arc::new(Mutex::new(SlaveReqHandler {
cache_offset: cache.0.addr, cache_offset: cache.0.addr,
cache_size: cache.0.len, cache_size: cache.0.len,
mmap_cache_addr: cache.1, mmap_cache_addr: cache.0.host_addr,
})); }));
let req_handler = MasterReqHandler::new(vu_master_req_handler).map_err(|e| { let req_handler = MasterReqHandler::new(vu_master_req_handler).map_err(|e| {
@ -552,6 +552,18 @@ impl VirtioDevice for Fs {
} }
} }
fn set_shm_regions(
&mut self,
shm_regions: VirtioSharedMemoryList,
) -> std::result::Result<(), crate::Error> {
if let Some(mut cache) = self.cache.as_mut() {
cache.0 = shm_regions;
Ok(())
} else {
Err(crate::Error::SetShmRegionsNotSupported)
}
}
fn update_memory(&mut self, mem: &GuestMemoryMmap) -> std::result::Result<(), crate::Error> { fn update_memory(&mut self, mem: &GuestMemoryMmap) -> std::result::Result<(), crate::Error> {
update_mem_table(&mut self.vu, mem).map_err(crate::Error::VhostUserUpdateMemory) update_mem_table(&mut self.vu, mem).map_err(crate::Error::VhostUserUpdateMemory)
} }

View File

@ -450,6 +450,48 @@ impl DeviceRelocation for AddressManager {
.register_ioevent(event, &io_addr, NoDatamatch) .register_ioevent(event, &io_addr, NoDatamatch)
.map_err(|e| io::Error::from_raw_os_error(e.errno()))?; .map_err(|e| io::Error::from_raw_os_error(e.errno()))?;
} }
} else {
let virtio_dev = virtio_pci_dev.virtio_device();
let mut virtio_dev = virtio_dev.lock().unwrap();
if let Some(mut shm_regions) = virtio_dev.get_shm_regions() {
if shm_regions.addr.raw_value() == old_base {
// Remove old region from KVM by passing a size of 0.
let mut mem_region = kvm_bindings::kvm_userspace_memory_region {
slot: shm_regions.mem_slot,
guest_phys_addr: old_base,
memory_size: 0,
userspace_addr: shm_regions.host_addr,
flags: 0,
};
// Safe because removing an existing guest region.
unsafe {
self.vm_fd
.set_user_memory_region(mem_region)
.map_err(|e| io::Error::from_raw_os_error(e.errno()))?;
}
// Create new mapping by inserting new region to KVM.
mem_region.guest_phys_addr = new_base;
mem_region.memory_size = shm_regions.len;
// Safe because the guest regions are guaranteed not to overlap.
unsafe {
self.vm_fd
.set_user_memory_region(mem_region)
.map_err(|e| io::Error::from_raw_os_error(e.errno()))?;
}
// Update shared memory regions to reflect the new mapping.
shm_regions.addr = GuestAddress(new_base);
virtio_dev.set_shm_regions(shm_regions).map_err(|e| {
io::Error::new(
io::ErrorKind::Other,
format!("failed to update shared memory regions: {:?}", e),
)
})?;
}
}
} }
} }
@ -1403,15 +1445,16 @@ impl DeviceManager {
libc::MAP_ANONYMOUS | libc::MAP_PRIVATE, libc::MAP_ANONYMOUS | libc::MAP_PRIVATE,
) )
.map_err(DeviceManagerError::NewMmapRegion)?; .map_err(DeviceManagerError::NewMmapRegion)?;
let addr: u64 = mmap_region.as_ptr() as u64; let host_addr: u64 = mmap_region.as_ptr() as u64;
self.memory_manager let mem_slot = self
.memory_manager
.lock() .lock()
.unwrap() .unwrap()
.create_userspace_mapping( .create_userspace_mapping(
fs_guest_addr.raw_value(), fs_guest_addr.raw_value(),
fs_cache, fs_cache,
addr, host_addr,
false, false,
false, false,
) )
@ -1425,11 +1468,12 @@ impl DeviceManager {
Some(( Some((
VirtioSharedMemoryList { VirtioSharedMemoryList {
host_addr,
mem_slot,
addr: fs_guest_addr, addr: fs_guest_addr,
len: fs_cache as GuestUsize, len: fs_cache as GuestUsize,
region_list, region_list,
}, },
addr,
mmap_region, mmap_region,
)) ))
} else { } else {