• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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::anyhow;
8 use anyhow::bail;
9 use anyhow::Context;
10 use cros_async::Executor;
11 use serde::Deserialize;
12 use serde::Serialize;
13 pub use sys::start_device as run_block_device;
14 pub use sys::Options;
15 use vm_memory::GuestMemory;
16 use vmm_vhost::message::*;
17 
18 use crate::virtio;
19 use crate::virtio::block::asynchronous::BlockAsync;
20 use crate::virtio::vhost::user::device::handler::DeviceRequestHandler;
21 use crate::virtio::vhost::user::device::handler::VhostUserDevice;
22 use crate::virtio::vhost::user::device::VhostUserDeviceBuilder;
23 use crate::virtio::Interrupt;
24 use crate::virtio::VirtioDevice;
25 
26 const NUM_QUEUES: u16 = 16;
27 
28 struct BlockBackend {
29     inner: Box<BlockAsync>,
30 
31     avail_features: u64,
32     acked_features: u64,
33     acked_protocol_features: VhostUserProtocolFeatures,
34 }
35 
36 #[derive(Serialize, Deserialize)]
37 struct BlockBackendSnapshot {
38     acked_features: u64,
39     // `avail_features` and `acked_protocol_features` don't need to be snapshotted, but they are
40     // to be used to make sure that the proper features are used on `restore`.
41     avail_features: u64,
42     acked_protocol_features: u64,
43 }
44 
45 impl VhostUserDeviceBuilder for BlockAsync {
build(self: Box<Self>, _ex: &Executor) -> anyhow::Result<Box<dyn vmm_vhost::Backend>>46     fn build(self: Box<Self>, _ex: &Executor) -> anyhow::Result<Box<dyn vmm_vhost::Backend>> {
47         let avail_features = self.features() | 1 << VHOST_USER_F_PROTOCOL_FEATURES;
48         let backend = BlockBackend {
49             inner: self,
50             avail_features,
51             acked_features: 0,
52             acked_protocol_features: VhostUserProtocolFeatures::empty(),
53         };
54         let handler = DeviceRequestHandler::new(backend);
55         Ok(Box::new(handler))
56     }
57 }
58 
59 impl VhostUserDevice for BlockBackend {
max_queue_num(&self) -> usize60     fn max_queue_num(&self) -> usize {
61         NUM_QUEUES as usize
62     }
63 
features(&self) -> u6464     fn features(&self) -> u64 {
65         self.avail_features
66     }
67 
ack_features(&mut self, value: u64) -> anyhow::Result<()>68     fn ack_features(&mut self, value: u64) -> anyhow::Result<()> {
69         let unrequested_features = value & !self.avail_features;
70         if unrequested_features != 0 {
71             bail!("invalid features are given: {:#x}", unrequested_features);
72         }
73 
74         self.acked_features |= value;
75 
76         Ok(())
77     }
78 
acked_features(&self) -> u6479     fn acked_features(&self) -> u64 {
80         self.acked_features
81     }
82 
protocol_features(&self) -> VhostUserProtocolFeatures83     fn protocol_features(&self) -> VhostUserProtocolFeatures {
84         VhostUserProtocolFeatures::CONFIG
85             | VhostUserProtocolFeatures::MQ
86             | VhostUserProtocolFeatures::BACKEND_REQ
87     }
88 
ack_protocol_features(&mut self, features: u64) -> anyhow::Result<()>89     fn ack_protocol_features(&mut self, features: u64) -> anyhow::Result<()> {
90         let features = VhostUserProtocolFeatures::from_bits(features)
91             .ok_or_else(|| anyhow!("invalid protocol features are given: {:#x}", features))?;
92         let supported = self.protocol_features();
93         self.acked_protocol_features = features & supported;
94         Ok(())
95     }
96 
acked_protocol_features(&self) -> u6497     fn acked_protocol_features(&self) -> u64 {
98         self.acked_protocol_features.bits()
99     }
100 
read_config(&self, offset: u64, data: &mut [u8])101     fn read_config(&self, offset: u64, data: &mut [u8]) {
102         self.inner.read_config(offset, data)
103     }
104 
reset(&mut self)105     fn reset(&mut self) {
106         if let Err(e) = self.inner.reset() {
107             base::error!("reset failed: {:#}", e);
108         }
109     }
110 
start_queue( &mut self, idx: usize, queue: virtio::Queue, mem: GuestMemory, doorbell: Interrupt, ) -> anyhow::Result<()>111     fn start_queue(
112         &mut self,
113         idx: usize,
114         queue: virtio::Queue,
115         mem: GuestMemory,
116         doorbell: Interrupt,
117     ) -> anyhow::Result<()> {
118         self.inner.start_queue(idx, queue, mem, doorbell)
119     }
120 
stop_queue(&mut self, idx: usize) -> anyhow::Result<virtio::Queue>121     fn stop_queue(&mut self, idx: usize) -> anyhow::Result<virtio::Queue> {
122         self.inner.stop_queue(idx)
123     }
124 
stop_non_queue_workers(&mut self) -> anyhow::Result<()>125     fn stop_non_queue_workers(&mut self) -> anyhow::Result<()> {
126         // TODO: This assumes that `reset` only stops workers which might not be true in the
127         // future. Consider moving the `reset` code into a `stop_all_workers` method or, maybe,
128         // make `stop_queue` implicitly stop a worker thread when there is no active queue.
129         self.inner.reset()
130     }
131 
snapshot(&self) -> anyhow::Result<Vec<u8>>132     fn snapshot(&self) -> anyhow::Result<Vec<u8>> {
133         // The queue states are being snapshotted in the device handler.
134         let serialized_bytes = serde_json::to_vec(&BlockBackendSnapshot {
135             acked_features: self.acked_features,
136             avail_features: self.avail_features,
137             acked_protocol_features: self.acked_protocol_features.bits(),
138         })
139         .context("Failed to serialize BlockBackendSnapshot")?;
140 
141         Ok(serialized_bytes)
142     }
143 
restore(&mut self, data: Vec<u8>) -> anyhow::Result<()>144     fn restore(&mut self, data: Vec<u8>) -> anyhow::Result<()> {
145         let block_backend_snapshot: BlockBackendSnapshot =
146             serde_json::from_slice(&data).context("Failed to deserialize BlockBackendSnapshot")?;
147         anyhow::ensure!(
148             self.avail_features == block_backend_snapshot.avail_features,
149             "Vhost user block restored avail_features do not match. Live: {:?}, snapshot: {:?}",
150             self.avail_features,
151             block_backend_snapshot.avail_features,
152         );
153         anyhow::ensure!(
154             self.acked_protocol_features.bits() == block_backend_snapshot.acked_protocol_features,
155             "Vhost user block restored acked_protocol_features do not match. Live: {:?}, \
156             snapshot: {:?}",
157             self.acked_protocol_features,
158             block_backend_snapshot.acked_protocol_features
159         );
160         self.acked_features = block_backend_snapshot.acked_features;
161         Ok(())
162     }
163 }
164