diff --git a/vhost-device-template/src/vhu_foo.rs b/vhost-device-template/src/backend.rs similarity index 98% rename from vhost-device-template/src/vhu_foo.rs rename to vhost-device-template/src/backend.rs index 20879ed..16f8264 100644 --- a/vhost-device-template/src/vhu_foo.rs +++ b/vhost-device-template/src/backend.rs @@ -5,10 +5,7 @@ // // SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause -use std::{ - convert, - io::{self, Result as IoResult}, -}; +use std::io::Result as IoResult; use log::{info, warn}; use thiserror::Error as ThisError; @@ -34,7 +31,7 @@ type Result = std::result::Result; #[derive(Copy, Clone, Debug, Eq, PartialEq, ThisError)] /// Errors related to vhost-device-foo daemon. -pub(crate) enum Error { +pub enum Error { #[error("Failed to handle event, didn't match EPOLLIN")] HandleEventNotEpollIn, #[error("Failed to handle unknown event")] @@ -47,13 +44,13 @@ pub(crate) enum Error { EventFdFailed, } -impl convert::From for io::Error { +impl From for std::io::Error { fn from(e: Error) -> Self { - io::Error::other(e) + std::io::Error::other(e) } } -pub(crate) struct VhostUserFooBackend { +pub struct VhostUserFooBackend { info: FooInfo, event_idx: bool, pub exit_event: EventFd, @@ -469,7 +466,7 @@ mod tests { .handle_event(0, EventSet::OUT, &[vring.clone()], 0) .unwrap_err() .kind(), - io::ErrorKind::Other + std::io::ErrorKind::Other ); assert_eq!( @@ -477,7 +474,7 @@ mod tests { .handle_event(1, EventSet::IN, &[vring.clone()], 0) .unwrap_err() .kind(), - io::ErrorKind::Other + std::io::ErrorKind::Other ); // Hit the loop part diff --git a/vhost-device-template/src/lib.rs b/vhost-device-template/src/lib.rs new file mode 100644 index 0000000..ba1191f --- /dev/null +++ b/vhost-device-template/src/lib.rs @@ -0,0 +1,104 @@ +// VIRTIO FOO Emulation via vhost-user +// +// Copyright 2023 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +pub mod backend; + +use std::{ + path::PathBuf, + sync::{Arc, RwLock}, + thread::{spawn, JoinHandle}, +}; + +use backend::VhostUserFooBackend; +use log::error; +use thiserror::Error as ThisError; +use vhost_user_backend::VhostUserDaemon; +use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap}; + +pub type Result = std::result::Result; + +#[derive(Debug, ThisError)] +/// Errors related to low level foo helpers +pub enum Error { + #[error("Could not create backend: {0}")] + CouldNotCreateBackend(backend::Error), + #[error("Could not create daemon: {0}")] + CouldNotCreateDaemon(vhost_user_backend::Error), + #[error("Fatal error: {0}")] + ServeFailed(vhost_user_backend::Error), +} + +#[derive(PartialEq, Debug)] +pub struct FooConfiguration { + pub socket_path: PathBuf, +} + +#[derive(Copy, Clone)] +pub struct FooInfo { + counter: u32, +} + +impl FooInfo { + pub fn new() -> Self { + Self { counter: 0 } + } + + pub fn counter(&mut self) -> u32 { + self.counter += 1; + self.counter + } +} + +impl Default for FooInfo { + fn default() -> Self { + Self::new() + } +} + +pub fn start_backend(config: FooConfiguration) -> Result<()> { + let socket_path = config.socket_path.clone(); + let info = FooInfo::new(); + + let handle: JoinHandle> = spawn(move || loop { + // There isn't much value in complicating code here to return an error from the + // threads, and so the code uses unwrap() instead. The panic on a thread + // won't cause trouble to the main() function and should be safe for the + // daemon. + let backend = Arc::new(RwLock::new( + VhostUserFooBackend::new(info).map_err(Error::CouldNotCreateBackend)?, + )); + + let mut daemon = VhostUserDaemon::new( + String::from("vhost-device-template-backend"), + backend, + GuestMemoryAtomic::new(GuestMemoryMmap::new()), + ) + .map_err(Error::CouldNotCreateDaemon)?; + + daemon.serve(&socket_path).map_err(Error::ServeFailed)?; + }); + + handle.join().map_err(std::panic::resume_unwind).unwrap() +} + +#[cfg(test)] +mod tests { + use std::path::Path; + + use assert_matches::assert_matches; + + use super::*; + + #[test] + fn test_fail_listener() { + // This will fail the listeners and thread will panic. + let socket_path = Path::new("/proc/foo/path/not/present").to_path_buf(); + let cmd_args = FooConfiguration { socket_path }; + + assert_matches!(start_backend(cmd_args).unwrap_err(), Error::ServeFailed(_)); + } +} diff --git a/vhost-device-template/src/main.rs b/vhost-device-template/src/main.rs index 900a5e9..8afbaea 100644 --- a/vhost-device-template/src/main.rs +++ b/vhost-device-template/src/main.rs @@ -5,35 +5,10 @@ // // SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause -mod vhu_foo; - -use std::{ - path::PathBuf, - process::exit, - sync::{Arc, RwLock}, - thread::{spawn, JoinHandle}, -}; +use std::{path::PathBuf, process::exit}; use clap::Parser; -use log::error; -use thiserror::Error as ThisError; -use vhost_user_backend::VhostUserDaemon; -use vhu_foo::VhostUserFooBackend; -use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap}; - -type Result = std::result::Result; - -#[derive(Debug, ThisError)] -/// Errors related to low level foo helpers -pub(crate) enum Error { - #[error("Could not create backend: {0}")] - CouldNotCreateBackend(vhu_foo::Error), - #[error("Could not create daemon: {0}")] - CouldNotCreateDaemon(vhost_user_backend::Error), - #[error("Fatal error: {0}")] - ServeFailed(vhost_user_backend::Error), -} - +use vhost_device_template::{start_backend, Error, FooConfiguration, Result}; #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] struct FooArgs { @@ -42,11 +17,6 @@ struct FooArgs { socket_path: PathBuf, } -#[derive(PartialEq, Debug)] -struct FooConfiguration { - socket_path: PathBuf, -} - impl TryFrom for FooConfiguration { type Error = Error; @@ -61,55 +31,12 @@ impl TryFrom for FooConfiguration { } } -#[derive(Copy, Clone)] -pub(crate) struct FooInfo { - counter: u32, -} - -impl FooInfo { - pub fn new() -> Self { - Self { counter: 0 } - } - - pub fn counter(&mut self) -> u32 { - self.counter += 1; - self.counter - } -} - -fn start_backend(args: FooArgs) -> Result<()> { - let config = FooConfiguration::try_from(args).unwrap(); - - let socket_path = config.socket_path.clone(); - let info = FooInfo::new(); - - let handle: JoinHandle> = spawn(move || loop { - // There isn't much value in complicating code here to return an error from the - // threads, and so the code uses unwrap() instead. The panic on a thread - // won't cause trouble to the main() function and should be safe for the - // daemon. - let backend = Arc::new(RwLock::new( - VhostUserFooBackend::new(info).map_err(Error::CouldNotCreateBackend)?, - )); - - let mut daemon = VhostUserDaemon::new( - String::from("vhost-device-template-backend"), - backend, - GuestMemoryAtomic::new(GuestMemoryMmap::new()), - ) - .map_err(Error::CouldNotCreateDaemon)?; - - daemon.serve(&socket_path).map_err(Error::ServeFailed)?; - }); - - handle.join().map_err(std::panic::resume_unwind).unwrap() -} - fn main() { env_logger::init(); - if let Err(e) = start_backend(FooArgs::parse()) { - error!("{e}"); + let config = FooConfiguration::try_from(FooArgs::parse()).unwrap(); + if let Err(e) = start_backend(config) { + log::error!("{e}"); exit(1); } } @@ -118,8 +45,6 @@ fn main() { mod tests { use std::path::Path; - use assert_matches::assert_matches; - use super::*; impl FooArgs { @@ -143,13 +68,4 @@ mod tests { assert_eq!(config, expected_config); } - - #[test] - fn test_fail_listener() { - // This will fail the listeners and thread will panic. - let socket_name = Path::new("~/path/not/present/foo"); - let cmd_args = FooArgs::from_args(socket_name); - - assert_matches!(start_backend(cmd_args).unwrap_err(), Error::ServeFailed(_)); - } }