From 9101bdd7a9849d805fec23e83015e088a3350e00 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Wed, 20 May 2020 17:04:52 +0100 Subject: [PATCH] vm-virtio: 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 Fixes: #680 Signed-off-by: Rob Bradford --- vm-virtio/src/block.rs | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/vm-virtio/src/block.rs b/vm-virtio/src/block.rs index 0a2448f76..00e750268 100755 --- a/vm-virtio/src/block.rs +++ b/vm-virtio/src/block.rs @@ -610,6 +610,7 @@ struct BlockEpollHandler { kill_evt: EventFd, pause_evt: EventFd, event_idx: bool, + writeback: Arc, } impl BlockEpollHandler { @@ -622,7 +623,9 @@ impl BlockEpollHandler { for avail_desc in queue.iter(&mem) { let len; match Request::parse(&avail_desc, &mem) { - Ok(request) => { + Ok(mut request) => { + request.set_writeback(self.writeback.load(Ordering::SeqCst)); + let mut disk_image_locked = self.disk_image.lock().unwrap(); let mut disk_image = disk_image_locked.deref_mut(); let status = match request.execute( @@ -931,6 +934,7 @@ pub struct Block { pause_evt: Option, paused: Arc, queue_size: Vec, + writeback: Arc, } #[derive(Serialize, Deserialize)] @@ -966,7 +970,8 @@ impl Block { let mut avail_features = (1u64 << VIRTIO_F_VERSION_1) | (1u64 << VIRTIO_BLK_F_FLUSH) - | (1u64 << VIRTIO_RING_F_EVENT_IDX); + | (1u64 << VIRTIO_RING_F_EVENT_IDX) + | (1u64 << VIRTIO_BLK_F_CONFIG_WCE); if iommu { avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM; @@ -979,6 +984,7 @@ impl Block { let disk_nsectors = disk_size / SECTOR_SIZE; let mut config = VirtioBlockConfig { capacity: disk_nsectors, + writeback: 1, ..Default::default() }; @@ -1002,6 +1008,7 @@ impl Block { pause_evt: None, paused: Arc::new(AtomicBool::new(false)), queue_size: vec![queue_size; num_queues], + writeback: Arc::new(AtomicBool::new(true)), }) } @@ -1024,6 +1031,27 @@ impl Block { Ok(()) } + + 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 Drop for Block { @@ -1085,6 +1113,7 @@ impl VirtioDevice for Block { } let (_, right) = config_slice.split_at_mut(offset as usize); right.copy_from_slice(&data[..]); + self.update_writeback(); } fn activate( @@ -1146,6 +1175,7 @@ impl VirtioDevice for Block { let event_idx = self.acked_features & 1u64 << VIRTIO_RING_F_EVENT_IDX == 1u64 << VIRTIO_RING_F_EVENT_IDX; + self.update_writeback(); let mut epoll_threads = Vec::new(); for _ in 0..self.queue_size.len() { @@ -1159,6 +1189,7 @@ impl VirtioDevice for Block { kill_evt: kill_evt.try_clone().unwrap(), pause_evt: pause_evt.try_clone().unwrap(), event_idx, + writeback: self.writeback.clone(), }; handler.queue.set_event_idx(event_idx);