aboutsummaryrefslogtreecommitdiffstats
path: root/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/src/backend.rs
diff options
context:
space:
mode:
Diffstat (limited to 'meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/src/backend.rs')
-rw-r--r--meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/src/backend.rs170
1 files changed, 170 insertions, 0 deletions
diff --git a/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/src/backend.rs b/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/src/backend.rs
new file mode 100644
index 00000000..177c76cc
--- /dev/null
+++ b/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/src/backend.rs
@@ -0,0 +1,170 @@
+// VIRTIO CAN Emulation via vhost-user
+//
+// Copyright 2023 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved.
+// Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
+//
+// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
+
+use log::{error, info, warn};
+use std::process::exit;
+use std::sync::{Arc, RwLock};
+use std::thread::{spawn, JoinHandle};
+
+use clap::Parser;
+use thiserror::Error as ThisError;
+use vhost::{vhost_user, vhost_user::Listener};
+use vhost_user_backend::VhostUserDaemon;
+use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap};
+
+use crate::can::{CanController};
+use crate::vhu_can::VhostUserCanBackend;
+
+pub(crate) type Result<T> = std::result::Result<T, Error>;
+
+#[derive(Debug, ThisError)]
+/// Errors related to low level CAN helpers
+pub(crate) enum Error {
+ #[error("Invalid socket count: {0}")]
+ SocketCountInvalid(usize),
+ #[error("Failed to join threads")]
+ FailedJoiningThreads,
+ #[error("Could not create can controller: {0}")]
+ CouldNotCreateCanController(crate::can::Error),
+ #[error("Could not create can backend: {0}")]
+ CouldNotCreateBackend(crate::vhu_can::Error),
+ #[error("Could not create daemon: {0}")]
+ CouldNotCreateDaemon(vhost_user_backend::Error),
+}
+
+#[derive(Parser, Debug)]
+#[clap(author, version, about, long_about = None)]
+struct CanArgs {
+ /// Location of vhost-user Unix domain socket. This is suffixed by 0,1,2..socket_count-1.
+ #[clap(short, long)]
+ socket_path: String,
+
+ /// A can device name to be used for reading (ex. vcan, can0, can1, ... etc.)
+ #[clap(short = 'i', long)]
+ can_in: String,
+
+ /// A can device name to be used for writing (ex. vcan, can0, can1, ... etc.)
+ #[clap(short = 'o', long)]
+ can_out: String,
+
+ /// Number of guests (sockets) to connect to.
+ #[clap(short = 'c', long, default_value_t = 1)]
+ socket_count: u32,
+}
+
+#[derive(PartialEq, Debug)]
+struct CanConfiguration {
+ socket_path: String,
+ socket_count: u32,
+ can_in: String,
+ can_out: String,
+}
+
+impl TryFrom<CanArgs> for CanConfiguration {
+ type Error = Error;
+
+ fn try_from(args: CanArgs) -> Result<Self> {
+
+ if args.socket_count == 0 {
+ return Err(Error::SocketCountInvalid(0));
+ }
+
+ let can_in = args.can_in.trim().to_string();
+ let can_out = args.can_out.trim().to_string();
+
+ Ok(CanConfiguration {
+ socket_path: args.socket_path,
+ socket_count: args.socket_count,
+ can_in,
+ can_out,
+ })
+ }
+}
+
+fn start_backend(args: CanArgs) -> Result<()> {
+
+ println!("start_backend function!\n");
+
+ let config = CanConfiguration::try_from(args).unwrap();
+ let mut handles = Vec::new();
+
+ for _ in 0..config.socket_count {
+ let socket = config.socket_path.to_owned();
+ let can_in = config.can_in.to_owned();
+ let can_out = config.can_out.to_owned();
+
+ let handle: JoinHandle<Result<()>> = spawn(move || loop {
+ // A separate thread is spawned for each socket and can connect to a separate guest.
+ // These are run in an infinite loop to not require the daemon to be restarted once a
+ // guest exits.
+ //
+ // 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 other threads/guests or the main() function and should be safe for the
+ // daemon.
+
+ let controller =
+ CanController::new(can_in.clone(), can_out.clone()).map_err(Error::CouldNotCreateCanController)?;
+ let shared_controller_1 = Arc::new(RwLock::new(controller));
+ let shared_controller_2 = shared_controller_1.clone();
+ let vu_can_backend = Arc::new(RwLock::new(
+ VhostUserCanBackend::new(shared_controller_1).map_err(Error::CouldNotCreateBackend)?,
+ ));
+ let _read_hanlde = CanController::start_read_thread(shared_controller_2);
+
+ let mut daemon = VhostUserDaemon::new(
+ String::from("vhost-device-can-backend"),
+ vu_can_backend.clone(),
+ GuestMemoryAtomic::new(GuestMemoryMmap::new()),
+ )
+ .map_err(Error::CouldNotCreateDaemon)?;
+
+ /* Start the read thread -- need to handle it after termination */
+ let vring_workers = daemon.get_epoll_handlers();
+ vu_can_backend.read()
+ .unwrap()
+ .set_vring_worker(&vring_workers[0]);
+
+ let listener = Listener::new(socket.clone(), true).unwrap();
+ daemon.start(listener).unwrap();
+
+ match daemon.wait() {
+ Ok(()) => {
+ info!("Stopping cleanly.");
+ }
+ Err(vhost_user_backend::Error::HandleRequest(
+ vhost_user::Error::PartialMessage | vhost_user::Error::Disconnected,
+ )) => {
+ info!("vhost-user connection closed with partial message. If the VM is shutting down, this is expected behavior; otherwise, it might be a bug.");
+ }
+ Err(e) => {
+ warn!("Error running daemon: {:?}", e);
+ }
+ }
+
+ // No matter the result, we need to shut down the worker thread.
+ vu_can_backend.read().unwrap().exit_event.write(1).unwrap();
+ });
+
+ handles.push(handle);
+ }
+
+ for handle in handles {
+ handle.join().map_err(|_| Error::FailedJoiningThreads)??;
+ }
+
+ Ok(())
+}
+
+pub(crate) fn can_init() {
+ env_logger::init();
+ println!("Can_init function!");
+ if let Err(e) = start_backend(CanArgs::parse()) {
+ error!("{e}");
+ exit(1);
+ }
+}