aboutsummaryrefslogtreecommitdiffstats
path: root/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/src/backend.rs
blob: 177c76cc8bbbbdff58e9b824d538d07e827d4378 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
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);
    }
}