1 // Copyright 2022 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 use anyhow::bail;
6 use anyhow::Context;
7 use argh::FromArgs;
8 use base::info;
9 use cros_async::Executor;
10 use hypervisor::ProtectionType;
11
12 use crate::virtio::base_features;
13 use crate::virtio::block::block::DiskOption;
14 use crate::virtio::vhost::user::device::listener::sys::VhostUserListener;
15 use crate::virtio::vhost::user::device::listener::VhostUserListenerTrait;
16 use crate::virtio::vhost::user::VhostUserDevice;
17 use crate::virtio::BlockAsync;
18
19 #[derive(FromArgs)]
20 #[argh(subcommand, name = "block")]
21 /// Block device
22 pub struct Options {
23 #[argh(option, arg_name = "PATH<:read-only>")]
24 /// path and options of the disk file.
25 file: String,
26 #[argh(option, arg_name = "PATH")]
27 /// path to a vhost-user socket
28 socket: Option<String>,
29 #[argh(option, arg_name = "STRING")]
30 /// VFIO-PCI device name (e.g. '0000:00:07.0')
31 vfio: Option<String>,
32 }
33
34 /// Starts a vhost-user block device.
35 /// Returns an error if the given `args` is invalid or the device fails to run.
start_device(opts: Options) -> anyhow::Result<()>36 pub fn start_device(opts: Options) -> anyhow::Result<()> {
37 if !(opts.socket.is_some() ^ opts.vfio.is_some()) {
38 bail!("Exactly one of `--socket` or `--vfio` is required");
39 }
40
41 let ex = Executor::new().context("failed to create executor")?;
42
43 let mut fileopts = opts.file.split(":").collect::<Vec<_>>();
44 let filename = fileopts.remove(0);
45
46 let disk = DiskOption {
47 path: filename.into(),
48 read_only: fileopts.contains(&"read-only"),
49 root: false,
50 sparse: false,
51 direct: false,
52 block_size: 512,
53 id: None,
54 multiple_workers: false,
55 async_executor: None,
56 };
57
58 let block = Box::new(BlockAsync::new(
59 base_features(ProtectionType::Unprotected),
60 disk.open()?,
61 disk.read_only,
62 disk.sparse,
63 disk.block_size,
64 false,
65 None,
66 None,
67 None,
68 None,
69 None,
70 )?);
71
72 let listener = VhostUserListener::new_from_socket_or_vfio(
73 &opts.socket,
74 &opts.vfio,
75 block.max_queue_num(),
76 None,
77 )?;
78 info!("vhost-user disk device ready, starting run loop...");
79
80 listener.run_device(ex, block)
81 }
82