CAN, GPIO, RNG vhost-devices for virtio-loopback [v6]
[AGL/meta-agl-devel.git] / meta-egvirt / recipes-extended / vhost-device-can / vhost-device-can-0.1.0 / src / backend.rs
1 // VIRTIO CAN Emulation via vhost-user
2 //
3 // Copyright 2023 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved.
4 //          Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
5 //
6 // SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
7
8 use log::{error, info, warn};
9 use std::process::exit;
10 use std::sync::{Arc, RwLock};
11 use std::thread::{spawn, JoinHandle};
12
13 use clap::Parser;
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};
18
19 use crate::can::{CanController};
20 use crate::vhu_can::VhostUserCanBackend;
21
22 pub(crate) type Result<T> = std::result::Result<T, Error>;
23
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")]
30     FailedJoiningThreads,
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),
37 }
38
39 #[derive(Parser, Debug)]
40 #[clap(author, version, about, long_about = None)]
41 struct CanArgs {
42     /// Location of vhost-user Unix domain socket. This is suffixed by 0,1,2..socket_count-1.
43     #[clap(short, long)]
44     socket_path: String,
45
46     /// A can device name to be used for reading (ex. vcan, can0, can1, ... etc.)
47     #[clap(short = 'i', long)]
48     can_in: String,
49
50     /// A can device name to be used for writing (ex. vcan, can0, can1, ... etc.)
51     #[clap(short = 'o', long)]
52     can_out: String,
53
54     /// Number of guests (sockets) to connect to.
55     #[clap(short = 'c', long, default_value_t = 1)]
56     socket_count: u32,
57 }
58
59 #[derive(PartialEq, Debug)]
60 struct CanConfiguration {
61     socket_path: String,
62     socket_count: u32,
63     can_in: String,
64     can_out: String,
65 }
66
67 impl TryFrom<CanArgs> for CanConfiguration {
68     type Error = Error;
69
70     fn try_from(args: CanArgs) -> Result<Self> {
71
72         if args.socket_count == 0 {
73             return Err(Error::SocketCountInvalid(0));
74         }
75
76                 let can_in = args.can_in.trim().to_string();
77                 let can_out = args.can_out.trim().to_string();
78
79         Ok(CanConfiguration {
80             socket_path: args.socket_path,
81                         socket_count: args.socket_count,
82             can_in,
83             can_out,
84         })
85     }
86 }
87
88 fn start_backend(args: CanArgs) -> Result<()> {
89
90         println!("start_backend function!\n");
91
92     let config = CanConfiguration::try_from(args).unwrap();
93     let mut handles = Vec::new();
94
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();
99
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
103             // guest exits.
104             //
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
108             // daemon.
109
110             let controller =
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)?,
116             ));
117                         let _read_hanlde = CanController::start_read_thread(shared_controller_2);
118
119             let mut daemon = VhostUserDaemon::new(
120                 String::from("vhost-device-can-backend"),
121                 vu_can_backend.clone(),
122                 GuestMemoryAtomic::new(GuestMemoryMmap::new()),
123             )
124             .map_err(Error::CouldNotCreateDaemon)?;
125
126                         /* Start the read thread -- need to handle it after termination */
127                         let vring_workers = daemon.get_epoll_handlers();
128                         vu_can_backend.read()
129                                                   .unwrap()
130                                                   .set_vring_worker(&vring_workers[0]);
131
132             let listener = Listener::new(socket.clone(), true).unwrap();
133             daemon.start(listener).unwrap();
134
135             match daemon.wait() {
136                 Ok(()) => {
137                     info!("Stopping cleanly.");
138                 }
139                 Err(vhost_user_backend::Error::HandleRequest(
140                     vhost_user::Error::PartialMessage | vhost_user::Error::Disconnected,
141                 )) => {
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.");
143                 }
144                 Err(e) => {
145                     warn!("Error running daemon: {:?}", e);
146                 }
147             }
148
149             // No matter the result, we need to shut down the worker thread.
150             vu_can_backend.read().unwrap().exit_event.write(1).unwrap();
151         });
152
153         handles.push(handle);
154     }
155
156     for handle in handles {
157         handle.join().map_err(|_| Error::FailedJoiningThreads)??;
158     }
159
160     Ok(())
161 }
162
163 pub(crate) fn can_init() {
164     env_logger::init();
165         println!("Can_init function!");
166     if let Err(e) = start_backend(CanArgs::parse()) {
167         error!("{e}");
168         exit(1);
169     }
170 }