From dc66eee8f053905363f479546401ce4ecd86b3a2 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Wed, 20 May 2020 17:04:52 +0100 Subject: [PATCH] vhost_user_block: Ensure backing file consistency Correctly implement the virtio specification by setting the writeback field on the request based on the algorithm in the spec. TEST=Boot with hypervisor-firmware with CH in verbose mode. See info level messages saying cache mode is writethrough in firmware (no support for flush or WCE). Once in the Linux kernel see messages that mode is writeback. Fixes: #1216 Signed-off-by: Rob Bradford --- vhost_user_block/src/lib.rs | 54 ++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/vhost_user_block/src/lib.rs b/vhost_user_block/src/lib.rs index dfd7c4050..8153a26bf 100644 --- a/vhost_user_block/src/lib.rs +++ b/vhost_user_block/src/lib.rs @@ -26,6 +26,8 @@ use std::ops::DerefMut; use std::os::unix::fs::OpenOptionsExt; use std::path::PathBuf; use std::process; +use std::result; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex, RwLock}; use std::time::Instant; use std::vec::Vec; @@ -98,6 +100,7 @@ struct VhostUserBlkThread { disk_nsectors: u64, event_idx: bool, kill_evt: EventFd, + writeback: Arc, } impl VhostUserBlkThread { @@ -105,6 +108,7 @@ impl VhostUserBlkThread { disk_image: Arc>, disk_image_id: Vec, disk_nsectors: u64, + writeback: Arc, ) -> Result { Ok(VhostUserBlkThread { mem: None, @@ -113,6 +117,7 @@ impl VhostUserBlkThread { disk_nsectors, event_idx: false, kill_evt: EventFd::new(EFD_NONBLOCK).map_err(Error::CreateKillEventFd)?, + writeback, }) } @@ -127,8 +132,9 @@ impl VhostUserBlkThread { debug!("got an element in the queue"); let len; match Request::parse(&head, mem) { - Ok(request) => { + Ok(mut request) => { debug!("element is a valid request"); + request.set_writeback(self.writeback.load(Ordering::SeqCst)); let status = match request.execute( &mut self.disk_image.lock().unwrap().deref_mut(), self.disk_nsectors, @@ -182,6 +188,8 @@ struct VhostUserBlkBackend { poll_queue: bool, queues_per_thread: Vec, queue_size: usize, + acked_features: u64, + writeback: Arc, } impl VhostUserBlkBackend { @@ -225,11 +233,13 @@ impl VhostUserBlkBackend { let mut queues_per_thread = Vec::new(); let mut threads = Vec::new(); + let writeback = Arc::new(AtomicBool::new(true)); for i in 0..num_queues { let thread = Mutex::new(VhostUserBlkThread::new( image.clone(), image_id.clone(), nsectors, + writeback.clone(), )?); threads.push(thread); queues_per_thread.push(0b1 << i); @@ -242,8 +252,31 @@ impl VhostUserBlkBackend { poll_queue, queues_per_thread, queue_size, + acked_features: 0, + writeback, }) } + + fn update_writeback(&mut self) { + // Use writeback from config if VIRTIO_BLK_F_CONFIG_WCE + let writeback = + if self.acked_features & 1 << VIRTIO_BLK_F_CONFIG_WCE == 1 << VIRTIO_BLK_F_CONFIG_WCE { + self.config.writeback == 1 + } else { + // Else check if VIRTIO_BLK_F_FLUSH negotiated + self.acked_features & 1 << VIRTIO_BLK_F_FLUSH == 1 << VIRTIO_BLK_F_FLUSH + }; + + info!( + "Changing cache mode to {}", + if writeback { + "writeback" + } else { + "writethrough" + } + ); + self.writeback.store(writeback, Ordering::SeqCst); + } } impl VhostUserBackend for VhostUserBlkBackend { @@ -269,6 +302,11 @@ impl VhostUserBackend for VhostUserBlkBackend { avail_features } + fn acked_features(&mut self, features: u64) { + self.acked_features = features; + self.update_writeback(); + } + fn protocol_features(&self) -> VhostUserProtocolFeatures { VhostUserProtocolFeatures::CONFIG } @@ -345,6 +383,20 @@ impl VhostUserBackend for VhostUserBlkBackend { self.config.as_slice().to_vec() } + fn set_config(&mut self, offset: u32, data: &[u8]) -> result::Result<(), io::Error> { + let config_slice = self.config.as_mut_slice(); + let data_len = data.len() as u32; + let config_len = config_slice.len() as u32; + if offset + data_len > config_len { + error!("Failed to write config space"); + return Err(io::Error::from_raw_os_error(libc::EINVAL)); + } + let (_, right) = config_slice.split_at_mut(offset as usize); + right.copy_from_slice(&data[..]); + self.update_writeback(); + Ok(()) + } + fn exit_event(&self, thread_index: usize) -> Option<(EventFd, Option)> { // The exit event is placed after the queue, which is event index 1. Some((