1 // VIRTIO CAN Emulation via vhost-user
3 // Copyright 2023 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved.
4 // Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
6 // SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
8 use log::{error, info, warn};
9 use std::process::exit;
10 use std::sync::{Arc, RwLock};
11 use std::thread::{spawn, JoinHandle};
14 use thiserror::Error as ThisError;
15 use vhost::{vhost_user, vhost_user::Listener};
16 use vhost_user_backend::VhostUserDaemon;
17 use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap};
19 use crate::can::{CanController};
20 use crate::vhu_can::VhostUserCanBackend;
22 pub(crate) type Result<T> = std::result::Result<T, Error>;
24 #[derive(Debug, ThisError)]
25 /// Errors related to low level CAN helpers
26 pub(crate) enum Error {
27 #[error("Invalid socket count: {0}")]
28 SocketCountInvalid(usize),
29 #[error("Failed to join threads")]
31 #[error("Could not create can controller: {0}")]
32 CouldNotCreateCanController(crate::can::Error),
33 #[error("Could not create can backend: {0}")]
34 CouldNotCreateBackend(crate::vhu_can::Error),
35 #[error("Could not create daemon: {0}")]
36 CouldNotCreateDaemon(vhost_user_backend::Error),
39 #[derive(Parser, Debug)]
40 #[clap(author, version, about, long_about = None)]
42 /// Location of vhost-user Unix domain socket. This is suffixed by 0,1,2..socket_count-1.
46 /// A can device name to be used for reading (ex. vcan, can0, can1, ... etc.)
47 #[clap(short = 'i', long)]
50 /// A can device name to be used for writing (ex. vcan, can0, can1, ... etc.)
51 #[clap(short = 'o', long)]
54 /// Number of guests (sockets) to connect to.
55 #[clap(short = 'c', long, default_value_t = 1)]
59 #[derive(PartialEq, Debug)]
60 struct CanConfiguration {
67 impl TryFrom<CanArgs> for CanConfiguration {
70 fn try_from(args: CanArgs) -> Result<Self> {
72 if args.socket_count == 0 {
73 return Err(Error::SocketCountInvalid(0));
76 let can_in = args.can_in.trim().to_string();
77 let can_out = args.can_out.trim().to_string();
80 socket_path: args.socket_path,
81 socket_count: args.socket_count,
88 fn start_backend(args: CanArgs) -> Result<()> {
90 println!("start_backend function!\n");
92 let config = CanConfiguration::try_from(args).unwrap();
93 let mut handles = Vec::new();
95 for _ in 0..config.socket_count {
96 let socket = config.socket_path.to_owned();
97 let can_in = config.can_in.to_owned();
98 let can_out = config.can_out.to_owned();
100 let handle: JoinHandle<Result<()>> = spawn(move || loop {
101 // A separate thread is spawned for each socket and can connect to a separate guest.
102 // These are run in an infinite loop to not require the daemon to be restarted once a
105 // There isn't much value in complicating code here to return an error from the
106 // threads, and so the code uses unwrap() instead. The panic on a thread won't cause
107 // trouble to other threads/guests or the main() function and should be safe for the
111 CanController::new(can_in.clone(), can_out.clone()).map_err(Error::CouldNotCreateCanController)?;
112 let shared_controller_1 = Arc::new(RwLock::new(controller));
113 let shared_controller_2 = shared_controller_1.clone();
114 let vu_can_backend = Arc::new(RwLock::new(
115 VhostUserCanBackend::new(shared_controller_1).map_err(Error::CouldNotCreateBackend)?,
117 let _read_hanlde = CanController::start_read_thread(shared_controller_2);
119 let mut daemon = VhostUserDaemon::new(
120 String::from("vhost-device-can-backend"),
121 vu_can_backend.clone(),
122 GuestMemoryAtomic::new(GuestMemoryMmap::new()),
124 .map_err(Error::CouldNotCreateDaemon)?;
126 /* Start the read thread -- need to handle it after termination */
127 let vring_workers = daemon.get_epoll_handlers();
128 vu_can_backend.read()
130 .set_vring_worker(&vring_workers[0]);
132 let listener = Listener::new(socket.clone(), true).unwrap();
133 daemon.start(listener).unwrap();
135 match daemon.wait() {
137 info!("Stopping cleanly.");
139 Err(vhost_user_backend::Error::HandleRequest(
140 vhost_user::Error::PartialMessage | vhost_user::Error::Disconnected,
142 info!("vhost-user connection closed with partial message. If the VM is shutting down, this is expected behavior; otherwise, it might be a bug.");
145 warn!("Error running daemon: {:?}", e);
149 // No matter the result, we need to shut down the worker thread.
150 vu_can_backend.read().unwrap().exit_event.write(1).unwrap();
153 handles.push(handle);
156 for handle in handles {
157 handle.join().map_err(|_| Error::FailedJoiningThreads)??;
163 pub(crate) fn can_init() {
165 println!("Can_init function!");
166 if let Err(e) = start_backend(CanArgs::parse()) {