Skip to content

Commit 5ae3293

Browse files
weltlingrbradford
authored andcommitted
virtio-devices: block: Fix writeback mode update flow
Virtio v1.2 says that if CONFIG_WCE is negotiated but FLUSH is not, the device must initialize writeback to 0. It also says that if CONFIG_WCE was not negotiated but FLUSH was, the driver should assume presence of a writeback cache. Introduce a pure is_writeback_enabled helper and a set_writeback_mode helper. This makes the two call flows explicit: * write_config resolves the guest requested mode against the negotiated features before storing it back * activate starts from the default writeback preference and then resolves it against the negotiated features * reset restores the initial writeback state This keeps the config space value and the runtime writeback flag in sync and makes the spec driven fallback easier to follow. Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
1 parent d0b2534 commit 5ae3293

1 file changed

Lines changed: 19 additions & 17 deletions

File tree

virtio-devices/src/block.rs

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -974,24 +974,24 @@ impl Block {
974974
}
975975
}
976976

977-
fn update_writeback(&mut self) {
978-
// Use writeback from config if VIRTIO_BLK_F_CONFIG_WCE
979-
let writeback = if self.common.feature_acked(VIRTIO_BLK_F_CONFIG_WCE.into()) {
980-
self.config.writeback == 1
981-
} else {
982-
// Else check if VIRTIO_BLK_F_FLUSH negotiated
983-
self.common.feature_acked(VIRTIO_BLK_F_FLUSH.into())
984-
};
977+
/// The virtio v1.2 spec says "If VIRTIO_BLK_F_CONFIG_WCE was not
978+
/// negotiated but VIRTIO_BLK_F_FLUSH was, the driver SHOULD assume
979+
/// presence of a writeback cache." It also says "If
980+
/// VIRTIO_BLK_F_CONFIG_WCE is negotiated but VIRTIO_BLK_F_FLUSH is not,
981+
/// the device MUST initialize writeback to 0."
982+
fn is_writeback_enabled(&self, desired: bool) -> bool {
983+
let flush = self.common.feature_acked(VIRTIO_BLK_F_FLUSH.into());
984+
let wce = self.common.feature_acked(VIRTIO_BLK_F_CONFIG_WCE.into());
985+
if wce { flush && desired } else { flush }
986+
}
985987

988+
fn set_writeback_mode(&mut self, enabled: bool) {
989+
self.config.writeback = enabled as u8;
990+
self.writeback.store(enabled, Ordering::Release);
986991
info!(
987992
"Changing cache mode to {}",
988-
if writeback {
989-
"writeback"
990-
} else {
991-
"writethrough"
992-
}
993+
if enabled { "writeback" } else { "writethrough" }
993994
);
994-
self.writeback.store(writeback, Ordering::Release);
995995
}
996996

997997
pub fn resize(&mut self, new_size: u64) -> Result<()> {
@@ -1073,8 +1073,8 @@ impl VirtioDevice for Block {
10731073
return;
10741074
}
10751075

1076-
self.config.writeback = data[0];
1077-
self.update_writeback();
1076+
let writeback = self.is_writeback_enabled(data[0] == 1);
1077+
self.set_writeback_mode(writeback);
10781078
}
10791079

10801080
fn activate(&mut self, context: crate::device::ActivationContext) -> ActivateResult {
@@ -1097,7 +1097,8 @@ impl VirtioDevice for Block {
10971097
// Recompute the barrier size from the queues that are actually activated.
10981098
self.common.paused_sync = Some(Arc::new(Barrier::new(queues.len() + 1)));
10991099

1100-
self.update_writeback();
1100+
let writeback = self.is_writeback_enabled(self.config.writeback == 1);
1101+
self.set_writeback_mode(writeback);
11011102

11021103
let mut epoll_threads = Vec::new();
11031104
let event_idx = self.common.feature_acked(VIRTIO_RING_F_EVENT_IDX.into());
@@ -1167,6 +1168,7 @@ impl VirtioDevice for Block {
11671168

11681169
fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
11691170
let result = self.common.reset();
1171+
self.set_writeback_mode(true);
11701172
event!("virtio-device", "reset", "id", &self.id);
11711173
result
11721174
}

0 commit comments

Comments
 (0)