1 // Copyright 2021 The ChromiumOS Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 mod sys; 6 7 use anyhow::Context; 8 use cros_async::Executor; 9 use serde::Deserialize; 10 use serde::Serialize; 11 pub use sys::start_device as run_block_device; 12 pub use sys::Options; 13 use vm_memory::GuestMemory; 14 use vmm_vhost::message::*; 15 16 use crate::virtio; 17 use crate::virtio::block::asynchronous::BlockAsync; 18 use crate::virtio::vhost::user::device::handler::DeviceRequestHandler; 19 use crate::virtio::vhost::user::device::handler::VhostUserDevice; 20 use crate::virtio::vhost::user::device::VhostUserDeviceBuilder; 21 use crate::virtio::VirtioDevice; 22 23 const NUM_QUEUES: u16 = 16; 24 25 struct BlockBackend { 26 inner: Box<BlockAsync>, 27 28 avail_features: u64, 29 } 30 31 #[derive(Serialize, Deserialize)] 32 struct BlockBackendSnapshot { 33 // `avail_features` don't need to be snapshotted, but they are 34 // to be used to make sure that the proper features are used on `restore`. 35 avail_features: u64, 36 } 37 38 impl VhostUserDeviceBuilder for BlockAsync { build(self: Box<Self>, _ex: &Executor) -> anyhow::Result<Box<dyn vmm_vhost::Backend>>39 fn build(self: Box<Self>, _ex: &Executor) -> anyhow::Result<Box<dyn vmm_vhost::Backend>> { 40 let avail_features = self.features() | 1 << VHOST_USER_F_PROTOCOL_FEATURES; 41 let backend = BlockBackend { 42 inner: self, 43 avail_features, 44 }; 45 let handler = DeviceRequestHandler::new(backend); 46 Ok(Box::new(handler)) 47 } 48 } 49 50 impl VhostUserDevice for BlockBackend { max_queue_num(&self) -> usize51 fn max_queue_num(&self) -> usize { 52 NUM_QUEUES as usize 53 } 54 features(&self) -> u6455 fn features(&self) -> u64 { 56 self.avail_features 57 } 58 protocol_features(&self) -> VhostUserProtocolFeatures59 fn protocol_features(&self) -> VhostUserProtocolFeatures { 60 VhostUserProtocolFeatures::CONFIG 61 | VhostUserProtocolFeatures::MQ 62 | VhostUserProtocolFeatures::BACKEND_REQ 63 | VhostUserProtocolFeatures::DEVICE_STATE 64 } 65 read_config(&self, offset: u64, data: &mut [u8])66 fn read_config(&self, offset: u64, data: &mut [u8]) { 67 self.inner.read_config(offset, data) 68 } 69 reset(&mut self)70 fn reset(&mut self) { 71 if let Err(e) = self.inner.reset() { 72 base::error!("reset failed: {:#}", e); 73 } 74 } 75 start_queue( &mut self, idx: usize, queue: virtio::Queue, mem: GuestMemory, ) -> anyhow::Result<()>76 fn start_queue( 77 &mut self, 78 idx: usize, 79 queue: virtio::Queue, 80 mem: GuestMemory, 81 ) -> anyhow::Result<()> { 82 self.inner.start_queue(idx, queue, mem) 83 } 84 stop_queue(&mut self, idx: usize) -> anyhow::Result<virtio::Queue>85 fn stop_queue(&mut self, idx: usize) -> anyhow::Result<virtio::Queue> { 86 self.inner.stop_queue(idx) 87 } 88 enter_suspended_state(&mut self) -> anyhow::Result<()>89 fn enter_suspended_state(&mut self) -> anyhow::Result<()> { 90 // TODO: This assumes that `reset` only stops workers which might not be true in the 91 // future. Consider moving the `reset` code into a `stop_all_workers` method or, maybe, 92 // make `stop_queue` implicitly stop a worker thread when there is no active queue. 93 self.inner.reset()?; 94 Ok(()) 95 } 96 snapshot(&mut self) -> anyhow::Result<serde_json::Value>97 fn snapshot(&mut self) -> anyhow::Result<serde_json::Value> { 98 // The queue states are being snapshotted in the device handler. 99 serde_json::to_value(BlockBackendSnapshot { 100 avail_features: self.avail_features, 101 }) 102 .context("Failed to serialize BlockBackendSnapshot") 103 } 104 restore(&mut self, data: serde_json::Value) -> anyhow::Result<()>105 fn restore(&mut self, data: serde_json::Value) -> anyhow::Result<()> { 106 let block_backend_snapshot: BlockBackendSnapshot = 107 serde_json::from_value(data).context("Failed to deserialize BlockBackendSnapshot")?; 108 anyhow::ensure!( 109 self.avail_features == block_backend_snapshot.avail_features, 110 "Vhost user block restored avail_features do not match. Live: {:?}, snapshot: {:?}", 111 self.avail_features, 112 block_backend_snapshot.avail_features, 113 ); 114 Ok(()) 115 } 116 } 117