Skip to content

Commit fd5bb13

Browse files
GaelanErik Schilling
authored and
Erik Schilling
committed
scsi: Add virtio daemon
This adds the virtio-specific parts that use the previously formed interfaces and scsi emulation in order to build a daemon that offers files from the host system as drives to the guest. The vast majority of this work was done by Gaelan Steele as part of a GSoC project [1][2]. [1] rust-vmm#4 [2] https://gist.github.com/Gaelan/febec4e4606e1320026a0924c3bf74d0 Co-developed-by: Erik Schilling <[email protected]> Signed-off-by: Erik Schilling <[email protected]> Signed-off-by: Gaelan Steele <[email protected]>
1 parent e3eaa27 commit fd5bb13

File tree

6 files changed

+725
-3
lines changed

6 files changed

+725
-3
lines changed

crates/scsi/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Upcoming Release
2+
3+
- First initial daemon implementation.

crates/scsi/src/backend.rs

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
2+
3+
use std::{
4+
fs::File,
5+
path::PathBuf,
6+
sync::{Arc, RwLock},
7+
};
8+
9+
use crate::scsi::emulation::{
10+
block_device::{BlockDevice, FileBackend, MediumRotationRate},
11+
target::EmulatedTarget,
12+
};
13+
use clap::Parser;
14+
use log::{error, info, warn};
15+
use thiserror::Error as ThisError;
16+
use vhost::vhost_user::{self, Listener};
17+
use vhost_user_backend::VhostUserDaemon;
18+
use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap};
19+
20+
use crate::vhu_scsi::VhostUserScsiBackend;
21+
22+
#[derive(Debug, ThisError)]
23+
pub enum Error {
24+
#[error("More than 256 LUNs aren't currently supported")]
25+
TooManyLUNs,
26+
#[error("Failed creating listener: {0}")]
27+
FailedCreatingListener(vhost_user::Error),
28+
}
29+
30+
pub type Result<T> = std::result::Result<T, Error>;
31+
32+
#[derive(Parser)]
33+
struct ScsiArgs {
34+
/// Make the images read-only.
35+
///
36+
/// Currently, we don't actually support writes, but sometimes we want to
37+
/// pretend the disk is writable to work around issues with some tools that
38+
/// use the Linux SCSI generic API.
39+
#[arg(long = "read-only", short = 'r')]
40+
read_only: bool,
41+
/// Tell the guest this disk is non-rotational.
42+
///
43+
/// Affects some heuristics in Linux around, for example, scheduling.
44+
#[arg(long = "solid-state")]
45+
solid_state: bool,
46+
/// Location of vhost-user socket.
47+
#[clap(short, long)]
48+
socket_path: PathBuf,
49+
/// Images against which the SCSI actions are emulated.
50+
images: Vec<PathBuf>,
51+
}
52+
53+
fn create_backend(args: &ScsiArgs) -> Result<VhostUserScsiBackend> {
54+
let mut backend = VhostUserScsiBackend::new();
55+
let mut target = EmulatedTarget::new();
56+
if args.images.len() > 256 {
57+
error!("");
58+
// This is fairly simple to add; it's just a matter of supporting the right LUN
59+
// encoding formats.
60+
return Err(Error::TooManyLUNs);
61+
}
62+
if !args.read_only {
63+
warn!("Currently, only read-only images are supported. Unless you know what you're doing, you want to pass -r");
64+
}
65+
for image in &args.images {
66+
let mut dev = BlockDevice::new(FileBackend::new(File::open(image).expect("Opening image")));
67+
dev.set_write_protected(args.read_only);
68+
dev.set_solid_state(if args.solid_state {
69+
MediumRotationRate::NonRotating
70+
} else {
71+
MediumRotationRate::Unreported
72+
});
73+
target.add_lun(Box::new(dev));
74+
}
75+
backend.add_target(Box::new(target));
76+
Ok(backend)
77+
}
78+
79+
fn start_backend(backend: VhostUserScsiBackend, args: ScsiArgs) -> Result<()> {
80+
let backend = Arc::new(RwLock::new(backend));
81+
let mut daemon = VhostUserDaemon::new(
82+
"vhost-user-scsi".into(),
83+
Arc::clone(&backend),
84+
GuestMemoryAtomic::new(GuestMemoryMmap::new()),
85+
)
86+
.expect("Creating daemon");
87+
88+
daemon
89+
.start(Listener::new(args.socket_path, true).map_err(Error::FailedCreatingListener)?)
90+
.expect("Starting daemon");
91+
92+
match daemon.wait() {
93+
Ok(()) => {
94+
info!("Stopping cleanly.");
95+
}
96+
Err(vhost_user_backend::Error::HandleRequest(vhost_user::Error::PartialMessage)) => {
97+
info!("vhost-user connection closed with partial message. If the VM is shutting down, this is expected behavior; otherwise, it might be a bug.");
98+
}
99+
Err(e) => {
100+
warn!("Error running daemon: {:?}", e);
101+
}
102+
}
103+
// No matter the result, we need to shut down the worker thread.
104+
// unwrap will only panic if we already panicked somewhere else
105+
backend
106+
.read()
107+
.unwrap()
108+
.exit_event
109+
.write(1)
110+
.expect("Shutting down worker thread");
111+
Ok(())
112+
}
113+
114+
pub fn scsi_init() -> Result<()> {
115+
env_logger::init();
116+
let args = ScsiArgs::parse();
117+
let backend = create_backend(&args)?;
118+
start_backend(backend, args)
119+
}

crates/scsi/src/lib.rs

Lines changed: 0 additions & 1 deletion
This file was deleted.

crates/scsi/src/main.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1-
fn main() {
2-
println!("Hello world");
1+
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
2+
3+
mod backend;
4+
mod scsi;
5+
mod vhu_scsi;
6+
mod virtio;
7+
8+
fn main() -> backend::Result<()> {
9+
backend::scsi_init()
310
}

0 commit comments

Comments
 (0)