From e1980a9c360b04705a16434bdaf1a56161dafb56 Mon Sep 17 00:00:00 2001 From: Daniel Verkamp Date: Fri, 7 Feb 2020 11:00:55 -0800 Subject: devices: pmem: implement flush using msync() Previously, writable pmem devices implemented the flush command using fsync(); however, this does not guarantee synchronization of memory mappings via mmap() to the file on disk. What we actually need is msync() on the pmem file mapping, but we don't have access to that mapping in the pmem child process, and it isn't trivial to pass it along since it is owned by the Vm object once it has been added as a mmap_arena. In order to call msync() on the mapping, add a new VmControl socket so that the pmem device can request that the main process issues an msync() on the MemoryMappingArena identified by its slot number. BUG=chromium:1007535 TEST=mount filesystem on /dev/pmem0 and sync; verify msync in strace Change-Id: Id0484757c422cf81d454fd54012a12dbcc1baaf6 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2044365 Reviewed-by: Stephen Barber Tested-by: kokoro Commit-Queue: Daniel Verkamp --- devices/src/virtio/pmem.rs | 60 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 12 deletions(-) (limited to 'devices') diff --git a/devices/src/virtio/pmem.rs b/devices/src/virtio/pmem.rs index 931b037..499e110 100644 --- a/devices/src/virtio/pmem.rs +++ b/devices/src/virtio/pmem.rs @@ -13,6 +13,10 @@ use sys_util::{error, EventFd, GuestAddress, GuestMemory, PollContext, PollToken use data_model::{DataInit, Le32, Le64}; +use msg_socket::{MsgReceiver, MsgSender}; + +use vm_control::{VmMsyncRequest, VmMsyncRequestSocket, VmMsyncResponse}; + use super::{ copy_config, DescriptorChain, DescriptorError, Interrupt, Queue, Reader, VirtioDevice, Writer, TYPE_PMEM, VIRTIO_F_VERSION_1, @@ -83,19 +87,38 @@ struct Worker { interrupt: Interrupt, queue: Queue, memory: GuestMemory, - disk_image: File, + pmem_device_socket: VmMsyncRequestSocket, + mapping_arena_slot: u32, } impl Worker { fn execute_request(&self, request: virtio_pmem_req) -> u32 { match request.type_.to_native() { - VIRTIO_PMEM_REQ_TYPE_FLUSH => match self.disk_image.sync_all() { - Ok(()) => VIRTIO_PMEM_RESP_TYPE_OK, - Err(e) => { - error!("failed flushing disk image: {}", e); - VIRTIO_PMEM_RESP_TYPE_EIO + VIRTIO_PMEM_REQ_TYPE_FLUSH => { + let request = VmMsyncRequest::MsyncArena { + slot: self.mapping_arena_slot, + offset: 0, // The pmem backing file is always at offset 0 in the arena. + }; + + if let Err(e) = self.pmem_device_socket.send(&request) { + error!("failed to send request: {}", e); + return VIRTIO_PMEM_RESP_TYPE_EIO; + } + + match self.pmem_device_socket.recv() { + Ok(response) => match response { + VmMsyncResponse::Ok => VIRTIO_PMEM_RESP_TYPE_OK, + VmMsyncResponse::Err(e) => { + error!("failed flushing disk image: {}", e); + VIRTIO_PMEM_RESP_TYPE_EIO + } + }, + Err(e) => { + error!("failed to receive data: {}", e); + VIRTIO_PMEM_RESP_TYPE_EIO + } } - }, + } _ => { error!("unknown request type: {}", request.type_.to_native()); VIRTIO_PMEM_RESP_TYPE_EIO @@ -199,21 +222,27 @@ pub struct Pmem { worker_thread: Option>, disk_image: Option, mapping_address: GuestAddress, + mapping_arena_slot: u32, mapping_size: u64, + pmem_device_socket: Option, } impl Pmem { pub fn new( disk_image: File, mapping_address: GuestAddress, + mapping_arena_slot: u32, mapping_size: u64, + pmem_device_socket: Option, ) -> SysResult { Ok(Pmem { kill_event: None, worker_thread: None, disk_image: Some(disk_image), mapping_address, + mapping_arena_slot, mapping_size, + pmem_device_socket, }) } } @@ -233,11 +262,15 @@ impl Drop for Pmem { impl VirtioDevice for Pmem { fn keep_fds(&self) -> Vec { + let mut keep_fds = Vec::new(); if let Some(disk_image) = &self.disk_image { - vec![disk_image.as_raw_fd()] - } else { - vec![] + keep_fds.push(disk_image.as_raw_fd()); } + + if let Some(ref pmem_device_socket) = self.pmem_device_socket { + keep_fds.push(pmem_device_socket.as_raw_fd()); + } + keep_fds } fn device_type(&self) -> u32 { @@ -274,7 +307,9 @@ impl VirtioDevice for Pmem { let queue = queues.remove(0); let queue_event = queue_events.remove(0); - if let Some(disk_image) = self.disk_image.take() { + let mapping_arena_slot = self.mapping_arena_slot; + + if let Some(pmem_device_socket) = self.pmem_device_socket.take() { let (self_kill_event, kill_event) = match EventFd::new().and_then(|e| Ok((e.try_clone()?, e))) { Ok(v) => v, @@ -291,8 +326,9 @@ impl VirtioDevice for Pmem { let mut worker = Worker { interrupt, memory, - disk_image, queue, + pmem_device_socket, + mapping_arena_slot, }; worker.run(queue_event, kill_event); }); -- cgit 1.4.1