1 // Copyright 2021 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 use std::cell::RefCell;
6 use std::fs::OpenOptions;
7 use std::rc::Rc;
8 use std::sync::{atomic::AtomicU64, atomic::Ordering, Arc};
9
10 use anyhow::{anyhow, bail, Context};
11 use argh::FromArgs;
12 use futures::future::{AbortHandle, Abortable};
13 use sync::Mutex;
14 use vmm_vhost::message::*;
15
16 use base::{warn, Event, Timer};
17 use cros_async::{sync::Mutex as AsyncMutex, EventAsync, Executor, TimerAsync};
18 use data_model::DataInit;
19 use disk::create_async_disk_file;
20 use hypervisor::ProtectionType;
21 use vm_memory::GuestMemory;
22
23 use crate::virtio::block::asynchronous::{flush_disk, handle_queue};
24 use crate::virtio::block::*;
25 use crate::virtio::vhost::user::device::{
26 handler::{DeviceRequestHandler, Doorbell, VhostUserBackend},
27 vvu::pci::VvuPciDevice,
28 };
29 use crate::virtio::{self, base_features, block::sys::*, copy_config};
30
31 const QUEUE_SIZE: u16 = 256;
32 const NUM_QUEUES: u16 = 16;
33
34 pub(crate) struct BlockBackend {
35 ex: Executor,
36 disk_state: Rc<AsyncMutex<DiskState>>,
37 disk_size: Arc<AtomicU64>,
38 block_size: u32,
39 seg_max: u32,
40 avail_features: u64,
41 acked_features: u64,
42 acked_protocol_features: VhostUserProtocolFeatures,
43 flush_timer: Rc<RefCell<TimerAsync>>,
44 flush_timer_armed: Rc<RefCell<bool>>,
45 workers: [Option<AbortHandle>; Self::MAX_QUEUE_NUM],
46 }
47
48 impl BlockBackend {
49 /// Creates a new block backend.
50 ///
51 /// * `ex`: executor used to run this device task.
52 /// * `filename`: Name of the disk image file.
53 /// * `options`: Vector of file options.
54 /// - `read-only`
new(ex: &Executor, filename: &str, options: Vec<&str>) -> anyhow::Result<Self>55 pub(crate) fn new(ex: &Executor, filename: &str, options: Vec<&str>) -> anyhow::Result<Self> {
56 let read_only = options.contains(&"read-only");
57 let sparse = false;
58 let block_size = 512;
59 let f = OpenOptions::new()
60 .read(true)
61 .write(!read_only)
62 .create(false)
63 .open(filename)
64 .context("Failed to open disk file")?;
65 let disk_image = create_async_disk_file(f).context("Failed to create async file")?;
66
67 let base_features = base_features(ProtectionType::Unprotected);
68
69 if block_size % SECTOR_SIZE as u32 != 0 {
70 bail!(
71 "Block size {} is not a multiple of {}.",
72 block_size,
73 SECTOR_SIZE,
74 );
75 }
76 let disk_size = disk_image.get_len()?;
77 if disk_size % block_size as u64 != 0 {
78 warn!(
79 "Disk size {} is not a multiple of block size {}; \
80 the remainder will not be visible to the guest.",
81 disk_size, block_size,
82 );
83 }
84
85 let avail_features = build_avail_features(base_features, read_only, sparse, true)
86 | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits();
87
88 let seg_max = get_seg_max(QUEUE_SIZE);
89
90 let async_image = disk_image.to_async_disk(ex)?;
91
92 let disk_size = Arc::new(AtomicU64::new(disk_size));
93
94 let disk_state = Rc::new(AsyncMutex::new(DiskState::new(
95 async_image,
96 Arc::clone(&disk_size),
97 read_only,
98 sparse,
99 None, // id: Option<BlockId>,
100 )));
101
102 let timer = Timer::new().context("Failed to create a timer")?;
103 let flush_timer_write = Rc::new(RefCell::new(
104 TimerAsync::new(
105 // Call try_clone() to share the same underlying FD with the `flush_disk` task.
106 timer.0.try_clone().context("Failed to clone flush_timer")?,
107 ex,
108 )
109 .context("Failed to create an async timer")?,
110 ));
111 // Create a separate TimerAsync with the same backing kernel timer. This allows the
112 // `flush_disk` task to borrow its copy waiting for events while the queue handlers can
113 // still borrow their copy momentarily to set timeouts.
114 // Call try_clone() to share the same underlying FD with the `flush_disk` task.
115 let flush_timer_read = timer
116 .0
117 .try_clone()
118 .context("Failed to clone flush_timer")
119 .and_then(|t| TimerAsync::new(t, ex).context("Failed to create an async timer"))?;
120 let flush_timer_armed = Rc::new(RefCell::new(false));
121 ex.spawn_local(flush_disk(
122 Rc::clone(&disk_state),
123 flush_timer_read,
124 Rc::clone(&flush_timer_armed),
125 ))
126 .detach();
127
128 Ok(BlockBackend {
129 ex: ex.clone(),
130 disk_state,
131 disk_size,
132 block_size,
133 seg_max,
134 avail_features,
135 acked_features: 0,
136 acked_protocol_features: VhostUserProtocolFeatures::empty(),
137 flush_timer: flush_timer_write,
138 flush_timer_armed,
139 workers: Default::default(),
140 })
141 }
142 }
143
144 impl VhostUserBackend for BlockBackend {
145 const MAX_QUEUE_NUM: usize = NUM_QUEUES as usize;
146 const MAX_VRING_LEN: u16 = QUEUE_SIZE;
147
148 type Error = anyhow::Error;
149
features(&self) -> u64150 fn features(&self) -> u64 {
151 self.avail_features
152 }
153
ack_features(&mut self, value: u64) -> anyhow::Result<()>154 fn ack_features(&mut self, value: u64) -> anyhow::Result<()> {
155 let unrequested_features = value & !self.avail_features;
156 if unrequested_features != 0 {
157 bail!("invalid features are given: {:#x}", unrequested_features);
158 }
159
160 self.acked_features |= value;
161
162 Ok(())
163 }
164
acked_features(&self) -> u64165 fn acked_features(&self) -> u64 {
166 self.acked_features
167 }
168
protocol_features(&self) -> VhostUserProtocolFeatures169 fn protocol_features(&self) -> VhostUserProtocolFeatures {
170 VhostUserProtocolFeatures::CONFIG | VhostUserProtocolFeatures::MQ
171 }
172
ack_protocol_features(&mut self, features: u64) -> anyhow::Result<()>173 fn ack_protocol_features(&mut self, features: u64) -> anyhow::Result<()> {
174 let features = VhostUserProtocolFeatures::from_bits(features)
175 .ok_or_else(|| anyhow!("invalid protocol features are given: {:#x}", features))?;
176 let supported = self.protocol_features();
177 self.acked_protocol_features = features & supported;
178 Ok(())
179 }
180
acked_protocol_features(&self) -> u64181 fn acked_protocol_features(&self) -> u64 {
182 self.acked_protocol_features.bits()
183 }
184
read_config(&self, offset: u64, data: &mut [u8])185 fn read_config(&self, offset: u64, data: &mut [u8]) {
186 let config_space = {
187 let disk_size = self.disk_size.load(Ordering::Relaxed);
188 build_config_space(disk_size, self.seg_max, self.block_size, NUM_QUEUES)
189 };
190 copy_config(data, 0, config_space.as_slice(), offset);
191 }
192
reset(&mut self)193 fn reset(&mut self) {
194 panic!("Unsupported call to reset");
195 }
196
start_queue( &mut self, idx: usize, mut queue: virtio::Queue, mem: GuestMemory, doorbell: Arc<Mutex<Doorbell>>, kick_evt: Event, ) -> anyhow::Result<()>197 fn start_queue(
198 &mut self,
199 idx: usize,
200 mut queue: virtio::Queue,
201 mem: GuestMemory,
202 doorbell: Arc<Mutex<Doorbell>>,
203 kick_evt: Event,
204 ) -> anyhow::Result<()> {
205 if let Some(handle) = self.workers.get_mut(idx).and_then(Option::take) {
206 warn!("Starting new queue handler without stopping old handler");
207 handle.abort();
208 }
209
210 // Enable any virtqueue features that were negotiated (like VIRTIO_RING_F_EVENT_IDX).
211 queue.ack_features(self.acked_features);
212
213 let kick_evt = EventAsync::new(kick_evt.0, &self.ex)
214 .context("failed to create EventAsync for kick_evt")?;
215 let (handle, registration) = AbortHandle::new_pair();
216
217 let disk_state = Rc::clone(&self.disk_state);
218 let timer = Rc::clone(&self.flush_timer);
219 let timer_armed = Rc::clone(&self.flush_timer_armed);
220 self.ex
221 .spawn_local(Abortable::new(
222 handle_queue(
223 self.ex.clone(),
224 mem,
225 disk_state,
226 Rc::new(RefCell::new(queue)),
227 kick_evt,
228 doorbell,
229 timer,
230 timer_armed,
231 ),
232 registration,
233 ))
234 .detach();
235
236 self.workers[idx] = Some(handle);
237 Ok(())
238 }
239
stop_queue(&mut self, idx: usize)240 fn stop_queue(&mut self, idx: usize) {
241 if let Some(handle) = self.workers.get_mut(idx).and_then(Option::take) {
242 handle.abort();
243 }
244 }
245 }
246
247 #[derive(FromArgs)]
248 #[argh(description = "")]
249 struct Options {
250 #[argh(
251 option,
252 description = "path and options of the disk file.",
253 arg_name = "PATH<:read-only>"
254 )]
255 file: String,
256 #[argh(option, description = "path to a vhost-user socket", arg_name = "PATH")]
257 socket: Option<String>,
258 #[argh(
259 option,
260 description = "VFIO-PCI device name (e.g. '0000:00:07.0')",
261 arg_name = "STRING"
262 )]
263 vfio: Option<String>,
264 }
265
266 /// Starts a vhost-user block device.
267 /// Returns an error if the given `args` is invalid or the device fails to run.
run_block_device(program_name: &str, args: &[&str]) -> anyhow::Result<()>268 pub fn run_block_device(program_name: &str, args: &[&str]) -> anyhow::Result<()> {
269 let opts = match Options::from_args(&[program_name], args) {
270 Ok(opts) => opts,
271 Err(e) => {
272 if e.status.is_err() {
273 bail!(e.output);
274 } else {
275 println!("{}", e.output);
276 }
277 return Ok(());
278 }
279 };
280
281 if !(opts.socket.is_some() ^ opts.vfio.is_some()) {
282 bail!("Exactly one of `--socket` or `--vfio` is required");
283 }
284
285 let ex = Executor::new().context("failed to create executor")?;
286
287 let mut fileopts = opts.file.split(":").collect::<Vec<_>>();
288 let filename = fileopts.remove(0);
289
290 let block = BlockBackend::new(&ex, filename, fileopts)?;
291 let handler = DeviceRequestHandler::new(block);
292 match (opts.socket, opts.vfio) {
293 (Some(socket), None) => {
294 // run_until() returns an Result<Result<..>> which the ? operator lets us flatten.
295 ex.run_until(handler.run(socket, &ex))?
296 }
297 (None, Some(device_name)) => {
298 let device = VvuPciDevice::new(device_name.as_str(), BlockBackend::MAX_QUEUE_NUM)?;
299 ex.run_until(handler.run_vvu(device, &ex))?
300 }
301 _ => unreachable!("Must be checked above"),
302 }
303 }
304