From 1416ecf64420a48ae832a2862f923a18e615bb32 Mon Sep 17 00:00:00 2001 From: Matias Ezequiel Vara Larsen Date: Tue, 3 Mar 2026 15:48:16 +0100 Subject: [PATCH 1/4] Fix test_can_controller_operation test Fix test by using CAN_FRMF_TYPE_FD flag for 64 bytes packets and use VIRTIO_CAN_TX as type. Signed-off-by: Matias Ezequiel Vara Larsen --- vhost-device-can/src/can.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/vhost-device-can/src/can.rs b/vhost-device-can/src/can.rs index 47a1fbc76..3b4f8b33e 100644 --- a/vhost-device-can/src/can.rs +++ b/vhost-device-can/src/can.rs @@ -293,6 +293,7 @@ mod tests { use super::*; use crate::vhu_can::VhostUserCanBackend; + use crate::virtio_can::VIRTIO_CAN_TX; #[test] fn test_can_controller_creation() { @@ -340,11 +341,11 @@ mod tests { let mut controller = CanController::new(can_name.clone()).unwrap(); let frame = VirtioCanFrame { - msg_type: VIRTIO_CAN_RX.into(), + msg_type: VIRTIO_CAN_TX.into(), can_id: 123.into(), length: 64.into(), reserved: 0.into(), - flags: 0.into(), + flags: CAN_FRMF_TYPE_FD.into(), sdu: [0; 64], }; From e17c119eec544a6672beb4fa33ecdcdec56516c8 Mon Sep 17 00:00:00 2001 From: Matias Ezequiel Vara Larsen Date: Thu, 23 Apr 2026 13:00:22 +0200 Subject: [PATCH 2/4] Fix test_can_controller_start_read_thread() Make the test finish when interface exists. Signed-off-by: Matias Ezequiel Vara Larsen --- vhost-device-can/src/can.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vhost-device-can/src/can.rs b/vhost-device-can/src/can.rs index 3b4f8b33e..84800fe14 100644 --- a/vhost-device-can/src/can.rs +++ b/vhost-device-can/src/can.rs @@ -367,6 +367,8 @@ mod tests { // Test start_read_thread let thread_handle = CanController::start_read_thread(arc_controller.clone()); + + arc_controller.write().unwrap().exit_read_thread(); assert!(thread_handle.join().is_ok()); } From bd317e3c8b5c6f9e69454ae944db855cb8ad56f6 Mon Sep 17 00:00:00 2001 From: Matias Ezequiel Vara Larsen Date: Thu, 23 Apr 2026 13:57:19 +0200 Subject: [PATCH 3/4] Add mock interface for tests Add a mock interface for tests and an env variable for running the tests with a real interface. To run with a real CAN interface named `vcan0`, run: TEST_CAN_INTERFACE=vcan0 cargo test --package vhost-device-can Also, remove the name of the interfaces from tests since we use either the mock interface or an existing interface. For those tests that do not require an interface because they test a failing path, use `nonexistent_can` interface. Signed-off-by: Matias Ezequiel Vara Larsen --- vhost-device-can/src/can.rs | 69 +++++++++++++++++++++++++-------- vhost-device-can/src/vhu_can.rs | 59 ++++++++++------------------ 2 files changed, 72 insertions(+), 56 deletions(-) diff --git a/vhost-device-can/src/can.rs b/vhost-device-can/src/can.rs index 84800fe14..c4233ec47 100644 --- a/vhost-device-can/src/can.rs +++ b/vhost-device-can/src/can.rs @@ -49,11 +49,28 @@ pub(crate) enum Error { NoOutputInterface, } +#[derive(Debug)] +pub(crate) enum CanSocketKind { + Fd(CanFdSocket), + #[cfg(test)] + Mock, +} + +impl CanSocketKind { + pub fn write_frame(&self, frame: &CanAnyFrame) -> std::io::Result<()> { + match self { + CanSocketKind::Fd(socket) => socket.write_frame(frame), + #[cfg(test)] + CanSocketKind::Mock => Ok(()), + } + } +} + #[derive(Debug)] pub(crate) struct CanController { pub config: VirtioCanConfig, pub can_name: String, - pub can_socket: Option, + pub can_socket: Option, pub rx_event_fd: EventFd, rx_fifo: Queue, pub status: bool, @@ -80,6 +97,30 @@ impl CanController { }) } + #[cfg(test)] + pub(crate) fn new_mock() -> Result { + let rx_fifo = Queue::new(); + let rx_efd = EventFd::new(EFD_NONBLOCK).map_err(|_| Error::EventFdFailed)?; + + Ok(CanController { + config: VirtioCanConfig { status: 0x0.into() }, + can_name: "vcan0".to_string(), + can_socket: Some(CanSocketKind::Mock), + rx_event_fd: rx_efd, + rx_fifo, + status: true, + ctrl_state: CAN_CS_STOPPED, + }) + } + + #[cfg(test)] + pub(crate) fn new_test() -> Result { + if let Ok(iface) = std::env::var("TEST_CAN_INTERFACE") { + return CanController::new(iface); + } + CanController::new_mock() + } + pub fn print_can_frame(canframe: VirtioCanFrame) { trace!("canframe.msg_type 0x{:x}", canframe.msg_type.to_native()); trace!("canframe.can_id 0x{:x}", canframe.can_id.to_native()); @@ -129,7 +170,7 @@ impl CanController { pub fn open_can_socket(&mut self) -> Result<()> { self.can_socket = match CanFdSocket::open(&self.can_name) { - Ok(socket) => Some(socket), + Ok(socket) => Some(CanSocketKind::Fd(socket)), Err(_) => { warn!("Error opening CAN socket"); return Err(Error::SocketOpen); @@ -297,16 +338,13 @@ mod tests { #[test] fn test_can_controller_creation() { - let can_name = "can".to_string(); - - let controller = CanController::new(can_name.clone()).unwrap(); - assert_eq!(controller.can_name, can_name); + let controller = CanController::new_test().unwrap(); + assert!(!controller.can_name.is_empty()); } #[test] fn test_can_controller_push_and_pop() { - let can_name = "can".to_string(); - let mut controller = CanController::new(can_name.clone()).unwrap(); + let mut controller = CanController::new_test().unwrap(); let frame = VirtioCanFrame { msg_type: VIRTIO_CAN_RX.into(), @@ -327,8 +365,7 @@ mod tests { #[test] fn test_can_controller_config() { - let can_name = "can".to_string(); - let mut controller = CanController::new(can_name.clone()).unwrap(); + let mut controller = CanController::new_test().unwrap(); // Test config let config = controller.config(); @@ -337,8 +374,7 @@ mod tests { #[test] fn test_can_controller_operation() { - let can_name = "can".to_string(); - let mut controller = CanController::new(can_name.clone()).unwrap(); + let mut controller = CanController::new_test().unwrap(); let frame = VirtioCanFrame { msg_type: VIRTIO_CAN_TX.into(), @@ -355,14 +391,13 @@ mod tests { let operation_result = controller.can_out(frame); assert!(operation_result.is_ok()); } - Err(_) => warn!("There is no CAN interface with {can_name} name"), + Err(_) => warn!("There is no CAN interface"), } } #[test] fn test_can_controller_start_read_thread() { - let can_name = "can".to_string(); - let controller = CanController::new(can_name.clone()).unwrap(); + let controller = CanController::new_test().unwrap(); let arc_controller = Arc::new(RwLock::new(controller)); // Test start_read_thread @@ -375,7 +410,7 @@ mod tests { #[test] fn test_can_open_socket_fail() { let controller = - CanController::new("can0".to_string()).expect("Could not build controller"); + CanController::new("nonexistent_can".to_string()).expect("Could not build controller"); let controller = Arc::new(RwLock::new(controller)); VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); @@ -388,7 +423,7 @@ mod tests { #[test] fn test_can_read_socket_fail() { let controller = - CanController::new("can0".to_string()).expect("Could not build controller"); + CanController::new("nonexistent_can".to_string()).expect("Could not build controller"); let controller = Arc::new(RwLock::new(controller)); VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); diff --git a/vhost-device-can/src/vhu_can.rs b/vhost-device-can/src/vhu_can.rs index 2505700fe..e257e52eb 100644 --- a/vhost-device-can/src/vhu_can.rs +++ b/vhost-device-can/src/vhu_can.rs @@ -777,8 +777,7 @@ mod tests { #[test] fn test_virtio_can_empty_requests() { - let controller = - CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = CanController::new_test().expect("Could not build controller"); let controller = Arc::new(RwLock::new(controller)); let mut vu_can_backend = VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); @@ -801,8 +800,7 @@ mod tests { #[test] fn test_virtio_can_empty_handle_request() { - let controller = - CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = CanController::new_test().expect("Could not build controller"); let controller = Arc::new(RwLock::new(controller)); let mut vu_can_backend = VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); @@ -873,8 +871,7 @@ mod tests { #[test] fn test_virtio_can_ctrl_request() { // Initialize can device and vhost-device-can backend - let controller = - CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = CanController::new_test().expect("Could not build controller"); let controller = Arc::new(RwLock::new(controller)); let mut vu_can_backend = VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); @@ -1100,8 +1097,7 @@ mod tests { #[test] fn test_virtio_can_check_tx_unknown_frame() { - let controller = - CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = CanController::new_test().expect("Could not build controller"); let controller = Arc::new(RwLock::new(controller)); let vu_can_backend = VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); @@ -1123,8 +1119,7 @@ mod tests { #[test] fn test_virtio_can_check_tx_can_frame() { - let controller = - CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = CanController::new_test().expect("Could not build controller"); let controller = Arc::new(RwLock::new(controller)); let mut vu_can_backend = VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); @@ -1171,8 +1166,7 @@ mod tests { #[test] fn test_virtio_can_check_tx_canfd_frame() { - let controller = - CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = CanController::new_test().expect("Could not build controller"); let controller = Arc::new(RwLock::new(controller)); let mut vu_can_backend = VhostUserCanBackend::new(controller).expect("Could not build vhucan device"); @@ -1278,8 +1272,7 @@ mod tests { #[test] fn test_virtio_can_check_tx_rtr_frame() { - let controller = - CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = CanController::new_test().expect("Could not build controller"); let controller = Arc::new(RwLock::new(controller)); let mut vu_can_backend = VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); @@ -1340,8 +1333,7 @@ mod tests { #[test] fn test_virtio_can_check_tx_eff_frame() { - let controller = - CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = CanController::new_test().expect("Could not build controller"); let controller = Arc::new(RwLock::new(controller)); let mut vu_can_backend = VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); @@ -1401,8 +1393,7 @@ mod tests { #[test] fn test_virtio_can_tx_general_tests() { - let controller = - CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = CanController::new_test().expect("Could not build controller"); let controller = Arc::new(RwLock::new(controller)); let mut vu_can_backend = VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); @@ -1498,8 +1489,7 @@ mod tests { #[test] fn test_virtio_can_tx_device_stopped_test() { - let controller = - CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = CanController::new_test().expect("Could not build controller"); let controller = Arc::new(RwLock::new(controller)); let mut vu_can_backend = VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); @@ -1554,7 +1544,7 @@ mod tests { #[test] fn test_virtio_can_tx_device_started_test_send_fail() { let controller = - CanController::new("can0".to_string()).expect("Could not build controller"); + CanController::new("nonexistent_can".to_string()).expect("Could not build controller"); let controller = Arc::new(RwLock::new(controller)); let mut vu_can_backend = VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); @@ -1611,8 +1601,7 @@ mod tests { #[test] fn test_virtio_can_tx_device_started_check_frame_fail() { - let controller = - CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = CanController::new_test().expect("Could not build controller"); let controller = Arc::new(RwLock::new(controller)); let mut vu_can_backend = VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); @@ -1666,8 +1655,7 @@ mod tests { #[test] fn test_virtio_can_check_rx_err_frame() { - let controller = - CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = CanController::new_test().expect("Could not build controller"); let controller = Arc::new(RwLock::new(controller)); let vu_can_backend = VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); @@ -1709,8 +1697,7 @@ mod tests { #[test] fn test_virtio_can_check_rx_features_not_negotiated() { - let controller = - CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = CanController::new_test().expect("Could not build controller"); let controller = Arc::new(RwLock::new(controller)); let vu_can_backend = VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); @@ -1746,8 +1733,7 @@ mod tests { #[test] fn test_virtio_can_check_rx_eff_frame() { - let controller = - CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = CanController::new_test().expect("Could not build controller"); let controller = Arc::new(RwLock::new(controller)); let mut vu_can_backend = VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); @@ -1814,8 +1800,7 @@ mod tests { #[test] fn test_virtio_can_check_rx_rtr_frame() { - let controller = - CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = CanController::new_test().expect("Could not build controller"); let controller = Arc::new(RwLock::new(controller)); let mut vu_can_backend = VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); @@ -1878,8 +1863,7 @@ mod tests { #[test] fn test_virtio_can_check_rx_can_frame() { - let controller = - CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = CanController::new_test().expect("Could not build controller"); let controller = Arc::new(RwLock::new(controller)); let mut vu_can_backend = VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); @@ -1925,8 +1909,7 @@ mod tests { #[test] fn test_virtio_can_check_rx_canfd_frame() { - let controller = - CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = CanController::new_test().expect("Could not build controller"); let controller = Arc::new(RwLock::new(controller)); let mut vu_can_backend = VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); @@ -2010,8 +1993,7 @@ mod tests { #[test] fn test_virtio_can_check_rx_canfd_vcan0() { - let controller = - CanController::new("vcan0".to_string()).expect("Could not build controller"); + let controller = CanController::new_test().expect("Could not build controller"); let controller = Arc::new(RwLock::new(controller)); let mut vu_can_backend = VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); @@ -2049,8 +2031,7 @@ mod tests { #[test] fn test_virtio_can_rx_request() { - let controller = - CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = CanController::new_test().expect("Could not build controller"); let controller = Arc::new(RwLock::new(controller)); let mut vu_can_backend = VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); From 8a2257e23db4893030daf126c16c87b7d3d5682d Mon Sep 17 00:00:00 2001 From: Matias Ezequiel Vara Larsen Date: Thu, 23 Apr 2026 15:21:53 +0200 Subject: [PATCH 4/4] remove checking interface's name in check_rx_frame The CAN-FD flag inference logic in check_rx_frame was gated on can_name == "vcan0". This was added as a workaround for virtual CAN interfaces (vcan), which do not distinguish between CAN and CAN-FD frames at the socket level, i.e., all frames arrive as CanAnyFrame::Normal regardless of their actual type. However, the interface name check is unnecessary. Classic CAN frames can never exceed 8 bytes, so the condition `res_len > 8` is already sufficient to infer a CAN-FD frame when the feature has been negotiated. This holds for all interface types: - vcan: frames arrive without the FD flag, but res_len > 8 detects them - Real CAN-FD hardware: the FD flag is already set by process_frame, so the additional |= is redundant but harmless - Real classic CAN hardware: frames never exceed 8 bytes, so the condition is never triggered Removing the name check also fixes the limitation of only supporting an interface named exactly "vcan0", which excluded vcan1, vcan2, etc. Signed-off-by: Matias Ezequiel Vara Larsen --- vhost-device-can/src/vhu_can.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/vhost-device-can/src/vhu_can.rs b/vhost-device-can/src/vhu_can.rs index e257e52eb..9bb4eafbd 100644 --- a/vhost-device-can/src/vhu_can.rs +++ b/vhost-device-can/src/vhu_can.rs @@ -272,16 +272,12 @@ impl VhostUserCanBackend { can_rx.flags = (can_rx.flags.to_native() | VIRTIO_CAN_FLAGS_RTR).into(); } - // Treat Vcan interface as CANFD if MTU is set to 64 bytes. - // - // Vcan can not be configured as CANFD interface, but it is - // possible to configure its MTU to 64 bytes. So if a messages - // bigger than 8 bytes is being received we consider it as - // CANFD message. - let can_name = self.controller.read().unwrap().can_name.clone(); - if self.check_features(VIRTIO_CAN_F_CAN_FD) && res_len > 8 && can_name == "vcan0" { + // Classic CAN frames cannot exceed 8 bytes. If CAN-FD has been + // negotiated and the frame length is > 8, treat it as a CAN-FD + // frame. This also covers virtual interfaces (vcan) which do not + // distinguish between CAN and CAN-FD at the socket level. + if self.check_features(VIRTIO_CAN_F_CAN_FD) && res_len > 8 { res_flags |= CAN_FRMF_TYPE_FD; - warn!("\n\n\nCANFD VCAN0\n\n"); } // Check if CAN/FD length is out-of-range (based on negotiated features)